区块链就像黑匣子一样是完全封闭的,无法与外部世界连通,智能合约本身也无法连接链下数据。对于现实世界中的例如:天气,比赛分数以及航班信息等都无法获取,这也是智能合约最大的痛点,极大程度上限制了智能合约开发者的创造力,那么有什么办法可以解决吗?
区块链就像黑匣子一样是完全封闭的,无法与外部世界连通,智能合约本身也无法连接链下数据。对于现实世界中的例如:天气,比赛分数以及航班信息等都无法获取,这也是智能合约最大的痛点,极大程度上限制了智能合约开发者的创造力,那么有什么办法可以解决吗?
答:当然是有的,预言机则充当这类角色,负责上传现实世界中的真实数据到智能合约。
预言机分为中心化预言机和去中心化预言机。
工作流程
中心化预言机
负责上传现实世界的真实数据到智能合约的一个数据源,但是由于中心化的缘故,中心化预言机中不仅存在单点失败风险,而且还存在数据不安全风险,这又变相削弱了智能合约安全性的特性。
去中心化预言机
多个数据节点形成去中心预言机,每个节点都会收集数据,达成共识后输入到区块链的智能合约。而
chainlink便是其中的一种。
- 技术上:避免了单点失败风险。
- 数据上:通过网络对多个数据源进行验证。
chainlink提供了Data Feed,VRF,Automation 等功能,目前采用的共识机制是取中位数。
业务流程
chainlink部署到区块链中的智能合约,最后用户可以通过部署在区块链的智能合约获取到相应的价格数据。
技术架构
采用代理模式,便于合约的升级。
使用流程如下:

应用案例

Tools: Foundry
Env: Sepolia
Data Source: https://docs.chain.link/data-feeds/price-feeds/addresses
Step:
forge initforge install https://github.com/smartcontractkit/chainlink --no-commit查询合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
contract PriceFeed {
    AggregatorV3Interface priceFeed;
    constructor() {
        priceFeed = AggregatorV3Interface(
            0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43
        );
    }
    function getPrice() public view returns (int256 price) {
        (, price, , , ) = priceFeed.latestRoundData();
    }
}
部署合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {PriceFeed} from "../src/DataFeed.sol";
contract DeployDataFeed is Script {
    function run() external returns(PriceFeed priceFeed) {
        vm.startBroadcast();
        priceFeed = new PriceFeed();
        vm.stopBroadcast();
    }
}执行查询操作:
cast to-dec $(cast call 0x9AC6521008b6Cf909b0360db0B6819bBa895D559 "getPrice()" --rpc-url $env:s_rpc --private-key $env:s_pk)
可以有效减少依赖单一数据源的风险,从而降低了价格被操作的可能性。
举个例子,假如某个项目的代币价格依赖于某个 dex,hacker可以通过闪电贷的功能,大幅度操纵dex的价格,达到操纵项目的代币价格,从而完成一系列的恶意操作。
链上方案:

区块哈希在某种程度上容易被控制,因为区块中包含的内容是交易,交易哈希则是由这些交易所决定的,如果矿工是作恶节点,ta可以通过选择某些特定的交易进行打包,从而控制生成的随机数,进而达到操控随机数的目的。所以这种方式不可行,该作恶方式也被称为MEV。
链下方案:

随机数由链下的预言机生成,且不完全取决于区块哈希,同时预言机的私钥也参与运算。
为了防止伪造随机数,chainlink预言机提供了可以验证的随机数。验证要点:
可验证随机数(VRF)的特点:
// VRF 是由3个函数组成
// 1. 密钥生成函数 
G(r) => (PK, SK)
PK: public key
SK: secret key
// 2. 随机数生成函数
E(SK, seed) => (Randomness, Proof)
seed: RNC的种子
Randomness: 随机数
Proof: 证明
// 3. 验证函数
V(PK, seed, Randomness, Proof) => (Trus or False)
True:  验证成功
False: 验证失败业务流程

技术架构

VRF使用场景

Purpose:
/**
    1. register VRF subscription
    2. add consumer into subscription
    3. consumer -> request random number
    4. consumer <- receive random number
*/Tools: Foundry
Env: Sepolia
Data Source:
Step:
step1




step2:订阅信息

