Zhuang's Diary

言之有物,持之以恒

SeaweedFS 是开源的,简单的,高伸缩性的分布式文件系统。SeaweedFS 作为支持全 POSIX 文件系统语义替代,Seaweed-FS 选择仅实现 key-file 的映射,类似 “NoSQL”,也可以说是 “NoFS”。

SeaweedFS 仅花费 40 字节的硬盘来存储每个文件的元数据。

GlusterFS, Ceph相比较

System File Meta File Content Read POSIX REST API Optimized for small files
SeaweedFS lookup volume id, cacheable O(1) disk seek Yes Yes
SeaweedFS Filer Linearly Scalable, Customizable O(1) disk seek FUSE Yes Yes
GlusterFS hashing FUSE, NFS
Ceph hashing + rules FUSE Yes
MooseFS in memory FUSE No

体验seaweedfs

参考==>https://hub.docker.com/r/chrislusf/seaweedfs

  1. 拉取 docker-compose 文件,wget https://raw.githubusercontent.com/chrislusf/seaweedfs/master/docker/seaweedfs-compose.yml
  2. 启动,docker-compose -f seaweedfs-compose.yml -p seaweedfs up

参考 => https://github.com/chrislusf/seaweedfs/wiki/Volume-Server-API

1
2
3
4
5
6
➜  seaweedfs: curl http://localhost:9333/dir/assign
{"fid":"7,01248b7b86","url":"172.18.0.3:8080","publicUrl":"172.18.0.3:8080","count":1}%
➜ seaweedfs: curl -F file=@/home/will/documents/zaq12wsxcde3--de4064dc15870163a9aab589a1ccf7900dd68ef4 http://127.0.0.1:8080/7,01248b7b86
{"name":"zaq12wsxcde3--de4064dc15870163a9aab589a1ccf7900dd68ef4","size":489,"eTag":"468de108"}%
➜ seaweedfs: curl http://localhost:9333/dir/lookup\?volumeId\=7
{"volumeId":"7","locations":[{"url":"172.18.0.3:8080","publicUrl":"172.18.0.3:8080"}]}%
  1. curl http://localhost:9333/dir/assign 获取fid
  2. curl -F file=@/home/will/documents/zaq12wsxcde3--de4064dc15870163a9aab589a1ccf7900dd68ef4 http://127.0.0.1:8080/7,01248b7b86 上传文件
  3. curl http://localhost:9333/dir/lookup\?volumeId\=7 查询
  4. 在浏览器中打开 http://172.18.0.3:8080/7,01248b7b86,查看文档
  5. master 的url:http://127.0.0.1:9333/
  6. volume的url:http://172.18.0.3:8080/ui/index.html

Chainlink是一个去中心化的预言机,但是你同样可以在私有环境中使用Chainlink,让它成为一个为私有链或联盟链服务的中心化的预言机系统。只要你的私有链环境支持完整的EVM环境,就可以利用Chainlink的开源实现,为自己的系统搭建一个具有广泛适配能力的预言机系统。

准备工作

私有链/联盟链(需支持EVM,开放WebSocket端口)

由于目前Chainlink的合约系统都是在以太坊网络上的虚拟环境中运行的,所以需要您的私有链/联盟链支持EVM环境以及Solidity合约编程语言。

用于部署合约的账户,账户需持有足够数量的Native Token(原生代币)

一般来说,向区块链提交事务,不管是创建合约还是普通转账,都需要事务发起账户提供一定的手续费,所以需要一个持有原生代币的账户,用于部署合约和节点提交事务。如果您的私有链提交事务不需要手续费,那仅需要一个账户就可以。(具体请根据您的私有链的配置操作)

一台服务器用于部署Chainlink节点

Chainlink节点相当于区块链在真实世界中的代理,它可以接收链上的数据请求,并获取到链上所需要的数据,通过事务提交给链上。所以需要一台服务器来建立Chainlink节点服务,服务器配置不需要很高,但是需要和私有链环境保持良好的网络通讯。

