一、数据层与逻辑层分离到两个合约
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);
}
}