背景介绍

近期有社区的小伙伴在开发智能合约并布置到Conflux网络时遇到了关于给智能合约发送交互时进行典当一部分cfx,并在交互后返还一部分cfx的问题。

正如烤仔回答的那样,在区块链中因为需求依据调用者对智能合约空间占用的状况收取必定数量的存储押金,这笔费用一般高过实践的需求,当空间开释时,就会返回给操作者必定数量的GAS费用。

Conflux在其协议规范中专门对这一机制进行了详细的介绍(参见第28页)。

理论介绍

在Conflux中引入了Collateral for storage(简称CFS)机制,作为运用存储的定价方式,相比Ethereum中的一次性存储费用,CFS机制会愈加公平合理。原则上,这种机制需求确定一笔资金,作为占用存储空间的典当物。在相应的存储空间被开释或被他人掩盖前,典当物都会被确定,而被确定的典当物所发生的相应利息会直接分配给矿工,用于存储空间的保护。因而,Conflux的存储成本也取决于空间占用的时刻长短。

在Conflux网络中,每个存储条目占用空间是64B(B为Bytes,字节),这也是国际状况下键/值对的巨细,需求阐明的是在区块链中键一般为256bits长,值也是256bits长(各自都是32B长,合起来为64B长)。贮存所需的押金与可以掩盖所有贮存物品的64B的最小倍数成正比。关于每一个存储条目,最后向该条目写入的账户称为该存储条意图所有者。假如某存储条目是在履行合约C时所写,且有担保人提供担保,那么C被视为该条意图写者,也相应地成为所有者(详见7.1节)。在国际状况下,一个存储条意图整个生命周期内,该条意图所有者必须确定固定数量的CFX作为占用存储空间的存储押金。具体来说,每一个巨细为64B的存储条目,其主人会被确定1/16CFX。而占用1KB空间则支付1CFX作为押金,其对应公式如下:

(102464)(116)=1(CFX)\left(\frac{1024}{64}\right)\left(\frac{1}{16}\right)=1(CFX)

在账户成为一个存储条意图所有者时(无论是创立还是修正),应立即为该条目确定1/16 CFX。假如有满足的余额,那么就会主动确定所需的押金,否则假如没有满足的余额,操作就会失利,无法创立或修正该条目。

当一个存储条目从国际状况中被删去时,相应的1/16 CFX押金将被解锁并返回到该条目所有者的余额中。假如一个存储条意图所有权发生变化,旧所有者的1/16 CFX押金被解锁,而新的所有者必须一起确定1/16 CFX作为押金。

为了便利处理,Conflux中引入了函数CFS,它将一个帐户地址和一个国际状况作为输入,并返回 在国际状况下,账户存储的确定押金总额。 假如国际状况从上下文中清晰,为了简练起见,咱们用CFS()替代CFS(;),其公式如下:

CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数(116)(CFX)CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数\left(\frac{1}{16}\right)(CFX)

特别的,关于一个由=S(T)发送的买卖T(或=T假如T调用的是地址T处的赞助合约),令为T履行前后的国际状况,是买卖履行结束后的国际状况,针对存储限制字段Tl有一个CFS(;)≤CFS(;)+Tl/1018的断言。

要害点:想弄清楚调用合约质押的CFX有多少,必定要弄清楚合约中变量的条目数,以及在通过函数调用合约进行操作时有多少条目被修正,且改动的数目被记载到了区块链中!

Solidity内存管理机制

依据Solidity文档关于其内存管理的描述及Conflux存储押金机制,咱们可以发现,合约存储需求有key和value进行保护,一般状况下:key的长度为256bits,value的长度相同为256bits,按一个智能合约的存储空间依照如下表格进行组织,其中{0,1}256表明256位比特串(比特串中只要0或1两个值),每个key/value对就可以被理解为一个条目

条目 键/地址({0,1}256) 值({0,1}256)
1 0…00000 0
2 0…00001 1
3 0…00002 2
2256 f…fffff 0

因为256bits=32bytes,两个256bits对应的长度为: 32+32=64(Bytes)32+32=64(Bytes)

Solidity中常见变量及其对应的条目数整理

变量 长度 定义方式
一般变量 1个一般变量对应1个条目 uint public count=0;
mapping mapping的每1个key都对应于1个条目 mapping(address => uint) public balances;
array 数组每1个元素对应于1个条目,数组长arr.length是额外的1个条目 uint[5] fixedArr = [1,2,3,4,5]; string productname;
struct struct内每个field对应条目数的累加 struct Person {uint age;uint stuID;string name;}

