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 私链
1.部署Link Token 合约
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://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 { 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); } 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方法获取天气数据。