之前的Chainlink节点版本默认使用Sqlite作为存储数据库,但是随着功能越来越完善,性能要求也越来越高,从0.8.0版本起,Chainlink节点要求必须使用postgres作为存储数据库,所以我们需要搭建一个postgres数据库。这个数据库可以和节点位于同一台服务器上,也可以位于不同的服务器上。您也可以同时搭建两个数据库作为备份。

(可选)准备truffle suite和npm或WebIDE Remix等开发部署工具

您可以选择Chainlink提供的truffle box来编写和部署测试合约

1
truffle unbox smartcontractkit/box

也可以在已有的项目中添加Chainlink开发库

1
2
truffle init
npm install @chainlink/contracts --save

同样的,您也可以使用可视化的Web IDE Remix来进行开发,使用起来更加方便,也无需配置本地开发环境。在使用Remix时,可以搭配MetaMask来进行转账和事务操作。

https://remix.ethereum.org

https://metamask.io

实施步骤

0.搭建 EVM 私链

LinkToken.sol

所有Chainlink的功能实现和数据流转都是围绕LINK token来实现的。这里的remix中默认是https url,如果访问vmware中的linux server,建议在remix中使用 http url

2.部署Chainlink节点

2.1 安装docker、postgres (建议postgres也在docker中安装)

2.2 创建环境变量配置文件

1
2
3
mkdir ~/.chainlink
cd ~/.chainlink
vim .env
1
2
3
4
5
6
7
8
9
10
11
12
ROOT=/chainlink
LOG_LEVEL=debug
ETH_CHAIN_ID=1337
MIN_OUTGOING_CONFIRMATIONS=0
MIN_INCOMING_CONFIRMATIONS=0
LINK_CONTRACT_ADDRESS=0x9E05B78ea853a4B093694645561c4BFc953A6f62
CHAINLINK_TLS_PORT=0
SECURE_COOKIES=false
ALLOW_ORIGINS=*
ETH_URL=ws://localhost:8546
DATABASE_URL=postgresql://postgres:123456@localhost:5432/chainlink?sslmode=disable
DATABASE_TIMEOUT=0

其中ETH_URL是私有链的RPC接口,必须是WebSocket接口,可以是ws也可以是wss

LINK_CONTRACT_ADDRESS 是刚刚部署的Link token 地址。

2.3 通过docker启动

1
2
3
4
5
cd ~/.chainlink
# 启动postgres
docker run --name postgres_chainlink -e POSTGRES_PASSWORD=123456 -v /home/will/documents/chainlink:/var/lib/postgresql/data -p 5432:5432 -d postgres:11.5-alpine
# 启动chainlink节点
docker run --net host -p 6688:6688 -v ~/.chainlink:/chainlink -it --env-file=.env smartcontract/chainlink:0.7.8 local n

其中--net host 是为了让通过docker启动的Chainlink节点可以访问到宿主记得网络,否则上面配置文件中的localhost都是不可访问的。也可以通过其他更加安全的docker网络配置完成同样功能。

0.7.8是Chainlink的release版本号,不写的话默认是latest。

2.4 记录节点管理密码

首次启动成功后,首先会要求输入一个密码,这个密码是Chainlink节点账户的私钥密码,可以用来控制Chainlink节点账户,必须牢记,否则节点账户所持有的资金无法取出。

然后会要求输入一对用户名和密码,这是Chainlink管理界面的的用户名和密码,在通过 http: //chainlink_ip:6688 访问管理端界面时会要求输入这对用户名和密码。

2.5 创建Job

https://docs.chain.link/docs/fulfilling-requests#section-add-jobs-to-the-node

创建不同数据类型的 Job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"initiators": [
{
"type": "runlog"
}
],
"tasks": [
{
"type": "httpget"
},
{
"type": "jsonparse"
},
{
"type": "ethbytes32"
},
{
"type": "ethtx"
}
]
}

