这篇文章主要讲解了“Solidity故障怎么排查”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Solidity故障怎么排查”吧!
1、推荐编辑器
目前尝试 Solidity 编程的最好的方式是使用 Remix(https://remix.ethereum.org/) (需要时间加载,请耐心等待)。Remix 是一个基于 Web 的 IDE,它可以让你编写 Solidity 智能合约,然后部署并运行该智能合约。
2、Visual Studio Extension
Microsoft Visual Studio 的 Solidity 插件,包含 Solidity 编译器。
3、Visual Studio Code extension
Microsoft Visual Studio Code 插件,包含语法高亮和 Solidity 编译器。
function mint(address receiver, uint amount)
1、在REMIX输入时,地址一定要有""表示,否则amount就取不到值。
例如是mint("0xca35b7d915458ef540ade6068dfe2f44e8fa733c",101)
2、程序中,如果注释包含中文,单步调试的位置不准确。
<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); }
测试地址和地址调用代码举例
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); } }
this (current contract’s type): 表示当前合约,可以显式的转换为Address
selfdestruct(address recipient): destroy the current contract, sending its funds to the given Address
销毁当前合约,发送当前以太坊余额到给定的地址 suicide(address recipient):
selfdestruct的别名函数
**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 个区块的哈希,其余的哈希均为零。
assert(bool condition): 如果条件不满足就抛出—用于内部错误。
require(bool condition): 如果条件不满足就抛掉—用于输入或者外部组件引起的错误。
revert(): 终止运行并恢复状态变动。
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:https://ethereum.stackexchange.com/q/1777/222)
上文中的“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。这在官方网络或测试网络上不是问题。
1、智能合约执行失败
告警描述: " Warning! Error encountered during contract execution [Out of gas] "
发生场景: 执行官网众筹智能合约代码,在给智能合约打代币前,往智能合约地址账户打ETH,交易失败,提示如下:
点击查看信息链接:https://ropsten.etherscan.io/tx/0x8b4da573b36dbf7361c95a0156dfe060553874fbb4d401f989c7a4a6d539ebfa
代码及原因分析: 下面是执行的回调函数,其中“tokenReward.transfer(msg.sender, amount / price);”的意思就是把智能合约的代币打给发送者账号。这个账号还没有代币,所以肯定会执行失败。
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> 从绿地金融中心搬到家里,做相同的操作,就可以安装成功了。 应该是绿地金融中心的路由器做了某些配置,导致下载不成功。
4、问题描述模板
告警描述: 发生场景: 原因分析: 解决方法:
1).modifer函数是干什么的?
2).如何打币回支付账号?
3).智能合约的定时器和系统函数是什么?
4).当创建一个智能合约时,msg.sender和this的区别?
答复:msg.sender是指外部账户的地址,this是指当前创建的智能合约的地址。
contract AddrTest{ event LogContractAddress(address exAccount, address contractAddress); function AddrTest(){ LogContractAddress(msg.sender,this); } }
在remix运行,可以证明这个推测
LOG说明
感谢各位的阅读,以上就是“Solidity故障怎么排查”的内容了,经过本文的学习后,相信大家对Solidity故障怎么排查这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。