关于区块链:Conflux的存储抵押机制

背景介绍

近期有社区的小伙伴在开发智能合约并部署到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作为押金,其对应公式如下:

$$\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在世界状态σ下领有的存储条目总数×\left(\frac{1}{16}\right)(CFX)$$

特地的,对于一个由α=S(T)发送的交易T(或α=Tα如果T调用的是地址Tα处的资助合约),令σ为T执行前后的世界状态,σ是交易执行完结后的世界状态,针对存储限度字段Tl有一个CFS(α;σ)≤CFS(α;σ)+Tl/1018的断言。

# <table><tr><td bgcolor=yellow>关键点:想弄清楚调用合约质押的CFX有多少,肯定要弄清楚合约中变量的条目数,以及在通过函数调用合约进行操作时有多少条目被批改,且改变的数目被记录到了区块链中!</td></tr></table>

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)$$

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在世界状态σ下领有的存储条目总数×\left(\frac{1}{16}\right)(CFX)$$

正是因为 uint public count=0 变量正好对应于一个64B的条目,联合所以之前咱们给定的实例,真正质押的数额为: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。

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

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

在程序启动后:首先显示account1的账户余额:1994.680912261955383167 CFX。

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

也就是说通过account1与合约contract的这一次交互操作,其账户扣除了:
$$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

也就是说,通过account2对合约的调用后,account1的账户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)$$

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

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

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

这也间接佐证了,account1中数据存储所占用的合约空间就是1个64Bytes:
$$0.0625×16=1(个)‬$$

此时咱们再去confluxscan处查看账户account1对应的余额为:1994.680912261955327318 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在世界状态σ下领有的存储条目总数×\left(\frac{1}{16}\right)(CFX)$$

正是因为 uint [] arr = [1,2,3,4,5]; 数组内元素及数组长度正好对应了6个64B长度的条目,但因为数组长度没有扭转并写入区块链,联合之前咱们给定的实例剖析,质押的数额该当为: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。

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

因为account1调用的increment()函数批改了合约存储的记录占用了存储空间,且批改的数据被区块链记录,所以须要上交CFX作为押金

在程序启动后:首先显示account1的账户余额:1986.673099761952481801 CFX。

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

也就是说通过account1与合约contract的这一次交互操作,其账户扣除了:
$$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

也就是说,通过account2对合约的调用后,account1的账户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)$$

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

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

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

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

w=559&h=159&f=png&s=23450)

依照计算公式:
$$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在世界状态σ下领有的存储条目总数×\left(\frac{1}{16}\right)(CFX)$$

正是因为 animal1.age+=inc;animal2.age-=inc; 调用改变了2个64B长度的条目,并将扭转记录进区块链,联合之前给定的实例进行剖析,质押的数额该当是改变的构造体实例元素内的age变量,正好对应了2个64B长度的条目,因为没有改变name,联合之前咱们给定的实例剖析,质押的数额该当为: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。

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

因为account1调用的getAnimal()函数批改了合约存储的记录占用了存储空间,且批改的数据被区块链记录,所以须要上交CFX作为押金

在程序启动后:首先显示account1的账户余额:1983.472904449451016507 CFX。

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

也就是说通过account1与合约contract的这一次交互操作,其账户扣除了:
$$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

也就是说,通过account2对合约的调用后,account1的账户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)$$

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

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

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

此时咱们再去confluxscan处查看账户account1对应的余额为:1983.47290444945095546 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

为不便示意,后文应用上表中账户名指代各账户

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

在程序启动后:首先显示account1的账户余额:98.955078124999570464 CFX。

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

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

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理