链下预言机的代理合约的两种设计模式 2020年10月25日

一、数据层与逻辑层分离到两个合约

在这里插入图片描述
OracleAddrResolver.sol, 用于 保存 和 获取 Oracle.sol 的合约地址:

/* 
 *
 * SPDX-License-Identifier: TeslaZhou & 鄱阳县凰岗镇周鑫,
 * 微信:blockchainxunlei_com,
 * QQ :3367871560。
 * 中国成功男士一枚,目前单身,欢迎各位靓女加我交流区块链技术,哈哈。
 *
*/
pragma solidity 0.5.17; 


/* 极简的代理合约 OracleAddrResolver.sol,
 * 用于 保存 和 获取 某个方法要调用的合约的地址。 
 */
contract OraclizeAddrResolver {

    address public addr;

    address public owner;

    constructor() public{
        owner = msg.sender;
    }

    function changeOwner(address newowner) public {
        require (msg.sender == owner, "error------You aren't the owner !");
        owner = newowner;
    }

    function getAddress() external view returns (address oaddr){
        return addr;
    }

    function setAddr(address newaddr) public {
        require (msg.sender == owner, "error------You aren't the owner !");
        addr = newaddr;
    }

}

Oracle.sol 预言机的逻辑操作层(保存被client.sol调用的各种方法):

/* 
 *
 * SPDX-License-Identifier: TeslaZhou & 鄱阳县凰岗镇周鑫,
 * 微信:blockchainxunlei_com,
 * QQ :3367871560。
 * 中国成功男士一枚,目前单身,欢迎各位靓女加我交流区块链技术,哈哈。
 *
*/
pragma solidity 0.5.17;
pragma experimental ABIEncoderV2;
/**
 * 预言机合约
 */