2.6 向Chainlink节点(ACCOUNT_ADDRESS)转入一定数量的Native token用于提交事务

比如:如果是以太坊公网,需要转入ETH,以作为交易的gas费用。

节点地址可以在管理页面的configure页面下找到,账户资金仅用于提交transaction。

3.部署oracle contract

1
2
3
pragma solidity 0.4.24;

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/Oracle.sol";

调用合约中的setFulfillPermission方法,传递参数为(ACCOUNT_ADDRESS,true),ACCOUNT_ADDRESS是ChainLink Operator。

4.编写部署用户合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
pragma solidity 0.4.24;

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";


contract GetTemperature is ChainlinkClient {
LinkTokenInterface internal LinkToken;


//string constant url = "https://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new";
string constant url = "https://api.seniverse.com/v3/weather/now.json?key=S9g8Ize9pNyUZ_BOP&location=shanghai&language=zh-Hans&unit=c";
address constant oracleAddress = 0xa6126AD8B8307C6e1b668F486BEA155e814FA22d;
bytes32 constant JobId = "d91130d49daf46aaa591bcbce6d59b72";
address constant linkAddress = 0x9E05B78ea853a4B093694645561c4BFc953A6f62;

constructor() public {
setChainlinkToken(linkAddress);
setChainlinkOracle(oracleAddress);
LinkToken = LinkTokenInterface(linkAddress);
}

string public temperature;

function getData() public {

// 发起Chainlink请求
requestTemperature(JobId);

}


function requestTemperature(bytes32 _jobId) public returns (bytes32 requestId) {

Chainlink.Request memory req = buildChainlinkRequest(_jobId, this, this.fulfillTemperature.selector);

req.add("get", url);
req.add("path", "results.0.now.temperature");

requestId = sendChainlinkRequest(req, 1 * LINK);

return requestId;
}


function fulfillTemperature(bytes32 _requestId, bytes32 _temp)
public recordChainlinkFulfillment(_requestId)
{
temperature = bytes32ToString(_temp);
//data = _temp;
}


function bytes32ToString(bytes32 x) private pure returns (string) {
bytes memory bytesString = new bytes(32);
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}

}

其中 linkAddress、OracleAddress、JobId 需要根据上面的配置结果填写,或作为参数在调用时传入。

向部署好的用户合约地址转入Link token,用户在发起请求是支付给节点的费用。上述部署的Link Token为18位精度,建议使用 transfer 方法转移 Link token 2000,000000000000000000

发起请求获取数据,使用getData方法获取天气数据。

准备虚拟服务器

  1. 在vmware上安装centos7.7 server

  2. vmware工具栏,虚拟机->管理->克隆,制作k8s-master

  3. vmware工具栏,虚拟机->管理->克隆,制作k8s-worker01

  4. vmware工具栏,虚拟机->管理->克隆,制作k8s-worker02

  5. 于k8s-master内,/etc/sysconfig/network-scripts/ifcfg-ens33,将server编辑为静态ip,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #######################################
    ### depends on vm network settings ####
    #######################################
    BOOTPROTO=static
    IPADDR=192.168.180.10
    NETMASK=255.255.255.0
    GATEWAY=192.168.180.2
    DNS1=192.168.180.2
    ONBOOT=yes

    其中,192.168.180.2来自vmware工具栏,编辑->虚拟网络编辑器->NAT设置->子网IP

    重启server网卡如下:

    1
    2
    nmcli c reload ens33
    nmcli c up ens33

    从windows登录k8s-master,登录IP为192.168.180.10

  6. 于k8s-worker01内,/etc/sysconfig/network-scripts,将server编辑为静态ip,IPADDR=192.168.180.11

    重启server网卡如上

  7. 于k8s-worker01内,/etc/sysconfig/network-scripts,将server编辑为静态ip,IPADDR=192.168.180.12

    重启server网卡如上

  8. optional,检查vmware,编辑->虚拟网络编辑器 中的VMnet8的连通状态,如果有需要,在windows10->设置->更改适配器选项,重新启动VMnet8网卡

