前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第十二课 SOLIDITY语法难点解析及故障排查

第十二课 SOLIDITY语法难点解析及故障排查

作者头像
辉哥
发布2018-08-10 11:21:08
1.1K0
发布2018-08-10 11:21:08
举报
文章被收录于专栏:区块链入门
1.编辑器说明

(1)推荐编辑器 目前尝试 Solidity 编程的最好的方式是使用 Remix (需要时间加载,请耐心等待)。Remix 是一个基于 Web 的 IDE,它可以让你编写 Solidity 智能合约,然后部署并运行该智能合约。 如果外网不能访问,可以访问欧阳哥哥搭建的REMIX编辑器 (2)Visual Studio Extension Microsoft Visual Studio 的 Solidity 插件,包含 Solidity 编译器。 (3)Visual Studio Code extension Microsoft Visual Studio Code 插件,包含语法高亮和 Solidity 编译器。

2. REMIN的函数引用

function mint(address receiver, uint amount)

(1) 在REMIX输入时,地址一定要有""表示,否则amount就取不到值。 例如是mint("0xca35b7d915458ef540ade6068dfe2f44e8fa733c",101) (2) 程序中,如果注释包含中文,单步调试的位置不准确。

3.address相关全局函数

<address>.balance (uint256): 该地址有多少以太坊余额(wei为单位)

<address>.transfer(uint256 amount): 发送特定数量(wei为单位)的以太坊到对应地址,当出现错误时会扔出异常,但不会因异常而停止。固定需要耗费2300个gas。

<address>.send(uint256 amount) returns (bool): 发送特定数量(wei为单位)的以太坊到对应地址,当出现错误时会返回flase。固定需要耗费2300个gas。

【警告】send() 执行有一些风险:如果调用栈的深度超过1024或gas耗光,交易都会失败。因此,为了保证安全,必须检查send的返回值,如果交易失败,会回退以太币。如果用transfer会更好。

<address>.call(...) returns (bool): CALL的低级调用函数,当失败时返回false。执行需要消耗不固定的gas。 【说明】不鼓励使用call函数,后期将会被移除。调用该函数可能造成安全攻击,详见后期安全相关文章。

<address>.callcode(...) returns (bool): CALLCODE的低级调用函数,当失败时返回false。执行需要消耗不固定的gas。 不建议使用,后续版本会删除。

<address>.delegatecall(...) returns (bool): DELEGATECALL的低级调用函数,当失败时返回false。执行需要消耗不固定的gas。 【说明】为了和非ABI协议的合约进行交互,可以使用call() 函数, 它用来向另一个合约发送原始数据,支持任何类型任意数量的参数,每个参数会按规则(ABI协议)打包成32字节并一一拼接到一起。一个例外是:如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名而直接使用。如果仅想发送消息体,需要避免第一个参数是4个字节。如下面的例子:

function callfunc(address addr) returns (bool){ bytes4 methodId = bytes4(keccak256("setScore(uint256)")); return addr.call(methodId, 100); }

测试地址和地址调用代码举例

代码语言:javascript
复制
pragma solidity ^0.4.18;

contract AddrTest{
    /*event函数知识把参数信息打印在REMIX等编译器的LOG位置区,不需要函数定义。*/
    event logdata(bytes data);
    event LogContractAddress(address exAccount, address contractAddress);
    
    uint score = 0;
    
    /*回调函数,没有函数名。任何调用不存在的函数,这时被调用的合约的fallback函数会执行。
     payable:如果一个函数需要进行货币操作,必须要带上payable关键字*/
    function() payable {
        logdata(msg.data);
    }
    
    /*智能合约构建函数*/
    function AddrTest(){
    LogContractAddress(msg.sender,this);
    }

    function getBalance() returns (uint) {
        return this.balance;
    }


    function setScore(uint s) public {
        score = s;
    }

    function getScore() returns ( uint){
        return score;
    }
}

contract CallTest{
    /*该函数有函数申明没有实际函数内容,在remix的value区域设置以太坊的个数,调用该函数会把外部账户(ACCOUNT)中的
      以太坊转移到智能合约账户中*/
    function deposit() payable {
    }

    event logSendEvent(address to, uint value);
    event LogContractAddress(address exAccount, address contractAddress);
    
    
    /*转以太坊给目标地址*/
    function transferEther(address towho) payable {
        towho.transfer(10);/*单位为wei*/
        
        logSendEvent(towho, 10);
    }
   
    /*不指定调用函数,则调用无函数名的回调函数*/
    function callNoFunc(address addr) returns (bool){
        return addr.call("tinyxiong", 1234);
    }

    /*制定调用函数的方法*/
    function callfunc(address addr) returns (bool){
        bytes4 methodId = bytes4(keccak256("setScore(uint256)"));
        return addr.call(methodId, 100);
    }  

    /*返回当前合约的以太坊余额*/
    function getBalance() returns (uint) {
        return this.balance;//0
    }  
    
    /*销毁智能合约,把以太坊余额返回给当前外部账户*/
    function ContractSuide() {
        LogContractAddress(this,msg.sender);
        suicide(msg.sender);
    }
}