Conflux的存储押金机制描述

Conflux 的网络中,存储押金的费用是每 1024 字节 1 CFX。因为每个条目占用 64 字节,因而,每个条意图押金费用便是 1/16 CFX. 每笔买卖履行期间,新发生的押金费用会在买卖履行结束的时一致收取。假如一个存储条目被其他人改写了,改写的人将交纳存储押金,而原先的押金交纳者将得到退回的押金。值得一提的是,押金退回是“悄悄”加在余额里的,并没有转账买卖可供查询。

Conflux 的每笔买卖中,需求填写一个存储上限(单位是字节)。该上限规则了,押金交纳者在买卖履行前后押金增量不得超越存储上限乘 1/1024 CFX. 假如这个值填写得过低,会导致履行后押金超越上限,履行失利。假如填写的过高,导致发送者余额不足以支付押金,也会导致买卖失利。

布置合约

请参阅链接,测验布置和调用智能合约。

实例解说

1. 一个含有1个一般uint变量的比如

智能合约代码如下:

pragma solidity ^0.5.0;
contract Counter {
    uint public count=0;
    event SelfEvent(address indexed sender, uint current);
    constructor() public {
    }
    function inc(uint num) public returns (uint){
        return count += num;
    }
    function self() public {
        emit SelfEvent(msg.sender, count);
    }
}

因为该智能合约中只要 uint public count=0; 一个变量,只对应于1个条目,依照之前的公式剖析: CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数(116)(CFX)CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数\left(\frac{1}{16}\right)(CFX)

正是因为 uint public count=0 变量正好对应于一个64B的条目,结合所以之前咱们给定的实例,真实质押的数额为:0.0625 CFX (116)=0.0625(CFX)\left(\frac{1}{16}\right)=0.0625(CFX)

下面结合对合约的实践调用进行验证: 调用合约代码的案例如下(文件名为:call_calc.js):