从winscp端登录:

最后,按照 kuboard -> 安装 -> 安装 kubernetes 手册完成kubernetes安装。

我相信每个人都会想,每次更新完代码,更新完配置文件后,就直接这么 ctrl+c 真的没问题吗,ctrl+c到底做了些什么事情呢?

本文我们讨论 ctrl+c 背后的信号以及如何优雅的重启服务,以及对 HTTP 服务进行热更新。

ctrl + c

在终端执行特定的组合键可以使系统发送特定的信号给此进程,完成一系列的动作

命令 信号 含义
ctrl + c SIGINT 强制进程结束
ctrl + z SIGTSTP 任务中断,进程挂起
ctrl + \ SIGQUIT 进程结束 和 dump core
ctrl + d EOF
常用于重启、重新加载进程 SIGHUP 若程序中没有捕捉该信号,当收到该信号时,进程就会退出
SIGPIPE 在进程往一个已经关闭的管道写数据时会产生

因此在我们执行ctrl + c关闭服务端时,会强制进程结束,导致正在访问的用户等出现问题

常见的 kill -9 pid 会发送 SIGKILL 信号给进程,也是类似的结果。

信号

本段中出现信号是什么呢?

信号是 Unix 、类 Unix 以及其他 POSIX 兼容的操作系统中进程间通讯的一种有限制的方式。

它是一种异步的通知机制,用来提醒进程一个事件(硬件异常、程序执行异常、外部发出信号)已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程。此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

所有信号

从go语言 go root SDK中 zerrors_linux_amd64.go 来看,关于信号有如下常量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
SIGABRT   
SIGALRM
SIGBUS
SIGCHLD
SIGCLD
SIGCONT
SIGFPE
SIGHUP
SIGILL
SIGINT
SIGIO
SIGIOT
SIGKILL
SIGPIPE
SIGPOLL
SIGPROF
SIGPWR
SIGQUIT
SIGSEGV
SIGSTKFLT
SIGSTOP
SIGSYS
SIGTERM
SIGTRAP
SIGTSTP
SIGTTIN
SIGTTOU
SIGUNUSED
SIGURG
SIGUSR1
SIGUSR2
SIGVTALRM
SIGWINCH
SIGXCPU
SIGXFSZ

怎样算优雅

优雅地退出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// TrapSignal catches the SIGTERM/SIGINT/SIGKILL and executes cb function. 
// After that it exits with code 0.
func TrapSignal(cb func()) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
go func() {
for sig := range c {
fmt.Sprintf("captured %v, exiting...", sig)
if cb != nil {
cb()
}
os.Exit(0)
}
}()
}

在接收退出信号SIGTERM/SIGINT/SIGKILL之后,系统退出os.Exit(0)之前,执行cb() callback函数。具体执行方法建议如下:

1
2
3
4
5
6
7
8
9
10
11
func Cmd(ctx *cli.Context) error {
// program of business logic
start()

TrapSignal(logger, func() {
// program of business exit here
stop()
})

return nil
}

gin退出

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
engine := gin.Default()
engine.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
TrapSignal(func() {
fmt.Println("before exit.")
})

engine.Run()
}

以下列表均为Ethereum token相关的提案:

EIP1080 这个是一种可以恢复的 token,其诞生的主要原因是因为以太坊的DAO之前的币被盗用才创建这个ip的,可以从合约层面恢复被盗或丢失的账户,这也减轻合理可证明的令牌或资产损失或盗窃的影响,并帮助解决其他冲突。不应该因为丢失、盗窃或冲突而修改Ethereum的协议,但是可以在智能契约层解决这些问题。其中定义的接口有一个抽象方法叫做claimLost,我在想这个东西是否可以利用在一些类似于财产险的场景中