step3:合约编写
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {ChainlinkVRF} from "../src/ChainlinkVRF.sol";
contract DeployChainlinkVRF is Script {
    function run() external returns(ChainlinkVRF chainlinkVRF) {
        vm.startBroadcast();
        chainlinkVRF = new ChainlinkVRF();
        vm.stopBroadcast();
    }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IVRFCoordinatorV2Plus, VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
// contract address : 0xD12546C3f4777fe85Cbb9101e4FC3a8e2D0D84fD
contract ChainlinkVRF is VRFConsumerBaseV2Plus {
    IVRFCoordinatorV2Plus COORDINATOR;
    address vrfCoordinatorAddr = 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B;
    uint256[] public s_randomWords;
    constructor() VRFConsumerBaseV2Plus(vrfCoordinatorAddr) {
        COORDINATOR = IVRFCoordinatorV2Plus(vrfCoordinatorAddr);
    }
    function requestRandomWrods(
        bytes32 keyHash,
        uint256 subId,
        uint16 requestConfirmations,
        uint32 callbackGasLimit,
        uint32 numWords
    ) external onlyOwnerOrCoordinator returns (uint256 requestId) {
        VRFV2PlusClient.RandomWordsRequest memory request = VRFV2PlusClient
            .RandomWordsRequest(
                keyHash,
                subId,
                requestConfirmations,
                callbackGasLimit,
                numWords,
                ""
            );
        requestId = COORDINATOR.requestRandomWords(request);
    }
    function fulfillRandomWords(
        uint256,
        uint256[] calldata randomWords
    ) internal override {
        s_randomWords = randomWords;
    }
    function getRandomWrods() external view returns (uint256[] memory) {
        return s_randomWords;
    }
}step4:将部署的地址添加到官网

step5:获取随机数并验证
cast send 0xD12546C3f4777fe85Cbb9101e4FC3a8e2D0D84fD "requestRandomWrods(by" tes32,uint256,uint16,uint32,uint32)" 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae 9345996778540633145599080787286650640079903515402078432104978623655322245466 3 300000 5 --rpc-url $env:s_rpc --private-key $env:s_pk 
cast call 0xD12546C3f4777fe85Cbb9101e4FC3a8e2D0D84fD "getRandomWrods()" --rpc-url $env:s_rpc --private-key $env:s_pk
成功获取五个随机数,长度都是uint256类型的,如果不需要这么长,可以在实际开发中按需取模等。
通过链下预言机获取随机数可以防止被作恶矿工控制随机数,这也是 solidity CTF中一种常见的题型,用户可以通过控制block.timestamp,block.difficulty等区块信息从而达到控制随机数的效果。
合约自动化执行: 顾名思义,合约执行执行。比如:当某个代币的价格达到某个指定的值或者区间的时候,合约会自动执行买入或者卖出的操作。
手动DevOp&中心化服务器 和 Bounty模式

这两种方式都存在一定的风险。
Chainlink Automation
 **业务流程**
用户编写 upkeep合约,并注册到chainlink中;
预言机会在每个区块中检查 CheckUpkeep,判断是否满足条件;
如果CheckUpkeep条件不满足,则下一个区块继续检查;
如果CheckUpkeep条件满足了,预言机则调用 keepers注册合约;
keepers注册合约调用用户合约perfomUpkeep。
技术架构

注:CheckUpkeep是在链下预言机完成的,可以节省gas费。
使用场景

Tools: Foundry
Env: Sepolia
Data Source: https://automation.chain.link/
需求
“我”的时候,合约将自动领取steps
step1:编写智能合约,并部署到 sepolia测试网上

step2:注册合约地址






step3:测试功能
先查看UserKeeper合约是否获奖
 cast call 0xaCCd83C73663Ef519501a38FA0C26d1736b1CB5c "rewards(address)" 0x48a8423d88d492B929dDD60D36BdC112366Ae52e --rpc-url $env:s_rpc --private-key $env:s_pk
# output=> 0x0000000000000000000000000000000000000000000000000000000000000000设置Airdrop合约中的获奖者
cast send 0xaCCd83C73663Ef519501a38FA0C26d1736b1CB5c "setWinner(address)" 0x48a8423d88d492B929dDD60D36BdC112366Ae52e --rpc-url $env:s_rpc --private-key $env:s_pk
再次查看UserKeeper合约是否获奖

可以看到,此时UserKeeper已获奖【100】,再去Chainlink中查看操作记录

测试合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";
// contract address => 0x48a8423d88d492B929dDD60D36BdC112366Ae52e
contract UserKeeper is AutomationCompatibleInterface {
    Airdrop public airdrop;
    constructor() {
        airdrop = new Airdrop();
    }
    function checkUpkeep(
        bytes calldata
    )
        external
        view
        override
        returns (bool upkeepNeeded, bytes memory performData)
    {
        if (airdrop.winner() == address(this)) {
            return (true, "");
        }
    }
    function performUpkeep(bytes calldata) external override {
        airdrop.airdrop();
    }
}
// contract address => 0xaCCd83C73663Ef519501a38FA0C26d1736b1CB5c
contract Airdrop {
    mapping(address => uint256) public rewards;
    address public owner;
    address public winner;
    constructor() {
        owner = tx.origin; // there is a risk
    }
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    function setWinner(address _winner) external onlyOwner {
        winner = _winner;
    }
    function airdrop() external {
        require(msg.sender == winner, "You are not the winner.");
        rewards[msg.sender] = 100;
    }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {UserKeeper, Airdrop} from "../src/Automation.sol";
contract DeployAutomation is Script {
    function run() external returns (UserKeeper userKeeper, Airdrop airdrop) {
        vm.startBroadcast();
        userKeeper = new UserKeeper();
        airdrop = userKeeper.airdrop();
        vm.stopBroadcast();
    }
} 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!