4.Contract Related

this (current contract’s type): 表示当前合约,可以显式的转换为Address selfdestruct(address recipient): destroy the current contract, sending its funds to the given Address 销毁当前合约,发送当前以太坊余额到给定的地址 suicide(address recipient): selfdestruct的别名函数

5. 区块和交易属性

block.blockhash(uint blockNumber) returns (bytes32): 给定区块的哈希—仅对最近的 256 个区块有效而不包括当前区块 block.coinbase (address): 挖出当前区块的矿工地址 block.difficulty (uint): 当前区块难度 block.gaslimit (uint): 当前区块 gas 限额 block.number (uint): 当前区块号 block.timestamp (uint): 自 unix epoch 起始当前区块以秒计的时间戳 msg.data (bytes): 完整的 calldata msg.gas (uint): 剩余 gas msg.sender (address): 消息发送者(当前调用) msg.sig (bytes4): calldata 的前 4 字节(也就是函数标识符) msg.value (uint): 随消息发送的 wei 的数量 now (uint): 目前区块时间戳(block.timestamp) tx.gasprice (uint): 交易的 gas 价格 tx.origin (address): 交易发起者(完全的调用链)

注解 对于每一个外部函数调用,包括 msg.sender 和 msg.value 在内所有 msg 成员的值都会变化。这里包括对库函数的调用。

注解 不要依赖 block.timestamp、 now 和 block.blockhash 产生随机数,除非你知道自己在做什么。 时间戳和区块哈希在一定程度上都可能受到挖矿矿工影响。例如,挖矿社区中的恶意矿工可以用某个给定的哈希来运行赌场合约的 payout 函数,而如果他们没收到钱,还可以用一个不同的哈希重新尝试。 当前区块的时间戳必须严格大于最后一个区块的时间戳,但这里唯一能确保的只是它会是在权威链上的两个连续区块的时间戳之间的数值。

注解 基于可扩展因素,区块哈希不是对所有区块都有效。你仅仅可以访问最近 256 个区块的哈希,其余的哈希均为零。

6. 错误处理

assert(bool condition): 如果条件不满足就抛出—用于内部错误。

require(bool condition): 如果条件不满足就抛掉—用于输入或者外部组件引起的错误。

revert(): 终止运行并恢复状态变动。

7 数学和密码学函数

addmod(uint x, uint y, uint k) returns (uint): 计算 (x + y) % k,加法会在任意精度下执行,并且加法的结果即使超过 2**256 也不会被截取。从 0.5.0 版本的编译器开始会加入对 k != 0 的校验(assert)。

mulmod(uint x, uint y, uint k) returns (uint): 计算 (x * y) % k,乘法会在任意精度下执行,并且乘法的结果即使超过 2**256 也不会被截取。从 0.5.0 版本的编译器开始会加入对 k != 0 的校验(assert)。

keccak256(...) returns (bytes32): 计算 (tightly packed) arguments 的 Ethereum-SHA-3 (Keccak-256)哈希。

sha256(...) returns (bytes32): 计算 (tightly packed) arguments 的 SHA-256 哈希。

sha3(...) returns (bytes32): 等价于 keccak256。

ripemd160(...) returns (bytes20): 计算 (tightly packed) arguments 的 RIPEMD-160 哈希。

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) : 利用椭圆曲线签名恢复与公钥相关的地址,错误返回零值。(example usage)

上文中的“tightly packed”是指不会对参数值进行 padding 处理(就是说所有参数值的字节码是连续存放的,译者注),这意味着下边这些调用都是等价的: keccak256("ab", "c") keccak256("abc") keccak256(0x616263) keccak256(6382179) keccak256(97, 98, 99) 如果需要 padding,可以使用显式类型转换:keccak256("\x00\x12") 和 keccak256(uint16(0x12)) 是一样的。

请注意,常量值会使用存储它们所需要的最少字节数进行打包。例如:keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678))。

在一个私链上,你很有可能碰到由于 sha256、ripemd160 或者 ecrecover 引起的 Out-of-Gas。这个原因就是他们被当做所谓的预编译合约而执行,并且在第一次收到消息后这些合约才真正存在(尽管合约代码是硬代码)。发送到不存在的合约的消息非常昂贵,所以实际的执行会导致 Out-of-Gas 错误。在你的合约中实际使用它们之前,给每个合约发送一点儿以太币,比如 1 Wei。这在官方网络或测试网络上不是问题。