const { Conflux, util } = require('js-conflux-sdk');
// 这个地址是上面打印出来的 receipt.contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x845dd6f64bb3d2771a8f30dc85bb14f5ac26b75e';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425********************';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812********************';
const compiled = require(`./build/Counter.json`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  let inc = await contract.inc(10);
  console.log("输出:"  + inc.toString());
  const account1 = cfx.Account(PRIVATE_KEY1);//运用私钥创立账户
  // 进行记载并花费CFX
  await contract.inc(10).sendTransaction({ from: account1 }).confirmed();
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call inc(10) the current drip:"+after_call_balance.toString());
  console.log("after account1 call inc(10) the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  //创立account2,并测验调用合约希望开释account1的cfx
  const account2 = cfx.Account(PRIVATE_KEY2);//运用私钥创立账户
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call inc(5) the current drip:"+after_call_balance.toString());
  console.log("before account2 call inc(5) the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.inc(5).sendTransaction({ from: account2 }).confirmed();
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call inc(10) the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call inc(10) the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

调用方式为:

node call_calc.js

为了便利描述,将参加调用合约的账户用account1和account2进行表明,被调用的合约用contract进行表明,将其账户信息和对应的账户地址进行汇总 ,所列表格如下:

账户名 账户地址
account1 0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account2 0x1941E3137aDDf02514cBFeC292710463d41e8196
countract 0x845dd6f64bb3d2771a8f30dc85bb14f5ac26b75e

为便利表明,后文运用上表中账户名指代各账户

在调用前,运用confluxscan查account1对应的CFX余额,发现余额为:1994.680912261955354268 CFX。

Conflux的存储抵押机制

(1)运用account1进行第一次合约调用:

Conflux的存储抵押机制

因为account1调用合约占用了空间,需求上交CFX作为押金

在程序发动后:首要显现account1的账户余额:1994.680912261955383167 CFX。

程序会运用account1调用contract.inc(10)向合约建议交互,调用完成后发现account1的账户余额会变为:1994.618412261955356217 CFX。

Conflux的存储抵押机制

也便是说通过account1与合约contract的这一次交互操作,其账户扣除了: 1994.680912261955383167−1994.618412261955356217=0.06250000000002695(CFX)1994.680912261955383167-1994.618412261955356217=0.06250000000002695(CFX)

这阐明因为调用contract.inc(10)与合约进行交互。account1上交了0.06250000000002695的CFX。

运用account2调用合约,以协助account1开释空间

程序会持续运转,并运用account2通过调用contract.inc(5)向合约建议交互

在调用前,account1的账户余额为:1994.618412261955356217 CFX,与过程(1)结束时account1的账户余额保持一致。

account2调用合约后,account1的CFX余额变为1994.680912261955356217 CFX

Conflux的存储抵押机制

也便是说,通过account2对合约的调用后,account1的账户CFX余额变化为: 1994.618412261955356217−1994.680912261955356217=−0.0625(CFX)1994.618412261955356217-1994.680912261955356217=-0.0625(CFX)

这意味着,因为account1占用的64Bytes合约空间被开释,0.0625 CFX会被交还到了account1的账户中。依照过程(1)上钩算得到的付款额:0.06250000000002695 CFX,咱们可以估测,account1调用contract.inc(10)实践所耗费的费用为: 0.06250000000002695−0.0625=0.00000000000002695(CFX)0.06250000000002695-0.0625=0.00000000000002695(CFX)

调用合约前,程序显现的account1的CFX余额为:1994.618412261955437067 CFX。

而调用合约后,对应账户的CFX余额为:1994.618412261955410117 CFX。

也便是说通过这次与合约的交互操作,账户扣除了0.00000000000002695个CFX,其计算公式如下: 1994.618412261955437067−1994.618412261955410117=0.00000000000002695(CFX)1994.618412261955437067-1994.618412261955410117=0.00000000000002695(CFX)

这也直接佐证了,account1中数据存储所占用的合约空间便是1个64Bytes: 0.062516=1(个)0.062516=1(个)

此刻咱们再去confluxscan处检查账户account1对应的余额为:1994.680912261955327318 CFX

Conflux的存储抵押机制
依照计算公式: 1994.680912261955354268−1994.680912261955327318=0.00000000000002695(CFX)1994.680912261955354268-1994.680912261955327318=0.00000000000002695 (CFX) 这一起也佐证了:account1调用contract.inc(10)与合约进行交互时,其账户实践耗费了0.00000000000002695 CFX

2. 一个含有1个长度为5(且在调用时修正5个元素值)的定长数组的比如

合约代码如下:

pragma solidity ^0.5.0;
contract Test {
    uint[5] arr = [1,2,3,4,5];
    event SelfEvent(address indexed sender, uint[5] current,uint length);
    function init() public{
        arr[0] = 100;
        arr[1] = 200;   
    }
    function getArrayContent() public returns(uint[5] memory){
        return arr;
    }
    function getArrayLength() public returns(uint){
        return arr.length;
    }
	function increment (uint data) public{
		for(uint i=0;i<arr.length;i++){
            arr[i]+=data;
        }
	}
    function getGrade() public returns (uint){
        uint grade = 0 ;
        for(uint i=0;i<arr.length;i++){
            grade += arr[i];
        }
        return grade;
    }
	function self() public {
        emit SelfEvent(msg.sender, arr,arr.length);
    }
}

因为该智能合约中只要 uint [] arr = [1,2,3,4,5]; 这个长度为5的数组,数组内数据对应了5个条目,数组长度5一起也对应了1个条目,共6个条目,因为调用合约中的 increment() 函数会修正数组中的每一个元素,但没有改动数组长度,因而依照之前的公式剖析: CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数(116)(CFX)CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数\left(\frac{1}{16}\right)(CFX)

正是因为 uint [] arr = [1,2,3,4,5]; 数组内元素及数组长度正好对应了6个64B长度的条目,但因为数组长度没有改动并写入区块链,结合之前咱们给定的实例剖析,质押的数额应当为:0.3125 CFX (116)5=0.3125(CFX)\left(\frac{1}{16}\right)5=0.3125(CFX)

调用合约的代码如下所示:

const { Conflux, util } = require('js-conflux-sdk');
// 这个地址是上面打印出来的 receipt.contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x822ebe7eb36cdf159d6d544f6321e1a5c6619dc2';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425*';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812*';
const compiled = require(`./build/Test.json`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  let inc = await contract.getGrade();
  console.log("output:"  + inc.toString());
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  const account1 = cfx.Account(PRIVATE_KEY1);//运用私钥创立账户
  // 进行记载并花费CFX
  await contract.increment(1).sendTransaction({ from: account1 }).confirmed();
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call increment() the current drip:"+after_call_balance.toString());
  console.log("after account1 call increment() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  //创立account2,并测验调用合约希望开释account1的cfx
  const account2 = cfx.Account(PRIVATE_KEY2);//运用私钥创立账户
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call increment() the current drip:"+after_call_balance.toString());
  console.log("before account2 call increment() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.increment(2).sendTransaction({ from: account2 }).confirmed();
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call increment() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call increment() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

为了便利描述,将参加调用合约的账户用account1和account2进行表明,被调用的合约用contract进行表明,将其账户信息和对应的账户地址进行汇总 ,所列表格如下:

账户名 账户地址
account1 0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account2 0x1941E3137aDDf02514cBFeC292710463d41e8196
countract 0x822ebe7eb36cdf159d6d544f6321e1a5c6619dc2

为便利表明,后文运用上表中账户名指代各账户

在调用前,运用confluxscan查account1对应的CFX余额,发现余额为:1986.673099761952452902 CFX。

Conflux的存储抵押机制

(1)运用account1进行第一次合约调用:

因为account1调用的increment()函数修正了合约存储的记载占用了存储空间,且修正的数据被区块链记载,所以需求上交CFX作为押金

在程序发动后:首要显现account1的账户余额:1986.673099761952481801 CFX。

程序会运用account1调用contract.increment(1)向合约建议交互,该函数会对数组中的每个元素进行加1操作,调用完成后发现account1的账户余额会变为:1986.360599761952433413 CFX。

Conflux的存储抵押机制

也便是说通过account1与合约contract的这一次交互操作,其账户扣除了: 1986.673099761952481801−1986.360599761952433413=0.312500000000048388(CFX)1986.673099761952481801-1986.360599761952433413=0.312500000000048388(CFX)

这阐明通过调用contract.increment(1)与合约进行交互对数据进行修正并将日志写入区块链。account1上交了0.312500000000048388的CFX。

运用account2调用合约,以协助account1开释空间

程序会持续运转,并运用account2通过调用contract.increment(2)向合约建议交互

在调用前,account1的账户余额为:1986.360599761952433413 CFX,与上一操作结束时account1的账户余额保持一致。

account2调用合约后,account1的CFX余额变为1986.673099761952433413 CFX

Conflux的存储抵押机制

也便是说,通过account2对合约的调用后,account1的账户CFX余额变化为: 1986.360599761952433413−1986.673099761952433413=−0.3125(CFX)1986.360599761952433413-1986.673099761952433413=-0.3125(CFX)

这意味着,因为account1调用increment函数改动并占用的320Bytes巨细的合约空间被开释,0.3125 CFX会被交还到了account1的账户中。依照过程(1)上钩算得到的付款额:0.06250000000002695 CFX,咱们可以估测,account1调用contract.increment(1)实践所耗费的费用为: 0.312500000000048388−0.3125=0.000000000000048388(CFX)0.312500000000048388-0.3125=0.000000000000048388(CFX)

程序中account1调用合约前,程序显现的account1的CFX余额为:1986.673099761952481801 CFX。

程序中account2调用合约后,account1账户的CFX余额为:1986.673099761952433413 CFX。

也便是说通过这次与合约的交互操作,account1账户扣除了0.000000000000048388个CFX,其计算公式如下: 1986.673099761952481801−1986.673099761952433413=0.000000000000048388(CFX)1986.673099761952481801-1986.673099761952433413=0.000000000000048388(CFX)

此刻咱们再去confluxscan处检查账户account1对应的余额为:1986.673099761952404514 CFX

Conflux的存储抵押机制
w=559&h=159&f=png&s=23450)

依照计算公式: 1986.673099761952452902−1986.673099761952404514=0.000000000000048388(CFX)1986.673099761952452902-1986.673099761952404514=0.000000000000048388 (CFX) 这一起也佐证了:account1调用contract.incement(1)与合约进行交互时,其账户实践耗费了0.000000000000048388 CFX

3. 一个内含 stringuint (且在调用时修正uint)的struct样例

合约代码如下:

pragma solidity ^0.5.0;
contract Struct_test {
    struct Animal {
        string name;
        uint age;
    }
	event SelfEvent(address indexed sender,uint current);
	event SelfEvent_string(address indexed sender,string current);
	Animal animal1 = Animal("英短",5);
	Animal animal2 = Animal("美短",5);
    function getAnimal(uint inc) public{
		animal1.age+=inc;
		animal2.age-=inc;
    }
	function get() public view returns(uint256){
        return animal1.age;
    }
	function self() public {
		emit SelfEvent(msg.sender, animal1.age);
        emit SelfEvent(msg.sender, animal2.age);
		emit SelfEvent_string(msg.sender, animal1.name);
		emit SelfEvent_string(msg.sender, animal2.name);
    }	
}

因为该智能合约中有一个包含 string name;uint age; 的结构体变量 Animal ,其中string对应变长数组,对应条目数依据实践设置的内容为准,而uint对应于1个条目。在合约中调用 getAnimal() 函数会修正实例化的animal1和animal2的age变量,改动的条目数为2,因而依照之前的公式剖析: CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数(116)(CFX)CFS()≡CFS(;)≡账户a在国际状况下具有的存储条目总数\left(\frac{1}{16}\right)(CFX)

正是因为 animal1.age+=inc;animal2.age-=inc; 调用改动了2个64B长度的条目,并将改动记载进区块链,结合之前给定的实例进行剖析,质押的数额应当是改动的结构体实例元素内的age变量,正好对应了2个64B长度的条目,因为没有改动name,结合之前咱们给定的实例剖析,质押的数额应当为:0.125 CFX (116)2=0.125(CFX)\left(\frac{1}{16}\right)2=0.125(CFX)

调用合约的代码如下所示:

const { Conflux, util } = require('js-conflux-sdk');
// 这个地址是上面打印出来的 receipt.contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x84dd09cd48e07426c4ac50a389930c034be6c82a';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425*';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812*';
const compiled = require(`./build/Struct_test`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  const account1 = cfx.Account(PRIVATE_KEY1);//运用私钥创立账户
  // 进行记载并花费CFX
  await contract.getAnimal(3).sendTransaction({ from: account1 }).confirmed();
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call getAnimal() the current drip:"+after_call_balance.toString());
  console.log("after account1 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  //创立account2,并测验调用合约希望开释account1的cfx
  const account2 = cfx.Account(PRIVATE_KEY2);//运用私钥创立账户
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call getAnimal() the current drip:"+after_call_balance.toString());
  console.log("before account2 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.getAnimal(3).sendTransaction({ from: account2 }).confirmed();
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call getAnimal() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

为了便利描述,将参加调用合约的账户用account1和account2进行表明,被调用的合约用contract进行表明,将其账户信息和对应的账户地址进行汇总 ,所列表格如下:

账户名 账户地址
account1 0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account2 0x1941E3137aDDf02514cBFeC292710463d41e8196
countract 0x84dd09cd48e07426c4ac50a389930c034be6c82a

为便利表明,后文运用上表中账户名指代各账户

在调用前,运用confluxscan查account1对应的CFX余额,发现余额为:1983.472904449450987608 CFX。

Conflux的存储抵押机制

(1)运用account1进行第一次合约调用:

因为account1调用的getAnimal()函数修正了合约存储的记载占用了存储空间,且修正的数据被区块链记载,所以需求上交CFX作为押金

在程序发动后:首要显现account1的账户余额:1983.472904449451016507 CFX。

程序会运用account1调用contract.getAnimal(3)向合约建议交互,该函数会对animal1.age进行加3操作,对animal2.age进行减3操作,调用完成后发现account1的账户余额会变为:1983.347904449450984359 CFX。

Conflux的存储抵押机制

也便是说通过account1与合约contract的这一次交互操作,其账户扣除了: 1983.472904449451016507−1983.347904449450984359=0.125000000000032148(CFX)1983.472904449451016507-1983.347904449450984359=0.125000000000032148(CFX)

这阐明通过调用contract.getAnimal(3)与合约进行交互对数据进行修正并将日志写入区块链。account1上交了额度为0.125000000000032148的CFX。

运用account2调用合约,以协助account1开释空间

调用合约的程序会持续运转,并运用account2通过调用contract.getAnimal(3)向合约建议交互

在调用前,account1的账户余额为:1983.347904449450984359 CFX,与上一操作结束时account1的账户余额保持一致。

account2调用合约后,account1的CFX余额变为1983.472904449450984359 CFX

Conflux的存储抵押机制

也便是说,通过account2对合约的调用后,account1的账户CFX余额变化为: 1983.347904449450984359−1983.472904449450984359=−0.125(CFX)1983.347904449450984359-1983.472904449450984359=-0.125(CFX)

这意味着,因为account1调用getAnimal()函数改动并占用的128Bytes巨细的合约空间被account2对合约的调用所开释,0.125 CFX会被交还到了account1的账户中。依照过程(1)上钩算得到的付款额:0.125000000000032148 CFX,咱们可以估测,account1调用contract.getAnimal(3)实践所耗费的费用为: 0.125000000000032148−0.125=0.000000000000032148(CFX)0.125000000000032148-0.125=0.000000000000032148(CFX)

程序中account1调用合约前,程序显现的account1的CFX余额为:1983.472904449451016507 CFX。

程序中account2调用合约后,account1账户的CFX余额为:1983.472904449450984359 CFX。

也便是说通过这次与合约的交互操作,account1账户扣除了0.000000000000032148个CFX,其计算公式如下: 1986.673099761952481801−1986.673099761952433413=0.000000000000032148(CFX)1986.673099761952481801-1986.673099761952433413=0.000000000000032148(CFX)

此刻咱们再去confluxscan处检查账户account1对应的余额为:1983.47290444945095546 CFX

Conflux的存储抵押机制

依照计算公式: 1983.472904449450987608−1983.47290444945095546=0.000000000000032148(CFX)1983.472904449450987608-1983.47290444945095546=0.000000000000032148 (CFX) 这一起也佐证了:account1调用contract.getAnimal(3)与合约进行交互时,其账户实践耗费了0.000000000000032148 CFX

4. 一个含有mapping的比如

合约代码如下:

pragma solidity ^0.5.0;
contract mapping_test {
	mapping(address => uint) public balances;
	event SelfEvent(address indexed sender,uint current);
    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
	function self() public {
		emit SelfEvent(msg.sender, balances[msg.sender]);
    }	
}

这段合约中运用到了 mapping 结构,因为该智能合约中有一个包含 mapping(address => uint) balance; ,而且调用相关函数时,会更新 address 对应条意图数据,调用改动了1个64B长度的条目,并将改动记载进区块链,结合之前给定的实例进行剖析,应当质押了0.0625个CFX。

调用合约代码如下:

const { Conflux, util } = require('js-conflux-sdk');
// 这个地址是上面打印出来的 receipt.contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x8433f943dd6a4cbf13209b9e8674c08349872ce8';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb977';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c';
const compiled = require(`./build/mapping_test`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  const account1 = cfx.Account(PRIVATE_KEY1);//运用私钥创立账户
  // 进行记载并花费CFX
  await contract.update(3).sendTransaction({ from: account1 }).confirmed();
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call update() the current drip:"+after_call_balance.toString());
  console.log("after account1 call update() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  //创立account2,并测验调用合约希望开释account1的cfx
  const account2 = cfx.Account(PRIVATE_KEY2);//运用私钥创立账户
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call update() the current drip:"+after_call_balance.toString());
  console.log("before account2 call update() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.update(5).sendTransaction({ from: account2 }).confirmed();
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call update() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call update() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

为了便利描述,将参加调用合约的账户用account1进行表明,被调用的合约用contract进行表明,将其账户信息和对应的账户地址进行汇总 ,所列表格如下:

账户名 账户地址
account1 0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account2 0x1941E3137aDDf02514cBFeC292710463d41e8196
countract 0x8433f943dd6a4cbf13209b9e8674c08349872ce8

为便利表明,后文运用上表中账户名指代各账户

调用时的输出状况如下所示:

Conflux的存储抵押机制

在程序发动后:首要显现account1的账户余额:98.955078124999570464 CFX。

程序会运用account1调用contract.update(5)向合约建议交互,该函数会对balances[msg.sender]进行设置新值的操作,调用完成后发现account1的账户余额会变为:98.892578124999543647 CFX。

也便是说通过account1与合约contract的这一次交互操作,其账户扣除了: 98.955078124999570464−98.892578124999543647=0.062500000000026817(CFX)98.955078124999570464-98.892578124999543647=0.062500000000026817(CFX)

因为存储典当的条目为1,所以质押0.0625 CFX是正确的,而调用该合约花费了0.000000000000026817 (CFX)


相关材料库:

  • Conflux开发材料包
  • conflux-chain github
  • conflux-fans github