contract Oracle{
    
using SafeMath for uint256;
    
mapping(address => uint256) public user_BalanceETH;
uint public queryID = 0;
struct query { 
    address user;
    string url;
    string[4] args; // 在Client中我们规定:参数的在url中的位置用**占领
    string want;
    uint256 Client_Want_Time_Interval;

    bool Server_sucess;
    }
    mapping (uint256 => string) public results;
    
    mapping (uint256 => query) public querys;    
   
    function Oracle_ETH() public view returns(uint256){ return address(this).balance; }
    

    
    // 通过监听事件geturl得到url,解析url,爬取数据
    event LogQuery(uint256 queryID, address indexed user, string url, string[4] args, string want, uint256 Client_Want_Time_Interval);

    
    /*
    从 Solidity 0.6.9 版本开始,之前仅用于外部函数(external 修饰的函数[2])的calldata位置,现在可以在内部函数( internal修饰的函数[3] )使用了。
    请注意,由于EVM不允许修改 calldata,因此无法在 calldata 变量中创建新值或将某些内容复制到 calldata变量。
    内部函数可以遍历 calldata 的数组,而不用再复制到内存了。
    使用 calldata 变量的好处是,它不用将 calldata 数据的副本保存到内存中,并确保不会修改数组或结构(calldata 位置是只读的),
    因此,如果可以的话,请尽量使用 calldata 作为数据位置
    */
    // 接收请求,通过监听事件 LogQuery 得到url,解析url,爬取数据
    function Query_0(string calldata url, string calldata want, uint256 Client_Want_Time_Interval) external payable returns(uint256 _queryID){
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
        _queryID = queryID++;
        query storage _query = querys[_queryID];
        
        _query.user = msg.sender;
        _query.url = url;
        _query.want = want; 
        _query.Client_Want_Time_Interval = Client_Want_Time_Interval;
        
        // _query.Server_sucess = Succee; 写入Client时再赋值
        emit LogQuery(_queryID, msg.sender, url, _query.args, want, Client_Want_Time_Interval);
        
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return _queryID;
    }
    function Query_1(string calldata url, string calldata args, string calldata want, uint256 Client_Want_Time_Interval) external payable returns(uint256 _queryID){
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
        _queryID = queryID++;                   
        query storage _query = querys[_queryID];
        
        _query.user = msg.sender;
        _query.url = url;
        _query.args[0] = args;
        _query.want = want; 
        _query.Client_Want_Time_Interval = Client_Want_Time_Interval;
        
        // _query.Server_sucess = Succee; 写入Client时再赋值
        emit LogQuery(_queryID, msg.sender, url, _query.args, want, Client_Want_Time_Interval);
        
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return _queryID;
    }    
    function Query_2(string calldata url, string[2] calldata args, string calldata want, uint256 Client_Want_Time_Interval) external payable returns(uint256 _queryID){
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
        _queryID = queryID++;
        query storage _query = querys[_queryID];
        
        _query.user = msg.sender;
        _query.url = url;
        _query.args[0] = args[0];
        _query.args[1] = args[1];
        _query.want = want; 
        _query.Client_Want_Time_Interval = Client_Want_Time_Interval;
        
        // _query.Server_sucess = Succee; 写入Client时再赋值
        emit LogQuery(_queryID, msg.sender, url, _query.args, want, Client_Want_Time_Interval);
        
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return _queryID;
    }
    function Query_3(string calldata url, string[3] calldata args, string calldata want, uint256 Client_Want_Time_Interval) external payable returns(uint256 _queryID){
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
         _queryID = queryID++;
        query storage _query = querys[_queryID];
        
        _query.user = msg.sender;
        _query.url = url;
        _query.args[0] = args[0];
        _query.args[1] = args[1];
        _query.args[2] = args[2];
        _query.want = want; 
        _query.Client_Want_Time_Interval = Client_Want_Time_Interval;
        
        // _query.Server_sucess = Succee; 写入Client时再赋值
        emit LogQuery(_queryID, msg.sender, url, _query.args, want, Client_Want_Time_Interval);
        
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return _queryID;
    }
    function Query_4(string calldata url, string[4] calldata args, string calldata want, uint256 Client_Want_Time_Interval) external payable returns(uint256 _queryID){
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
         _queryID = queryID++;
        query storage _query = querys[_queryID];
        
        _query.user = msg.sender;
        _query.url = url;
        _query.args[0] = args[0];
        _query.args[1] = args[1];
        _query.args[2] = args[2];
        _query.args[3] = args[3];
        _query.want = want; 
        _query.Client_Want_Time_Interval = Client_Want_Time_Interval;
        
        // _query.Server_sucess = Succee; 写入Client时再赋值
        emit LogQuery(_queryID, msg.sender, url, _query.args, want, Client_Want_Time_Interval);
        
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return _queryID;
        
    }
    

    
    
    
}



/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, reverts on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a);
    uint256 c = a - b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

CSDNserver.sol 代理合约,预言机的数据保留层(storage类型的布局要求和Oracle.sol一样,保留被Oracle.sol方法操作的结果):

/* 
 *
 * SPDX-License-Identifier: TeslaZhou & 鄱阳县凰岗镇周鑫,
 * 微信:blockchainxunlei_com,
 * QQ :3367871560。
 * 中国成功男士一枚,目前单身,欢迎各位靓女加我交流区块链技术,哈哈。
 *
*/

pragma solidity 0.5.17;
/**
 * 极简的代理合约 OracleAddrResolver.sol,
 * 用于 保存 和 获取 某个方法要调用的合约的地址。 
 */
interface OracleAddrResolverI {
    function getAddress() external view returns (address oaddr);
}