EIP1132 可以对ERC20进行时间锁定,这就使得在一些交易过程中不需要将ERC20转给其他第三方的资金托管智能合约,因为这种托管的方式你需要额外的信任这个托管合约,而且在转账给托管合约的时候会造成gas的消耗

EIP998 是一种针对721和20以及223的扩展,使721token能够拥有其他721token和20token,使20和223能够由721令牌拥有,该规范涵盖了四种不同类型的可组合,998721(20)自顶向下的可组合token,用于接收、持有和传输721(20)token,998721(20)自底向上的可组合token,它们将自己附加到其他721token上,还包含一个rootowner的概念,这种token组合是一个树形的结构

EIP1175 是一种区块链电商的解决方案,通过提供一个center,这个center面向buyer能够快速建立一个钱包,面向saler可以快速建立一个商店,而且能够创建自己商店的结算erc20token。经分析可以使用在积分,代金券,优惠券等场景中

EIP902 token validator 提供token所有权和传输验证的服务协议,服务于监管机构的KYC和AML验证,因为监管机构需要将诸如身份等的非链合规信息与链上服务联系起来,通过一个共享的白名单而不是每个token都建立自己的白名单

EIP1207 类似于OAuth经过第三方授权

EIP1261 成员资格验证令牌 MVT 依然属于KYC的范畴,比如一些许可证的签发,例如在一些区块链的反洗钱应用,需要把一些地址和成员资格关联起来,达到监管的目的,也存在一种场景,就是我们自己发行的erc20token我们希望只能在我们许可的会员间传递

EIP1450 给了我一个启发就是合规和监管对应的要求应该从合约层面解决,仅仅是对原有的ERC20加了一些modifier就可以满足监管和法律的要求,ERC-1450必须防止任何人执行转移、允许和批准功能和/或实现这些功能,否则总是会失败,ERC-1450更新了transferFrom、mint和burnFrom函数。转让方、铸币方和burnFrom只能由RTA执行,且受限于唯一发布的转让方修改人。此外,ERC-1450还定义了transferOwnership、setTransferAgent、setPhysicalAddressOfOperation和isTransferAgent等函数。只有发行者可以调用transferOwnership、setTransferAgent和setPhysicalAddressOfOperation函数。任何人都可以调用isTransferAgent函数。

EIP1462 基本的安全token 这些要求包括KYC(了解您的客户)和AML(反洗钱)规则,以及为账户锁定令牌并限制其因法律纠纷而转移的能力。还可以附加附加的法律文档,以便在令牌和off-chain法律实体之间建立双重绑定关系。这个草案对EIP1400和EIP1450都不太认同,因为这两个草案都包含一些特定的场景,是比较小众的需求,1411和1410无法支持777,1066将引入一个转移检查功能,这包含四个新函数,用于检查所提供的输入是否允许操作

EIP1523 保单721标准,提供了一个jsonSchema,这个jsonSchema类似一个Interface接口,包含符合该标准的一些属性和需要实现的方法

EIP1948 对721的一个升级,因为有需求希望NFT用例能够与一个动态数据进行关联,该token可以在其生命周期内更改这个动态数据,如果有了这个,区块链电子病历和电子保单就能拥有动态的可修改的数据了

EIP1996 可持有的token,这里指的是这个token可以被hold,而不是以前的token只能被own,这就意味着第三方可以hold同一笔token而不是own他,这位监管提供了一个很好的方案,持有指定了付款人、收款人、最高金额、公证员和到期日。在创建持有时,将暂停支付方的指定令牌余额。持有余额在执行或释放之前不能转移。持有只能由公证人执行,这将触发令牌从付款人向收款人的转移。如果在任何时候由公证员或在期满后由任何人解除持有,则不进行任何转让,并且支付人可以再次获得该金额。在一些业务下,必须在事先不知道确切金额的情况下保证付款,比如酒店可以暂停客人的账户,作为对任何可能的额外服务(如客房服务)的担保。当客人结账离开时,持有部分被执行,剩余的金额仍然在客人的账户上。而在其他一些业务情况下,在使用其服务之前必须保证支付。例如:当入住酒店时,酒店会hold客人的账户,以确保在交钥匙之前有足够的余额支付房间费用。

