精华内容
下载资源
问答
  • web3-智能合约概述 | PHP实现ETH 4
    千次阅读
    2022-02-26 19:06:23

    智能合约概述

    智能合约就是区块链上运行的软件,它常常被类比为「自动贩卖机」,因为大家认为这样比较容易理解: 自动贩卖机能接受并执行外部的指令。当顾客选定商品并付款后, 自动贩卖机将释放商品给顾客, 并不需要额外的人工介入:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOCdkEor-1650265504516)(https://www.li6.cc/assets/img/loading2.gif)]

    智能合约的概念最早由电脑科学家、密码学家Nick Szabo在1994年提出, 不过当时并没有合适的环境实现。由于区块链上的交易具有可追溯、抗篡改、不可逆转的特性, 使智能合约在没有第三方中间人的情况下,也可以进行安全的交易,这才使得自动化执行的智能合约得以落地。

    而以太坊由于内置了虚拟机和开发语言,这使得在以太坊区块链上开发智能合约的效率大大提高、难度 大大降低。因此,现在提到智能合约,基本上大家说的都是以太坊上的智能合约。

    在这一部分的课程中,我们将学习以下内容:

    使用solidity开发ERC20代币合约 使用命令行工具编译solidity智能合约 编写部署合约的php代码 在php代码中与智能合约交互


    智能合约的开发与交互

    学习ERC20代币智能合约的设计并使用solidity开发语言实现,然后使用php 进行部署与交互。

    在运行预置代码之前,请首先在1#终端启动节点仿真器:

    ~$ ganache-cli

    编译合约 执行以下命令:

    ~/repo/chapter5$ ./build-contract.sh

    部署合约 执行php脚本:

    ~/repo/chapter5$ php deploy-contract.php

    访问合约 执行php脚本:

    ~/repo/chapter5$ php access-contract.php

    ERC20代币规范

    目前几乎所有用于ICO筹集资金的代币,都是基于同样的技术:以太坊ERC-20标准,这些 代币实际上就是实现了ERC20标准的智能合约。

    一个ERC20代币合约应当实现如下标准的接口,当然你也可以根据自己的实际需要 补充额外的接口:

    contract ERC20 {
       function totalSupply() constant returns (uint theTotalSupply);
       function balanceOf(address _owner) constant returns (uint balance);
       function transfer(address _to, uint _value) returns (bool success);
       function transferFrom(address _from, address _to, uint _value) returns (bool success);
       function approve(address _spender, uint _value) returns (bool success);
       function allowance(address _owner, address _spender) constant returns (uint remaining);
       event Transfer(address indexed _from, address indexed _to, uint _value);
       event Approval(address indexed _owner, address indexed _spender, uint _value);
    }
    

    totalSupply()

    该函数应当返回流通中的代币供给总量。比如你准备为自己的网站发行100万个代币。

    balanceOf()

    该函数应当返回指定账户地址的代币余额。

    approve()

    使用该函数进行授权,被授权的账户可以调用账户的名义进行转账。

    transfer()

    该函数让调用账户进行转账操作,将指定数量的代币发送到另一个账户。

    transferFrom()

    该函数允许第三方进行转账操作,转出账户必须之前已经调用approve()方法授权给调用账户。

    Transfer事件

    每次转账成功后都必须触发该事件,参数为:转出账户、转入账户和转账代币数量。

    Approval事件

    每次进行授权,都必须触发该事件。参数为:授权人、被授权人和授权额度。

    除了以上必须实现的接口,ERC20还约定了几个可选的状态,以便钱包或 其他应用可以更好的标识代币:

    name : 代币名称。例如:HAPPY COIN。 symbol : 代币符号。例如:HAPY ,在钱包或交易所展示这个名字。 decimals :小数位数。默认值为18。钱包应用会使用这个参数。例如 假设你的代币小数位数设置为2,那么1002个代币在钱包里就显示为10.02了。

    代币合约状态设计

    智能合约的设计核心是状态的设计,然后再围绕状态设计相应的操作。代币合约也不例外。

    首先我们需要有一个状态来记录代币发行总量,通常这个总量在合约部署的时候就固定下来了,不过你也可以定义额外的非标接口来操作这个状态,例如增发:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z8VibJ9s-1650265504518)(https://www.li6.cc/assets/img/loading2.gif)]

    在solidity中,我们可以使用一个uint256类型的变量来记录发行总量:

    uint256 totalSupply;

    嗯,多多益善。不过没有比uint256更大的类型了。

    接下来我们还需要有一个状态来保存所有账户的代币余额: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wBhwmGox-1650265504518)(https://www.li6.cc/assets/img/loading2.gif)]

    在solidity中,可以使用一个从账户地址对应于一个整数(表示余额)的映射表来表示这个状态:

    mapping(address => uint256) balances;

    最后一个重要的状态是授权关系,我们需要记录三个信息:授权账户、被授权账户和授权额度: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7DO7StHQ-1650265504518)(https://www.li6.cc/assets/img/loading2.gif)]

    显然,这需要一个嵌套的映射表:

    mapping (address => mapping (address => uint256)) public allowed;

    至于代币名称、代币符号和小数点位数,就简单的使用string和uint8类型的变量吧:

    string public name;             
    string public symbol;
    

    阅读教程,回答以下问题: transfer()会修改哪些状态? transferFrom()会使用那些状态,又会修改哪些状态? approve()会修改哪些状态? uint8 public decimals;


    代币合约方法实现

    (demo:repo\chapter5\contract\EzToken.sol)

    定义好了核心状态,ERC20规定的接口实现起来非常简单。不过在实现这些接口之前,我们 先看一下构造函数:

    constructor(
        uint256 _initialAmount,
        string _tokenName,
        uint8 _decimalUnits,
        string _tokenSymbol
    ) public {
        balances[msg.sender] = _initialAmount;               
        totalSupply = _initialAmount;                        
        name = _tokenName;                                   
        decimals = _decimalUnits;                            
        symbol = _tokenSymbol;                               
    }
    

    嗯,很简单,就是保存一下传入四个参数:初识发行总量、代币名称、小数点位数和代币符号。 在上面的实现中,部署合约的账户在开始时将持有所有的代币。

    你可以根据自己的需要调整构造函数的参数和实现逻辑。

    transfer(to,value)

    容易理解,transfer()函数操作的状态就是balances。实现逻辑很直白,代码如下:

    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value);
        balances[msg.sender] -= _value;
        balances[_to] += _value;
        emit Transfer(msg.sender, _to, _value); 
        return true;
    }
    

    由于transfer()是从调用账户转出代币,因此首先需要检查调用账户的代币余额是否足够。 接下来就可以分别调整双方的账户余额,然后触发Transfer事件即可。

    approve(spender,value)

    approve()函数操作的状态是allowed。实现逻辑同样直白,上代码:

    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value); 
        return true;
    }
    

    修改allowed映射表之后,触发Approval事件即可。

    transferFrom(from,to,value)

    transferFrom()方法的逻辑相对复杂一点,它需要同时操作balances状态和allowed状态:

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        uint256 allowance = allowed[_from][msg.sender];
        require(balances[_from] >= _value && allowance >= _value);
        balances[_to] += _value;
        balances[_from] -= _value;
        if (allowance < MAX_UINT256) {
            allowed[_from][msg.sender] -= _value;
        }
        emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
        return true;
    }
    

    代码首先查看allowed状态来确定调用账户是否得到转出账户的授权 以及 授权额度是否足够本次转账。然后还要检查转出账户的余额是否足够本次转账。这些条件满足以后,直接调整 balances状态中转出账户和转入账户的余额,同时调整allowed状态中响应的授权额度。最后触发Transfer事件即可。

    balanceOf(owner)

    balanceOf()方法只是查询balances状态,因此它是一个不消耗gas的view函数:

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
    }
    allowance(owner,spender)
    

    allowance()方法查询账户对的授权额度,显然,它也是一个不修改状态的view函数:

    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }
    

    参考教程,编写代币合约EzToken.sol,实现ERC20规范。


    编译代币合约

    为了在Php代码中与合约交互,我们需要先编译solidity编写的合约,以便得到 EVM字节码和二进制应用接口(ABI)。

    字节码是最终运行在以太坊虚拟机中的代码,看起来就是这样:

    608060405234801561001057600080fd5b5060405…

    嗯,就是16进制码流,每两个字符表示1个字节,就像PC里的机器码,以太坊的字节码对应着以太坊虚拟机的操作码 ——— 字节码就是最终在以太坊的EVM上运行的合约代码,我们在部署合约的时候需要用到它。

    而ABI则是描述合约接口的一个JSON对象,用来在其他开发语言中调用合约。ABI 描述了合约中的每个方法、状态与事件的语言特性。例如,对于代币合约中的 transfer()方法,在ABI中描述如下: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-siJ1f96g-1650265504519)(https://www.li6.cc/assets/img/loading2.gif)]

    ABI提供的丰富信息是实现其他语言绑定的关键资料。任何时候与合约进行交互, 都需要持有合约的ABI信息。

    solidity的官方编译器solc是一个命令行程序,根据运行选项的不同会有 不一样的行为。例如,下面的使用–bin和–abi选项要求solc编译器同时 生成字节码文件和ABI文件,并输出到build目录:

    ~$ mkdir -p contract/build
    ~$ solc contract/EzToken.sol --bin --abi \\
                       --optimize --overwrite \\
                       -o contract/build/
    

    编译成功后,将在build目录中得到两个文件,这是我们下一步工作的基础:

    ~$ ls contract/build
    EzToken.abi EzToken.bin
    

    参考教程: 使用solc编译合约EzToken.sol 查看生成的ABI文件,找到balanceOf()函数对应的描述,与其solidity声明 对照查看,理解ABI中的信息涵盖范围。


    部署代币合约

    (demo: \repo\chapter5\deploy-contract.php)

    有了合约的字节码和ABI,就可以使用Web3\Contract类来部署合约到链上了。

    首先载入ABI 和 字节码:

    $abi = file_get_contents('contract/build/EzToken.abi');
    $bytecode = '0x' . file_get_contents('contract/build/EzToken.bin');
    

    然后创建Web3\Contract实例,需要传入Web3\Provider对象以及合约的 ABI信息,然后调用bytecode()方法设置字节码:

    $contract = new Web3\\Contract($web3->provider,$abi);
    $contract->bytecode($bytecode);
    

    为什么不在构造函数中直接传入字节码?

    这是因为只有在部署的时候,才需要使用字节码。一旦合约部署完成,只需要合约的ABI和部署地址就可以与合约交互了。

    一切就绪,现在只需要调用合约对象的new()方法即可部署。例如,下面的代码完成了1000万枚幸福币的发行:

    $cb = new Callback();
    $opts = [
      'from' => $accounts[0],
      'gas' => '0x200b20'  //2100000
    ];
    $contract->new(10000000,'HAPPY TOKEN',0,'HAPY',$opts,$cb);
    $txhash = $cb->result;
    

    容易理解,部署时会依次传入合约的构造函数声明的各参数值,也就是说,只有在 部署合约的时候,才会执行合约的构造函数。此外,由于部署合约是一个交易,因此 我们需要声明部署账户和gas用量。

    由于部署账户是节点旳第1个账户,该账户将持有初始发行的全部代币。

    接下来要等待交易收据,因为在收据中有合约部署的具体地址:

    $timeout = 60;
    $interval = 1;
    $t0 = time();
    while(true){
      $this->eth->getTransactionReceipt($txhash,$cb);        
      if(isset($cb->result)) break;
      $t1 = time();
      if(($t1 - $t0) > $timeout) break;
      sleep($interval);  
    }
    

    如果拿到收据,那就算部署成功了。但是如果你希望接着马上与这个新部署的合约对象交互,还需要设置其地址:

    $contract->at($receipt->contractAddress);
    

    最重要的,把地址抄下来,或者记录到一个文件中,否则你接下来没法访问合约了。

    file_put_contents('./contract/build/EzToken.addr',$receipt->contractAddress);
    

    参考教程,编写php脚本实现以下功能: 使用节点第1个账户部署代币合约,发行100万枚幸福币,代号HAPY 在控制台输出合约的部署地址 将合约的部署地址保存到文件中

    访问代币合约

    (demo:repo\chapter5\access-contract.php)

    要访问一个已经部署在链上的合约,只需要它的ABI和部署地址。

    同样,首先载入ABI和之前保存的地址:

    $abi = file_get_contents('./contract/build/EzToken.abi');
    $addr = file_get_contents('./contract/build/EzToken.addr');
    

    然后构建Contract对象,设置其部署地址:

    $contract = new Web3\\Contract($web3->provider,$abi);
    $contract->at($addr);
    

    接下来就可以调用合约的方法了。这分两种情况。

    如果是那些修改合约状态的交易函数,比如transfer(), 使用合约对象的send()方法,例如,向第2个节点账户转一些代币:

    $opts = [
      'from' => $accounts[0],
      'gas' => '0x200b20'
    ];
    $contract->send('transfer',$accounts[1],$opts,$cb);
    echo 'tx hash: ' . $cb->result . PHP_EOL;
    

    交易函数返回的总是交易收据。

    注意,因为我们使用第1个节点账户部署的合约,因此现在它持有全部的代币。

    如果是要调用合约中的只读函数,例如balanceOf(),那么使用合约对象的call() 方法。例如,获取第1个节点账户的代币余额:

    $opts = []; //不需要消耗gas
    $contract->call('balanceOf',$accounts[0],$opts,$cb);
    $balance = $cb->result['balance']->toString();
    echo 'balance' . $balance . PHP_EOL;
    

    注意,由于solidity支持函数返回多个值,因此call()方法的返回结果总是一个关联 数组,各键的名称与所调用合约方法在ABI中的outputs部分的名称对应。

    参考教程与示例代码, 编写php实现以下功能: 向节点第2个账户转账100个代币


    通知机制概述

    通知机制对任何应用开发都很重要,因为它提供了另外一个方向的变化 通知能力。以太坊也不例外,它的通知机制增强了智能合约与外部应用之间 的沟通能力。

    以太坊的通知机制是建立在日志基础之上。例如,如果智能合约触发了一个 事件,那么该事件将写入以太坊日志;如果外部应用订阅了这个事件,那么当日志中出现该事件后此外部应用就可以拉到,如图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hD9Um7jo-1650265504519)(https://www.li6.cc/assets/img/loading2.gif)]

    需要指出的是,以太坊的通知机制不是推(Push)模式,而是需要外部应用周期性轮询的拉(Pull)模式。外部应用通过在节点中创建过滤器来订阅感兴趣的日志,之后则通过检测该过滤器的变化获得最新的日志。

    在这一部分的课程中,我们将学习以下内容:

    使用块过滤器监听新块生成事件和新交易事件 使用待定交易过滤器监听待定交易事件 使用主题过滤器监听合约事件 解析合约事件产生的日志


    监听新块事件

    使用块过滤器来监听新块生成事件。其过程如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mATGFrC9-1650265504520)(https://www.li6.cc/assets/img/loading2.gif)]

    首先进行eth_newBlockFilter调用 来创建一个新块过滤器:

    $cb = new Callback();
    $web3->eth->newBlockFilter($cb); 
    $fid = $cb->result;
    

    然后调用eth_getFilterChanges进行周期性检查:

    while(true){
      $web3->eth->getFilterChanges($fid,$cb);
      $blocks = $cb->result;
      foreach($blocks as $hash) { 
          echo $hash . PHP_EOL;
      }
      sleep(2);
    }
    

    对于块过滤器,eth_getFilterChanges调用将返回块哈希值的数组。 如果你希望获取块的详细信息,可以使用eth_getBlockByHash调用。

    参考教程和示例代码,编写两个php脚本分别实现以下功能: 监听新块生成事件,并打印新块信息 从节点第1个账户向第2个账户转入1kwei。 在两个不同的终端分别运行两个脚本,观察监听脚本的输出。

    监听新交易事件

    要监听新的确认交易,也是使用块过滤器。其过程如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0zRcDZZ-1650265504520)(https://www.li6.cc/assets/img/loading2.gif)]

    实际上它的实现机制,就是在捕捉到新块事件后,进一步获取新块的详细信息。

    因此和监听新块事件一样,首先执行eth_newBlockFilter调用 来创建一个新块过滤器:

    $cb = new Callback();
    $web3->eth->newBlockFilter($cb);
    $fid = $cb->result;
    

    然后调用eth_getFilterChanges进行周期性检查。 对于该调用返回的每一个块哈希,进一步调用eth_getBlockByHash 来获取块的详细信息:

    while(true){
      $web3->eth->getFilterChanges($fid,$cb); $blocks = $cb->result;
      foreach($blocks as $hash) {
        $web3->eth->getBlockByHash($hash,true,$cb);  $block = $cb->result;
        foreach($block->transactions as $tx) var_dump($tx);
      }
      sleep(2);
    }
    

    eth_getBlockByHash调用的第二个参数用来声明是否需要返回完整的交易对象, 如果设置为false将仅返回交易的哈希。

    参考教程和示例代码,编写两个php脚本分别实现以下功能: 监听新交易生成事件,显示新交易信息 从节点第1个账户向第2个账户转入1gwei 在两个终端分别运行上面脚本,查看监听输出。

    监听待定交易事件

    待定交易指那些提交给节点但还未被网络确认的交易,因此它不会包含在区块中。 使用待定交易过滤器来监听新待定交易事件。其过程如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9A3hUdN-1650265504520)(https://www.li6.cc/assets/img/loading2.gif)]

    首先执行eth_newPendingTransactionFilter 调用来创建一个新待定交易过滤器:

    $cb = new Callback();
    $web3->eth->newPendingTransactionFilter($cb); $fid = $cb->result;
    

    然后同样是调用eth_getFilterChanges进行周期性检查。 例如,下面的代码将打印新出现的待定交易的信息:

    while(true){
      $web3->eth->getFilterChanges($cb); $ptxs = $cb->result;
      foreach($ptxs as $hash) echo $hash . PHP_EOL;
      sleep(2);
    }
    

    对于待定交易过滤器,eth_getFilterChanges调用返回的结果是自上次 调用之后新产生的一组待定交易的哈希。可以使用eth_getTransactionByHash 调用查看指定交易的详细信息。

    参考教程和示例代码,编写两个php脚本实现以下功能: 监听新待定交易事件,显示待定交易信息 从节点第1个账户向第2个账户转入1gwei 在两个终端分别运行上述脚本,查看监听输出。

    监听合约事件

    合约事件的监听是通过主题过滤器实现的,其过程如下:

    首先执行eth_newFilter调用 创建一个主题过滤器,获得该过滤器的编号:

    $cb = new Callback();
    $web3->eth->newFilter([],$cb);
    $fid = $cb->result;
    

    eth_newFilter调用可以接收一个选项参数,来过滤监听的日志类型。该选项 可以指定一些监听过滤条件,例如要监听的合约地址等。 不过在上面的代码中,我们使用一个空的关联数组,没有设置这些参数, 这意味着我们将监听全部日志。

    在创建主题过滤器之后,同样使用eth_getFilterChanges 调用进行周期性的检查,看是否有新的日志产生。该调用将返回自上次调用之后的所有新日志的数组:

    while(true){
      $web3->eth->getFilterChanges($cb);
      $logs = $cb->result;
      foreach($logs as $log) {
          var_dump($log);  
      }
      sleep(2);
    }
    

    每一个日志在php里被映射到一个StdClasst对象,但显然,捕捉到的日志还 需要进一步解码才可以得到事件的参数:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtG4IewL-1650265504521)(https://www.li6.cc/assets/img/loading2.gif)]

    参考教程,编写两个php脚本分别实现以下功能: 监听代币合约的transfer事件 每隔3秒从节点第1个账户向第3个账户转1个代币 在两个终端分别运行上述脚本,观察监听输出。

    使用主题过滤日志

    当我们创建主题过滤器时,以及查看日志数据时,都接触到了一个概念:主题。 以太坊利用主题来区别不同的事件。

    主题实际上就是事件的哈希签名。例如,对于代币合约的Transfer 事件,它的签名计算如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wL0ZO795-1650265504522)(https://www.li6.cc/assets/img/loading2.gif)]

    使用Web3\Contract\Ethabi类的encodeEventSignature()方法来 计算事件签名。我们可以从合约对象的ethabi属性得到一个Ethabi实例。 例如:

    $contract = new Contract($web3->provider,$abi);
    $ethabi = $contract->ethabi;
    $topic = $ethabi->encodeEventSignature('Transfer(address,address,)');
    echo 'topic: ' . $topic . PHP_EOL;
    

    由于在abi中已经记录了事件的参数信息,因此也可以直接传入abi信息:

    $topic = $ethabi->encodeEventSignature($abi->events['Transfer']);
    echo 'topic: ' . $topic . PHP_EOL;
    

    在创建主题过滤器时,就可以用这个主题来调整监听行为了:

    while(true){
      $contract->eth->getFilterChanges($fid,$cb);
      $logs = $cb->result;
    
      if(count($logs) >0) {    
         foreach($logs as $log)
         {
           if($log->topics[0] == $topic) 
           {
            var_dump($log);   
           } else {
            echo 'skip log ' . PHP_EOL;
           }
         }
      }
    
      sleep(2);
    }
    

    参考教程和示例代码,编写两个php脚本分别实现以下功能: 监听代币合约的Approval事件 分别触发Approval事件和Transfer事件 在两个终端分别运行以上脚本,查看监听输出是否与预期一致。

    更多相关内容
  • 写在前面Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解,如果你还不了解,建议你先看以太坊是什么欢迎订阅区块链技术专栏阅读更全面的分析文章。什么是错误处理错误处理是指在...

    这是Solidity教程系列文章第9篇介绍Solidity 错误处理。

    Solidity系列完整的文章列表请查看分类-Solidity。

    写在前面

    Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解,

    如果你还不了解,建议你先看以太坊是什么

    欢迎订阅区块链技术专栏阅读更全面的分析文章。

    什么是错误处理

    错误处理是指在程序发生错误时的处理方式,Solidity处理错误和我们常见的语言不一样,Solidity是通过回退状态的方式来处理错误。发生异常时会撤消当前调用(及其所有子调用)所改变的状态,同时给调用者返回一个错误标识。注意捕捉异常是不可能的,因此没有try ... catch...。

    为什么Solidity处理错误要这样设计呢?

    我们可以把区块链理解为是全球共享的分布式事务性数据库。全球共享意味着参与这个网络的每一个人都可以读写其中的记录。如果想修改这个数据库中的内容,就必须创建一个事务,事务意味着要做的修改(假如我们想同时修改两个值)只能被完全的应用或者一点都没有进行。

    学习过数据库的同学,应该理解事务的含义,如果你对事务一词不是很理解,建议你搜索一下“数据库事务“。

    Solidity错误处理就是要保证每次调用都是事务性的。

    如何处理

    Solidity提供了两个函数assert和require来进行条件检查,如果条件不满足则抛出异常。assert函数通常用来检查(测试)内部错误,而require函数来检查输入变量或合同状态变量是否满足条件以及验证调用外部合约返回值。

    另外,如果我们正确使用assert,有一个Solidity分析工具就可以帮我们分析出智能合约中的错误,帮助我们发现合约中有逻辑错误的bug。

    除了可以两个函数assert和require来进行条件检查,另外还有两种方式来触发异常:

    revert函数可以用来标记错误并回退当前调用

    使用throw关键字抛出异常(从0.4.13版本,throw关键字已被弃用,将来会被淘汰。)

    当子调用中发生异常时,异常会自动向上“冒泡”。 不过也有一些例外:send,和底层的函数调用call, delegatecall,callcode,当发生异常时,这些函数返回false。

    注意:在一个不存在的地址上调用底层的函数call,delegatecall,callcode 也会返回成功,所以我们在进行调用时,应该总是优先进行函数存在性检查。

    在下面通过一个示例来说明如何使用require来检查输入条件,以及assert用于内部错误检查:

    pragma solidity ^0.4.0;

    contract Sharer {

    function sendHalf(address addr) public payable returns (uint balance) {

    require(msg.value % 2 == 0); // 仅允许偶数

    uint balanceBeforeTransfer = this.balance;

    addr.transfer(msg.value / 2); // 如果失败,会抛出异常,下面的代码就不是执行

    assert(this.balance == balanceBeforeTransfer - msg.value / 2);

    return this.balance;

    }

    }

    我们实际运行下,看看异常是如何发生的:

    首先打开Remix,贴入代码,点击创建合约。如下图:

    1460000014228397?w=1816&h=610

    运行测试1:附加1wei (奇数)去调用sendHalf,这时会发生异常,如下图:

    1460000014228398?w=1990&h=1004

    运行测试2:附加2wei 去调用sendHalf,运行正常。

    运行测试3:附加2wei以及sendHalf参数为当前合约本身,在转账是发生异常,因为合约无法接收转账,错误提示上图类似。

    assert类型异常

    在下述场景中自动产生assert类型的异常:

    如果越界,或负的序号值访问数组,如i >= x.length 或 i < 0时访问x[i]

    如果序号越界,或负的序号值时访问一个定长的bytesN。

    被除数为0, 如5/0 或 23 % 0。

    对一个二进制移动一个负的值。如:5<

    整数进行可以显式转换为枚举时,如果将过大值,负值转为枚举类型则抛出异常

    如果调用未初始化内部函数类型的变量。

    如果调用assert的参数为false

    require类型异常

    在下述场景中自动产生require类型的异常:

    调用throw

    如果调用require的参数为false

    如果你通过消息调用一个函数,但在调用的过程中,并没有正确结束(gas不足,没有匹配到对应的函数,或被调用的函数出现异常)。底层操作如call,send,delegatecall或callcode除外,它们不会抛出异常,但它们会通过返回false来表示失败。

    如果在使用new创建一个新合约时出现第3条的原因没有正常完成。

    如果调用外部函数调用时,被调用的对象不包含代码。

    如果合约没有payable修饰符的public的函数在接收以太币时(包括构造函数,和回退函数)。

    如果合约通过一个public的getter函数(public getter funciton)接收以太币。

    如果.transfer()执行失败

    当发生require类型的异常时,Solidity会执行一个回退操作(指令0xfd)。

    当发生assert类型的异常时,Solidity会执行一个无效操作(指令0xfe)。

    在上述的两种情况下,EVM都会撤回所有的状态改变。是因为期望的结果没有发生,就没法继续安全执行。必须保证交易的原子性(一致性,要么全部执行,要么一点改变都没有,不能只改变一部分),所以需要撤销所有操作,让整个交易没有任何影响。

    注意assert类型的异常会消耗掉所有的gas, 而require从大都会版本(Metropolis, 即目前主网所在的版本)起不会消耗gas。

    参考视频

    我们也推出了目前市面上最全的视频教程:招募体验师,可以点击链接了解。

    参考文献

    ☛ 深入浅出区块链 - 系统学习区块链,打造最好的区块链技术博客。

    ☛ 我的知识星球为各位解答区块链技术问题,欢迎加入讨论。

    ☛ 关注公众号“深入浅出区块链技术”第一时间获取区块链技术信息。

    展开全文
  • 今天有人问如何用按钮调用智能合约,我不知道在哪里找,所以我决定写这个,很快就可以。image在这篇文章中,我将给出一个简单但有希望有效的演示,说明JavaScript开发人员如何创建一个能够调用智能合约的网页,并...

    今天有人问如何用按钮调用智能合约,我不知道在哪里找,所以我决定写这个,很快就可以。

    afc57384c59b?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

    image

    在这篇文章中,我将给出一个简单但有希望有效的演示,说明JavaScript开发人员如何创建一个能够调用智能合约的网页,并通过单击即可向其汇款(以太币)。

    要做到这一点,用户将需要使用支持Web3的浏览器,因此可以使用像Parity或Mist这样的可安装浏览器,或者他们可以使用像MetaMask这样的浏览器扩展。

    此外,虽然我可以教你如何直接使用web3API,但我将教你如何使用一个新的很方便的库与以太网智能合约EthJS交互。

    你的网站需要等待ready事件,然后检查全局web3对象。看起来像这样:

    window.addEventListener('load', function() {

    // Check if Web3 has been injected by the browser:

    if (typeof web3 !== 'undefined') {

    // You have a web3 browser! Continue below!

    startApp(web3);

    } else {

    // Warn the user that they need to get a web3 browser

    // Or install MetaMask, maybe with a nice graphic.

    }

    })

    afc57384c59b?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

    image

    在这个例子中,我假设你正在使用像Browserify或Webpack这样的JavaScript捆绑器,并且知道如何从NPM安装模块。

    在你的应用程序设置中,你将使用一些不同的ethjs模块,你将使用全局web3对象的currentProvider属性初始化它们,这就是它与区块链的对话方式。

    const Eth = require('ethjs-query')

    const EthContract = require('ethjs-contract')

    function startApp(web3) {

    const eth = new Eth(web3.currentProvider)

    const contract = new EthContract(eth)

    initContract(contract)

    }

    一旦实例化了合约,就可以使用它来创建对网络上实时合约的引用。要做到这一点,你需要两件事:

    合约地址。

    合约ABI。

    ABI是应用程序二进制接口,它告诉你的JavaScript如何与智能合约通信。它只是描述合约方法的JSON数据。

    通常,如果你发布合约,你知道如何获得ABI,如果你要与其他人签订合约,他们应该提供ABI,尽管有时候你可以找到与Etherscan等区块浏览器的合约相匹配的ABI。

    假设你有可用的ABI和地址,并了解我们现在如何创建合约对象。在这个例子中,我将使用仅包含Token标准中的transfer(to,value)方法的ABI:

    const abi = [{

    "constant": false,

    "inputs": [

    {

    "name": "_to",

    "type": "address"

    },

    {

    "name": "_value",

    "type": "uint256"

    }

    ],

    "name": "transfer",

    "outputs": [

    {

    "name": "success",

    "type": "bool"

    }

    ],

    "payable": false,

    "type": "function"

    }]

    const address = '0xdeadbeef123456789000000000000'

    function initContract (contract) {

    const MiniToken = contract(abi)

    const miniToken = MiniToken.at(address)

    listenForClicks(miniToken)

    }

    现在我们已经为智能合约初始化了一个JavaScript接口,所以我们只需要创建一个小HTML。

    Send Money!

    还有一点JavaScript来响应点击,并发送这些资金:

    function listenForClicks (miniToken) {

    var button = document.querySelector('button.transferFunds')

    button.addEventListener('click', function() {

    miniToken.transfer(toAddress, value, { from: addr })

    .then(function (txHash) {

    console.log('Transaction sent')

    console.dir(txHash)

    waitForTxToBeMined(txHash)

    })

    .catch(console.error)

    })

    }

    请注意,如果此交易也发送以太,你将向包含from字段的选项哈希添加value:'10000'。该值以wei为单位,为1x10 ^ -18以太。转换的简单方法是这样的:

    var inWei = web3.toWei('10', 'ether')

    对于普通的Web开发人员来说,一个奇怪的部分是交易响应并不意味着交易现在已经完成,它只是意味着它已经被传输到网络。它仍然需要被挖掘,而在以太坊中,平均需要大约14秒(阻断时间,查看EthStats.net上的统计数据)。

    现在还没有很好的订阅方法可以等待挖掘交易,所以你需要用收到的txHash进行轮询。是的,这很乏味,所以我会告诉你如何使用新的JavaScript async/await模式来减少痛苦:

    async function waitForTxToBeMined (txHash) {

    let txReceipt

    while (!txReceipt) {

    try {

    txReceipt = await eth.getTransactionReceipt(txHash)

    } catch (err) {

    return indicateFailure(err)

    }

    }

    indicateSuccess()

    }

    如此而已!(我知道,这很多)我希望这足以向你展示如何通过以太坊区块链与智能合约进行互动。一旦你习惯它,它就非常棒,因为它是一种具有权限的全局API,所以当你必须做一些有趣的事情,比如等待交易被挖掘,你不必做令人讨厌的事情,比如管理用户帐户和密码,或自己管理后端服务器!

    ======================================================================

    分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

    java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。

    python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。

    php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。

    以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。

    以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。

    C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。

    EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。

    java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。

    php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。

    tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

    汇智网原创翻译,转载请标明出处。这里是原文如何实现一键调用以太坊智能合约

    展开全文
  • 其中, Fabric 联盟链平台智能合约具有很好的代表性,本文主要分析其智能合约安全性,其他联盟链平台合约亦如此,除了 代码语言本身 的问题,也存在 系统机制安全 , 运行时安全 , 业务逻辑安全 等问题。 智能合约 ...

    25cbfd1225438aed283312c484f4c056.gif

    作者:极光@知道创宇404区块链安全研究团队
    时间:2020年8月27日

    前言

    随着区块链技术的发展,越来越多的个人及企业也开始关注区块链,而和区块链联系最为紧密的,恐怕就是金融行业了。然而虽然比特币区块链大受热捧,但毕竟比特币区块链是属于公有区块链,公有区块链有着其不可编辑,不可篡改的特点,这就使得公有链并不适合企业使用,毕竟如果某金融企业开发出一个区块链,无法受其主观控制,那对于它的意义就不大。因此私有链就应运而生,但私有链虽然能够解决以上的问题,如果仅仅只是各个企业自己单独建立,那么还将是一个个孤岛。如果能够联合起来开发私有区块链,最好不过,联盟链应运而生。 目前已经有了很多的联盟链,比较知名的有 Hyperledger 。超级账本(Hyperledger)是Linux基金会于2015年发起的推进区块链数字技术和交易验证的开源项目,加入成员包括:IBM、Digital Asset、荷兰银行(ABN AMRO)、埃森哲(Accenture)等十几个不同利益体,目标是让成员共同合作,共建开放平台,满足来自多个不同行业各种用户案例,并简化业务流程。 为了提升效率,支持更加友好的设计,各联盟链在智能合约上出现了不同的发展方向。其中, Fabric 联盟链平台智能合约具有很好的代表性,本文主要分析其智能合约安全性,其他联盟链平台合约亦如此,除了 代码语言本身 的问题,也存在 系统机制安全运行时安全业务逻辑安全 等问题。

    智能合约

    Fabric的智能合约称为链码(chaincode),分为系统链码和用户链码。系统链码用来实现系统层面的功能,用户链码实现用户的应用功能。链码被编译成一个独立的应用程序,运行于隔离的Docker容器中。和以太坊相比,Fabric链码和底层账本是分开的,升级链码时并不需要迁移账本数据到新链码当中,真正实现了逻辑与数据的分离,同时,链码采用Go、Java、Nodejs语言编写。 数据流向 Fabric链码通过gprc与peer节点交互 (1)当peer节点收到客户端请求的输入(propsal)后,会通过发送一个链码消息对象(带输入信息,调用者信息)给对应的链码。 (2)链码调用ChaincodeBase里面的invoke方法,通过发送获取数据(getState)和写入数据(putState)消息,向peer节点获取账本状态信息和发送预提交状态。 (3)链码发送最终输出结果给peer节点,节点对输入(propsal)和 输出(propsalreponse)进行背书签名,完成第一段签名提交。 (4)之后客户端收集所有peer节点的第一段提交信息,组装事务(transaction)并签名,发送事务到orderer节点排队,最终orderer产生区块,并发送到各个peer节点,把输入和输出落到账本上,完成第二段提交过程。 链码类型 •用户链码 由应用开发人员使用Go(Java/JS)语言编写基于区块链分布式账本的状态及处理逻辑,运行在链码容器中, 通过Fabric提供的接口与账本平台进行交互。 •系统链码 负责Fabric节点自身的处理逻辑, 包括系统配置、背书、校验等工作。系统链码仅支持Go语言, 在Peer节点启动时会自动完成注册和部署。 部署 可以通过官方  Fabric-samples  部署 test-network ,需要注意的是国内网络环境对于Go编译下载第三方依赖可能出现网络超时,可以参考 goproxy.cn[1] 解决,成功部署后如下图:

    36c5437a5aa14354cb308bd4bb693b0d.png

    语言特性问题

    不管使用什么语言对智能合约进行编程,都存在其对应的语言以及相关合约标准的安全性问题。Fabric 智能合约是以通用编程语言为基础,指定对应的智能合约模块。(如:Go/Java/Node.js) •不安全的随机数 随机数应用广泛,最为熟知的是在密码学中的应用,随机数产生的方式多种多样,例如在Go程序中可以使用 math/rand 获得一个随机数,此种随机数来源于伪随机数生成器,其输出的随机数值可以轻松预测。而在对安全性要求高的环境中,如 UUID 的生成,Token 生成,生成密钥、密文加盐处理。使用一个能产生可能预测数值的函数作为随机数据源,这种可以预测的数值会降低系统安全性。

    c5d89d9acb3e6e8de1b39469d1067a76.png

    伪随机数是用确定性的算法计算出来自[0,1]均匀分布的随机数序列。并不真正的随机,但具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,这里的“初值”就是随机种子,那么伪随机数的数序也不变。在上述代码中,通过对比两次执行结果都相同。

    4eed947dea6560e7cdbdf25b8f84f239.png

    通过分析rand.Intn()的源码,可见,在”math/rand” 包中,如果没有设置随机种子, Int() 函数自己初始化了一个 lockedSource 后产生伪随机数,并且初始化时随机种子被设置为1。因此不管重复执行多少次代码,每次随机种子都是固定值,输出的伪随机数数列也就固定了。所以如果能猜测到程序使用的初值(种子),那么就可以生成同一数序的伪随机数。
    fmt.Println(rand.Intn(100)) // fmt.Println(rand.Intn(100)) // fmt.Println(rand.Float64()) // 产生0.0-1.0的随机浮点数fmt.Println(rand.Float64()) // 产生0.0-1.0的随机浮点数jiguang@example$ go run unsafe_rand.go 81870.66456005321849040.4377141871869802jiguang@example$ go run unsafe_rand.go 81870.66456005321849040.4377141871869802jiguang@example$
    •不当的函数地址使用 错误的将函数地址当作函数、条件表达式、运算操作对象使用,甚至参与逻辑运算,将导致各种非预期的程序行为发生。比如在如下if语句,其中 func() 为程序中定义的一个函数:
    if (func == nil) {    ...}
    由于使用 func 而不是 func() ,也就是使用的是 func 的地址而不是函数的返回值,而函数的地址不等于 nil ,如果用函数地址与 nil 作比较时,将使其条件判断恒为 false•资源重释放 defer 关键字可以帮助开发者准确的释放资源,但是仅限于一个函数中。如果一个全局对象中存储了大量需要手动释放的资源,那么编写释放函数时就很容易漏掉一些释放函数,也有可能造成开发者在某些条件语句中提前进行资源释放。

    a766a6ecb072fa32a00f854826b9a80c.png

    •线程安全 很多时候,编译器会做一些神奇的优化,导致意想不到的数据冲突,所以,只要满足“同时有多个线程访问同一段内存,且其中至少有一个线程的操作是写操作”这一条件,就需要作并发安全方面的处理。

    cbf18a54ee51afc627b2b64a4461dfb7.png

    •内存分配 对于每一个开发者,内存是都需要小心使用的资源,内存管理不慎极容易出现的OOM(OutOfMemoryError),内存泄露最终会导致内存溢出,由于系统中的内存是有限的,如果过度占用资源而不及时释放,最后会导致内存不足,从而无法给所需要存储的数据提供足够的内存,从而导致内存溢出。导致内存溢出也可能是由于在给数据分配大小时没有根据实际要求分配,最后导致分配的内存无法满足数据的需求,从而导致内存溢出。
    var detailsID int = len(assetTransferInput.ID)assetAsBytes := make([]int, detailsID)
    如上代码, assetTransferInput.ID 为用户可控参数,如果传入该参数的值过大,则make内存分配可能导致内存溢出。 •冗余代码 有时候一段代码从功能上、甚至效率上来讲都没有问题,但从可读性和可维护性来讲,可优化的地方显而易见。特别是在需要消耗gas执行代码逻辑的合约中。
    if len(assetTransferInput.ID) < 0 {    return fmt.Errorf("assetID field must be a non-empty")}if len(assetTransferInput.ID) == 0 {    return fmt.Errorf("assetID field must be a non-empty")}

    运行时安全

    •整数溢出 不管使用的何种虚拟机执行合约,各类整数类型都存在对应的存储宽度,当试图保存超过该范围的数据时,有符号数就会发生整数溢出。 涉及无符号整数的计算不会产生溢出,而是当数值超过无符号整数的取值范围时会发生回绕。如:无符号整数的最大值加1会返回0,而无符号整数最小值减1则会返回该类型的最大值。当无符号整数回绕产生一个最大值时,如果数据用于如 []byte(string),string([]byte) 类的内存拷贝函数,则会复制一个巨大的数据,可能导致错误或者破坏堆栈。除此之外,无符号整数回绕最可能被利用的情况之一是用于内存的分配,如使用 make() 函数进行内存分配时,当 make() 函数的参数产生回绕时,可能为0或者是一个最大值,从而导致0长度的内存分配或者内存分配失败。

    ee090e6cb4d510d2191bcbe7cde40d67.png

    智能合约中GetAssetPrice函数用于返回当前计算的差价,第228可知, gas + rebate 可能发生溢出,uint16表示的最大整数为65535,即大于这个数将发生无符号回绕问题:
    var gas uint16 = uint16(65535)var rebate uint16 = uint16(1)fmt.Println(gas + rebate) // 0var gas1 uint16 = uint16(65535)var rebate2 uint16 = uint16(2)fmt.Println(gas1 + rebate2) // 1

    3005793f253ca525f65359f9de32f178.png

    •除数为零 代码基本算数运算过程中,当出现除数为零的错误时,通常会导致程序崩溃和拒绝服务漏洞。

    d57cacea386192a36adb6bebe26158d1.png

    CreateTypeAsset 函数的第64行,通过传入参数 appraisedValue 来计算接收资产类型值,实际上,当传入参数 appraisedValue 等于17时,将发生除零风险问题。

    444961997feb8268d7d4f874d08e3d23.png

    •忽略返回值 一些函数具有返回值且返回值用于判断函数执行的行为,如判断函数是否执行成功,因此需要对函数的返回值进行相应的判断,以  strconv.Atoi  函数为例,其原型为:  func Atoi(s string) (int, error) 如果函数执行成功,则返回第一个参数 int;如果发生错误,则返回 error,如果没有对函数返回值进行检测,那么当读取发生错误时,则可能因为忽略异常和错误情况导致允许攻击者引入意料之外的行为。

    84b0d49538c96e4c629a866e6df8e17f.png

    •空指针引用 指针在使用前需要进行健壮性检查,从而避免对空指针进行解引用操作。试图通过空指针对数据进行访问,会导致运行时错误。当程序试图解引用一个期望非空但是实际为空的指针时,会发生空指针解引用错误。对空指针的解引用会导致未定义的行为。在很多平台上,解引用空指针可能会导致程序异常终止或拒绝服务。如:在 Linux 系统中访问空指针会产生 Segmentation fault 的错误。
    func (s *AssetPrivateDetails) verifyAgreement(ctx contractapi.TransactionContextInterface, assetID string, owner string, buyerMSP string) *Asset {    ....    err = ctx.GetStub().PutPrivateData(assetCollection, transferAgreeKey,     []byte(clientID))    if err != nil {        fmt.Printf("failed to put asset bid: %v\n", err)        return nil    }}// Verify transfer details and transfer ownerasset := s.verifyAgreement(    ctx, assetTransferInput.ID, asset.Owner, assetTransferInput.BuyerMSP)var detailsID int = len(asset.ID)
    •越界访问 越界访问是代码语言中常见的缺陷,它并不一定会造成编译错误,在编译阶段很难发现这类问题,导致的后果也不确定。当出现越界时,由于无法得知被访问空间存储的内容,所以会产生不确定的行为,可能是程序崩溃、运算结果非预期。

    916634447a2c7aad3f059a872810a3bb.png

    43ce684088292a7c0c4a91fa01f817e8.png

    系统机制问题

    •全局变量唯一性 全局变量不会保存在数据库中,而是存储于单个节点,如果此类节点发生故障或重启时,可能会导致该全局变量值不再与其他节点保持一致,影响节点交易。因此,从数据库读取、写入或从合约返回的数据不应依赖于全局状态变量。

    9d1d7cf99bf03abe5212525f0d0ccbf6.png

    54a9e79d1a5676c49e31e29d729848b3.png

    •不确定性因素 合约变量的生成如果依赖于不确定因素(如:本节点时间戳)或者某个未在账本中持久化的变量,那么可能会因为各节点该变量的读写集不一样,导致交易验证不通过。

    b5244976c2606cad5de42053195b0927.png

    •访问外部资源 合约访问外部资源时,如第三方库,这些第三方库代码本身可能存在一些安全隐患。引入第三方库代码可能会暴露合约未预期的安全隐患,影响链码业务逻辑。

    业务逻辑安全

    •输入参数检查不到位 在编写智能合约时,开发者需要对每个函数参数进行合法性,预期性检查,即需要保证每个参数符合合约的实际应用场景,对输入参数检查不到位往往会导致非预期的结果。如近期爆出的 Filecoin测试网 代码中的严重漏洞,原因是  transfer  函数中对转账双方  from, to  地址检查不到位,导致了FIL无限增发。
    ### Beforefunc (vm *VM) transfer(from, to address.Address, amt types.BigInt) aerrors.ActorError {    if from == to {        return nil    }    ...}### Afterfunc (vm *VM) transfer(from, to address.Address, amt types.BigInt) aerrors.ActorError {    if from == to {        return nil    }    fromID, err := vm.cstate.LookupID(from)    if err != nil {        return aerrors.Fatalf("transfer failed when resolving sender address: %s", err)    }    toID, err := vm.cstate.LookupID(to)    if err != nil {        return aerrors.Fatalf("transfer failed when resolving receiver address: %s", err)    }    if fromID == toID {        return nil    }    ...}
    •函数权限失配 Fabrci智能合约go代码实现中是根据首字母的大小写来确定可以访问的权限。如果方法名首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用。因此,对于一些敏感操作的内部函数,应尽量保证方法名采用首字母小写开头,防止被外部恶意调用。 •异常处理问题 通常每个函数调用结束后会返回相应的返回参数,错误码,如果未认真检查错误码值而直接使用其返回参数,可能导致越界访问,空指针引用等安全隐患。 •外部合约调用引入安全隐患 在某些业务场景中,智能合约代码可能引入其他智能合约,这些未经安全检查的合约代码可能存在一些未预期的安全隐患,进而影响链码业务本身的逻辑。

    总结

    联盟链的发展目前还处于项目落地初期阶段,对于联盟链平台上的智能合约开发,项目方应该强化对智能合约开发者的安全培训,简化智能合约的设计,做到功能与安全的平衡,严格执行智能合约代码安全审计(自评/项目组review/三方审计) 在联盟链应用落地上,需要逐步推进,从简单到复杂,在项目开始阶段,需要设置适当的权限以防发生黑天鹅事件。

    REF

    [1] Hyperledger Fabric 链码 https://blog.51cto.com/clovemfong/2149953 [2] fabric-samples https://github.com/hyperledger/fabric-samples [3] Fabric2.0,使用test-network https://blog.csdn.net/zekdot/article/details/106977734 [4] 使用V8和Go实现的安全TypeScript运行时 https://php.ctolib.com/ry-deno.html [5] Hyperledger fabric https://github.com/hyperledger/fabric

    f6269cc20c61ae058a5ca4a9ec63ca66.gif 

    往 期 热 门

    (点击图片跳转)

    9d01af686c775d76b0e0af9e7040937d.png

    d66c4e8a2e37df888d046f13e9d6652f.png

    983b4a653a75627a68a7dfd4ee338ab4.png

    115b5b7913fc78e3ed8ecaaf9c59fe47.gif  

    cba053287ea528d2e7e1b844d3bb4129.png

     觉得不错点个“在看”哦699b347a415beefb316083fa171bcb94.png

    展开全文
  • 区块链学习笔记21——ETH智能合约

    千次阅读 2022-01-20 23:33:23
    区块链学习笔记21——ETH智能合约 学习视频:北京大学肖臻老师《区块链技术与应用》 笔记参考:北京大学肖臻老师《区块链技术与应用》公开课系列笔记——目录...Solidty是智能合约最常用的语言,其语法上与JavaScript
  • 在本文的这个以太坊智能合约教程中,我们将了解如何使用Truffle Ethereum和以太坊专用网络来执行智能合约。 我们将在以太坊智能合约教程中查看以下主题: 使用案例:保险流程中的智能合约 智能合约的好处 安装先决...
  • 以太坊系列之十六:golang进行智能合约开发 以太坊系列之十六: 使用golang与智能合约进行交互 以太坊系列之十六: 使用golang与智能合约进行交互 此例子的目录结构 token contract 智能合约的golang wrapper ......
  • 摘要:区块链联盟链智能合约形式化验证揭秘,解释了我们为什么要对区块链上的智能合约进行形式化验证,以及形式化验证的分类和业界针对每种分类所推出的形式化验证工具,最后作者描述了一下目前形式化验证的种种方法...
  • 四十种 智能合约 支持平台

    千次阅读 2020-01-02 21:02:21
    鉴于智能合约语言是“独立设计”的,因此更难以出错,并且语言的表达力完全可以用于“形式化验证”(参见下面给出的详细解释)。 智能合约语言:   F* 现状:  活跃。 说明: 与其它智能合约项目相比,Zen 协议...
  • 使用web3调用智能合约的简单例子

    千次阅读 2020-05-25 15:47:11
    当使用Solidity语言开发完成一个智能合约并部署后,如何在一个Web应用程序(或者手机App)中调用它?使用web3.js库,可以很容易的做到这些。通过一个最简单的例子我们来看看如何做到这些,分为三个步骤。 1. 编写...
  • 使用Go语言和以太坊智能合约交互

    千次阅读 2019-04-22 21:08:34
    尽管最近遇到了些麻烦,但以太坊仍然是区块链领域内智能合约的最大参与者,这似乎不会很快改变。 在我看来,技术本身具有很大的潜力,是从学术的角度看很有意思,但正如上面提到的问题和之前的许多问题是区块链技术...
  • 以太坊是备受关注的区块链,它基于密码...以太坊是第一个实现了虚拟机的区块链,因此为智能合约 - Smart Contract - 的运行提供了良好的支持环境。也正因为这个原因,以太坊被称为区块链 2.0,以区别于比特币代表的...
  • 智能合约中存在的3种最常见的误解

    万次阅读 2018-01-15 09:28:25
    作为一名受欢迎的区块链平台的开发者,我们有时被问到类似以太坊的智能合约是否走多链路线。我总是回答说:没有,至少目前还没有。 但智能合约在区块链充满炒作的世界里都可以风靡一时,为什么以前不行呢?那么问题...
  • 我们已经探索了很多主题,在编写智能合约时我们发现经常使用相同的模式:例如,智能合约具有在构造函数中设置的所有者,然后生成修改器以便仅让所有者使用一些功能。如果我们制定实施这些功能的基础合约并在未来的...
  • 就像“区块链”,“AI”和“云”这样的词语一样,“智能合约”也是那些得到大量炒作的短语之一。毕竟,没有什么比不通过司法系统而让人们能够相信发生了什么更有意思的了。智能合约的承诺包括: 自动,无须信任和...
  • 智能合约验证签名

    千次阅读 2018-04-15 20:22:41
    同时,智能合约编程语言solidity 也提供了签名和验证签名的操作:1、签名签名使用web3.eth.sign(),比如利用web3.js:var account = web3.eth.accounts[0]; var sha3Msg = web3.sha3("blockchain"); var signedData
  • 摘要: 智能合约是以数字形式定义的承诺,控制数字资产并涵盖合约参与者约定的权利和义务。它由计算机系统自动执行。在基于区块链的智能合约中,数据管理、事务验证和状态处理都是在区块链上完成的,区块链提供完备...
  • 背景最近开始做以太坊区块链开发工作。大多数开发工具,包括solidity语言都倾向JavaScript。当我开始使用Tunffle进行...所以如果我能选择使用py.test去测试我的智能合约话,我肯定会尝试一下。这时候我碰到了Popul...
  • 区块链(二、智能合约的开发)

    千次阅读 2021-04-21 21:19:13
    智能合约是按照用户的需求编写的代码,并部署和运行在以太坊虚拟机(EVM)上。智能合约在代码中限定了账户之间的交易规则,有利于通过原子化交易来实现数字资产的转移,也可以用于存储重要数据。智能合约就是由函数...
  • 本篇教程将带您完成一个DApp应用 - 宠物商店的收养追踪系统 在开始之前,本篇教程需要读者了解基本的以太坊...3.编写智能合约 4.编译和迁移智能合约 5.测试智能合约 6.创建与智能合约交互的用户界面 7.在浏览器...
  • 『区块链智能合约』从零构建Ethereum智能合约到实战开发 一、适合人群 iOS、Android、HTML5工程师 Node.js、PHP、Java…工程师 欲快速入门区块链智能合约开发的小伙伴们 二、课程目标 ...
  • 一起学:以太坊智能合约开发

    万次阅读 2018-07-03 02:45:10
    智能合约的开发语言 Solidity 是类似于 Javascript 的一门语言。同时,后面我们需要用到的 Truffle 快速开发框架也是基于 Javascript。所以,我们需要安装好 Node 环境。打开 Node 中文网 并下载相应的安装包...
  • 步步为营从智能合约到 DApp

    千次阅读 2018-07-03 02:45:08
    通过本系列课程的学习,将对智能合约有一个完整的认识,从零开始编写属于自己的智能合约,感受区块链技术带来的便利。 本课程分为四部分: 第一部分(第01课),智能合约市场概述; 第二部分(第02~05课),以太坊...
  • 在以太坊和其他区块链中,仍有很多被...在这篇文章中,我将介绍如何将python程序与以太坊智能合约集成。出于这样或那样的原因,可能也面临着这个问题,尽管以太坊提供了图灵完备语言,但并不是所有事情都能完成。...
  • 网络上不少部署智能合约的文章,但是都有一个共同的特点,就是采用命令行的方式来部署,先是建立SOLC的编译环境,然后部署Geth或者Eth节点,然后一步一步生成钱包、ABI、合约地址进行部署,对初学者来说晦涩难懂而且...
  • Nebulas是另一个可以开发智能合约的平台。它提供了一种使用JavaScript开发智能合约的方法,这是一种有趣的替代方案,可替代更为成熟的解决方案,例如以太坊。 我第一次在Reddit上宣读了Nebulas奖励计划,该计划奖励...
  • 接着上一个课程,创建通道后,就可以在这个通道上部署智能合约(也叫链码)了。 智能合约是介于区块链与业务系统之间,它是业务系统与区块链数据进行交互(设置,更新,获取数据)的工具。 正常情况下区块链开发...
  • 智能合约实施指南

    千次阅读 2018-09-25 10:30:08
    与区块链技术一样,智能合约在商业领域也非常有价值。 为了让我们的读者彻底了解智能合约是什么以及它们如何影响现代商业的交易方式,我们准备了本指南。 集中商业模式正在给去中心化的模式让路 传统的商业关系模型...
  • Tron区块链技术:多年来, 以太坊 一直是分散世界中开发智能合约的主流平台之一。然而,最近TRON作为一个准备面对以太坊的竞争平台在分散网络中崛起。TRON区块链技术是什么?Tron区块链是一个有前途的区块链平台,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,514
精华内容 605
关键字:

php智能合约语言