contract CSDNserver{
/* For Client API*/
    
using SafeMath for uint256;
    
mapping(address => uint) public user_BalanceETH;
uint public queryID = 0;
struct query { 
    address user;
    string url;
    string[4] args; // 在Client中我们规定:参数的在url中的位置用**占领
    string want;
    uint Client_Want_Time_Interval;

    bool Server_sucess;
    }
    mapping (uint256 => string) public results;
    
    mapping (uint256 => query) public querys;    
    
    
    address payable public Owner;
    constructor() public payable{ Owner = msg.sender; }
    modifier onlyOwner { require( msg.sender == Owner, "error------You are't the Owner."); _;}
    
    function withdrawFunds(address payable beneficiary, uint withdrawAmount) external onlyOwner {
        require( address(this).balance >= withdrawAmount, "error-----address(this).balance < withdrawAmount");
        beneficiary.transfer(withdrawAmount);
    }    
    
    
    
function CSDNserver_ETH() public view returns(uint256){ return address(this).balance; }
    
    
                                /*  OAR对应的OracleAddrResolver.sol用于 保存 和 获取 某个方法要调用的合约的地址。 */
    OracleAddrResolverI public OAR;
    
    string public Oracle_network_name;
    
    function ol_setNetworkName(string memory _network_name) private {
        Oracle_network_name = _network_name;
    }
    
    
//  由于EVM认为对不存在的合同的调用始终会成功,因此Solidity使用extcodesize操作码来检查要被调用的合同是否确实存在(包含代码),如果不存在,则会导致异常。
    function getCodeSize(address _addr) view private returns(uint _size) {
        assembly {  _size := extcodesize(_addr) }
    }
    
    function ol_setNetwork() public returns(bool){
        // if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed)>0){ //mainnet
        //     OAR = OracleAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed);
        //     ol_setNetworkName("eth_mainnet");
        //     return true;
        // }
        if (getCodeSize(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c)>0){ //ropsten testnet
            OAR = OracleAddrResolverI(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c);
            ol_setNetworkName("eth_ropsten");
            return true;
        }
        // if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e)>0){ //kovan testnet
        //     OAR = OracleAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e);
        //     ol_setNetworkName("eth_kovan");
        //     return true;
        // }
        if (getCodeSize(0x47D44B02253eB885B818F3e2078fC63CDe93CEBD)>0){ //rinkeby testnet
            OAR = OracleAddrResolverI(0x47D44B02253eB885B818F3e2078fC63CDe93CEBD);
            ol_setNetworkName("eth_rinkeby");
            return true;
        }
        // if (getCodeSize(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41)>0){ //goerli testnet
        //     OAR = OracleAddrResolverI(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41);
        //     ol_setNetworkName("eth_goerli");
        //     return true;
        // }

        return false;
    }    
    
    modifier olAPI {
        if( (address(OAR)==address(0)) || (getCodeSize(address(OAR))==0) )  ol_setNetwork();
        _;
    }    
    
    event Recharge(address indexed user,uint _wei,uint RechargeTime,uint user_BalanceETH);
    function() payable external olAPI { 
       // if(msg.data.length == 0) { // call只转发ETH
        require(msg.value > 32910, "error-----msg.value = 0");
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].add(msg.value);
        emit Recharge(msg.sender,msg.value,block.timestamp,user_BalanceETH[msg.sender]);   
       // }
       if(msg.data.length > 0) _delegate(OAR.getAddress());
    }    
    
    /**
    * @dev Fallback function allowing to perform a delegatecall to the given implementation.
    * This function will return whatever the implementation call returns
    *  后备功能允许对给定的实现执行委托调用。
    *  此函数将返回实现调用返回的任何内容
    */
    function _delegate(address implementation) private {
        /*solium-disable-next-line security/no-inline-assembly*/
    assembly {
                                        // Copy msg.data. We take full control of memory in this inline assembly
                                        // block because it will not return to Solidity code. We overwrite the
                                        // Solidity scratch pad at memory position 0.
    calldatacopy(0, 0, calldatasize)
                                        // Call the implementation.
                                        // out and outsize are 0 because we don't know the size yet.
    let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
                                        // Copy the returned data.
    returndatacopy(0, 0, returndatasize)
    switch result
                                        // delegatecall returns 0 on error.
    case 0 { revert(0, returndatasize) }
    default { return(0, returndatasize) }
        }
    }
    
    
    event Logwrite(uint256 queryID, string write_result, bool Server_sucess);
    event Logcallback(address user,address CSDNserver,string callback_result,uint256 time);    
    function write(uint256 myid, string calldata result) onlyOwner external{ 
        results[myid] = result;
        emit Logwrite(myid,result,true);
        querys[myid].Server_sucess = true;
    }
    
    function _callback(uint256 myid) external returns(string memory result){ 
        require( user_BalanceETH[msg.sender] >= 100000000000, "error-----user_BalanceETH[user] < 100 gwei." );
        return callback(myid);
    }
    
    function callback(uint256 myid) internal returns(string memory result) { 
        result = results[myid];
        emit Logcallback(tx.origin,msg.sender,result,block.timestamp);
        user_BalanceETH[msg.sender] = user_BalanceETH[msg.sender].sub(100000000000);
        return result;
    }    

    
    
}