EIP2018 可清除的token,其角色包含一个清算代理,操作员,开证申请人,清算过程将资金转移的承诺转化为资金从一个账户到另一个账户的实际流动。清算代理决定是否可以执行转移。应当转让的数额不能从支付人的余额中扣除,也不能用于其他转让,因此,保证了转让的执行是成功的。一个受管制的令牌需要符合所有的法律要求,特别是KYC和AML。其中一些检查可能无法在链上完成,因此传输可能无法在一个步骤中完成。目前还没有EIP来实现这样的外链检查。这个提议允许用户订购一个转移,可以由一个链外清算代理进行检查。根据它的结果,清算代理将执行或取消转移。要提供有关为何取消转账的更多信息,清算代理可以添加未执行转账的原因。

EIP2019 可资助的令牌,ERC-20标准令牌的扩展,允许令牌钱包所有者通过调用智能契约并附加一个资金指令字符串来请求为钱包提供资金。其角色包含一个令牌钱包的主人,象征性的合约所有者/代理人,开证申请人,令牌钱包所有者(或经过批准的地址)可以通过区块链订购令牌化请求。这是通过调用orderFund或orderFundFrom方法来完成的,这些方法启动令牌契约操作符的工作流,以支持或拒绝资金请求。在这种情况下,在提交请求时提供资金指令,操作人员使用这些指令来确定要借记的资金来源,以便(通过minting)为令牌钱包提供资金。一般而言,在区块链上逐字逐句地放置用于借记资金的显式路由指令是不可取的,建议使用私有通信替代方法,如私有通道、加密存储或类似的方法(在区块链分类帐之外)来这样做。另一种(不太理想的)可能性是将这些指令以加密的形式放在指令字段中。现在,大多数的令牌发行/资金请求,基于任何基于菲亚特的支付方法,需要一个以前的集中交易,能够获得所需的令牌发行请求者的钱包。为了尝试将所有需要的步骤引入到分散化中,公开令牌生命周期和支付事务的所有需要的步骤,一个资金请求可以允许钱包所有者通过区块链发起资金请求。关键好处:资金和付款的可追溯性得到了加强,从而使入账成为可能。所有的付款状态可以存储在链。几乎所有的货币/令牌生命周期都是通过一种分散的方法来覆盖的,并辅之以在生态系统中普遍使用的私有通信。

EIP2020 电子货币标准,如今,金融机构使用电子系统,这些系统在核心银行系统的数据库中保存账户余额。为了让一家机构能够保留客户账户余额的记录,并将其提供给客户,该机构必须在一个已知的法律框架下进行监管,而且必须拥有这样做的许可证。在监管监督下维持许可证,需要确保合规(即对所有客户执行KYC,并在允许交易前确保良好的“反洗钱”操作),并通过定期审计证明其技术和运营偿付能力,以便向机构存放资金的客户可以放心,他们的资金是安全的。

EIP2021 可支付令牌,令牌钱包的所有者(或批准的地址)可以通过区块链订购支付请求。这是通过调用orderPayoutFrom或orderPayoutFrom方法来完成的,这些方法启动令牌契约操作符的工作流,以支持或拒绝支付请求。在这种情况下,在提交请求时提供支付指令,操作人员使用这些指令来确定资金的目的地。通常,不建议在区块链上逐字逐句地为支出设置显式路由指令,建议使用私有通信替代方法,例如私有通道、加密存储或类似的方法(在区块链分类账之外)。另一种(不太理想的)可能性是将这些指令以加密的形式放在指令字段中。