8 Using for 如何使用

using A for B,这里A通常是某个library里面定义的某个方法,B是某种数据类型,这句话是把A方法绑定到B类型上,相当于给B类型附加了一个A方法。(也有翻译为附着库的) 在上面的例子中,将LibContract里定义的方法绑定到所有的数据类型。但是一般我们不会在所有的类型实例上都去调用LibContract的方法,应该是要按需using的,这里偷懒就写*。 在通俗一点的例子就是, 比如 using LibInt for uint,然后LibInt里面有定义一个toString方法。我们有一个uint a;那么可以这样调用a.toString(),toString方法在定义的时候,第一个参数会是一个uint类型的变量,表示调用者。

代码语言:javascript
复制
using A for B,A的函数的第一个参数必须和B的数据类型一致。

还有这个方法是可以重载的,你可以定义好几个同名的方法,但是第一个参数的类型不同,调用的时候自动的根据调用类型选择某一种方法。

8. solidity常见错误提示及原因分析

1). 智能合约执行失败

告警描述: " Warning! Error encountered during contract execution [Out of gas] " 发生场景: 执行官网众筹智能合约代码,在给智能合约打代币前,往智能合约地址账户打ETH,交易失败,提示如下: 点击查看信息链接 代码及原因分析: 下面是执行的回调函数,其中“tokenReward.transfer(msg.sender, amount / price);”的意思就是把智能合约的代币打给发送者账号。这个账号还没有代币,所以肯定会执行失败。

代码语言:javascript
复制
    function () payable {
        require(!crowdsaleClosed);
        uint amount = msg.value;
        balanceOf[msg.sender] += amount;
        amountRaised += amount;
        tokenReward.transfer(msg.sender, amount / price);
        FundTransfer(msg.sender, amount, true);
    }

解决方法: 先往智能合约账号打代币,然后打ETH,就不会执行失败了。

2). REMIX+MetaMASK的场景下,调用智能合约函数,提示不可知地址

告警描述: REMIX输出框输出以下告警内容: transact to Crowdsale.checkGoalReached errored: Unknown address - unable to sign transaction for this address: "0x3d7dfb80e71096f2c4ee63c42c4d849f2cbbe363" 发生场景: 更换了Meta账号后,调用之前账号创建的智能合约的函数 原因分析: Remix输出框提示未知地址:

告警描述

查看MetaMask的当前账号,发现是Account 1的账号,所以无法执行合约。

MetaMask的当前账号

解决方法: 更换MetaMask为Account8(地址为0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363)的账号即可正常运行。

3). GETH安装时出现异常

告警描述: GETH安装时出现以下告警: E: Failed to fetch http://101.110.118.22/ppa.launchpad.net/ethereum/ethereum/ubuntu/pool/main/e/ethereum/ethereum_1.8.10+build13740+artful_i386.deb Could not connect to 101.110.118.22:80 (101.110.118.22), connection timed out E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? 发生场景: GETH安装,执行以下命令出现。

sudo apt-get install ethereum

原因分析: 解决方法: <1> 参考网上解决方案 sudo vim /etc/resolv.conf ,添加:

nameserver 8.8.8.8

然后执行:

sudo /etc/init.d/networking restart

还是不行。 <2> 从绿地金融中心搬到家里,做相同的操作,就可以安装成功了。 应该是绿地金融中心的路由器做了某些配置,导致下载不成功。

2). 问题描述模板

告警描述: 发生场景: 原因分析: 解决方法:

9. 常见问题及解答

1).modifer函数是干什么的?

2).如何打币回支付账号?

3).智能合约的定时器和系统函数是什么?

4).当创建一个智能合约时,msg.sender和this的区别? 答复:msg.sender是指外部账户的地址,this是指当前创建的智能合约的地址。

代码语言:javascript
复制
contract AddrTest{
    event LogContractAddress(address exAccount, address contractAddress);
    
    function AddrTest(){
    LogContractAddress(msg.sender,this);
    }    
}

在remix运行,可以证明这个推测

LOG说明

10 View Functions,Pure Functions,Fallback Function的定义?

答案:点击参考官网文档,还没有时间翻译过来。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.04.08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2. REMIN的函数引用
  • 3.address相关全局函数
  • 4.Contract Related
  • 5. 区块和交易属性
  • 6. 错误处理
  • 7 数学和密码学函数
  • 8 Using for 如何使用
  • 8. solidity常见错误提示及原因分析
  • 9. 常见问题及解答
  • 10 View Functions,Pure Functions,Fallback Function的定义?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档