/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, reverts on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a);
    uint256 c = a - b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

CSDNclient.sol 客户端测试合约:

/* 
 *
 * SPDX-License-Identifier: TeslaZhou & 鄱阳县凰岗镇周鑫,
 * 微信:blockchainxunlei_com,
 * QQ :3367871560。
 * 中国成功男士一枚,目前单身,欢迎各位靓女加我交流区块链技术,哈哈。
 *
*/

pragma solidity 0.5.17;

contract CSDNclient {
 
    uint256 public Query_ID; 
    mapping (uint256 => string) public results;  
 

    function recharg_1(address payable _addr) payable public returns(bytes memory){ 
        (bool suc, bytes memory callData) = _addr.call.value(1 ether)("");
        require(suc, " error------call--false");
        return callData;
    }
    function recharg_2(address payable beneficiary, uint withdrawAmount) payable external {
        require( address(this).balance >= withdrawAmount, "error-----address(this).balance < withdrawAmount");
        beneficiary.transfer(withdrawAmount);
    }
    
    
    // 请求数据
    function update(address payable _addr) payable public returns(uint256){
        (bool suc, bytes memory callData) = _addr.call.value(32911 wei).gas(0.01 ether)(
            abi.encodeWithSignature(
            "Query_1(string,string,string,uint256)", 
            "https://api.etherscan.io/api?module=stats&action=ethprice&apikey=**","G3NMUCPVFVDFZN1M96U7AQ2EXXXRG8BDTJ","ethusd",180)
            );
        require(suc,"error----update----false");
                    // Query_ID = sliceUint(callData,0);
        Query_ID = abi.decode(callData,(uint256));
        return Query_ID;
    }
     
     
    function CSDNclient_callback(address _addr,uint8 _start) public returns(bytes memory,uint256,string memory){   
                        // (bool succees,bytes memory data) = _addr.delegatecall(abi.encodeWithSignature("test()")); 
            (bool succees,bytes memory data) = _addr.call.value(0 wei).gas(0.01 ether)(abi.encodeWithSignature("_callback(uint256)",Query_ID)); 
            require(succees, "error----CSDNclient_callback----false");
                        // results[Query_ID] = sliceUint(data,_start);
            results[Query_ID] = abi.decode(data,(string));
            return (data, sliceUint(data,_start),results[Query_ID]);
    }
    
    
    constructor() public payable{}
    
    // 动态bytes数组 转 uint256
    function sliceUint(bytes memory bs, uint start) internal pure returns(uint256) {
       require(bs.length >= start + 32, "slicing out of range");
       uint x;
       assembly {
          x := mload(add(bs, add(0x20, start)))
         }
       return x;
    }
    // bytes32 转 string
    function bytes32ToString(bytes32 x) internal pure returns(string memory) {
        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 (uint j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }    
    
    
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值