智能合约Hello World-GETH踩坑纪实
这里不妨假设大家都已经对truffle的基本使用比较熟悉,不熟悉的话可以返回第一页 看看
前几个步骤其实都没啥事,最麻烦的是GETH的部署,各种BUG
0x00 初始化工程
mkdir hello
cd hello
truffle init
0x01 编写合约
创建合约:在contract目录下创建Greeter.sol,内容如下:
pragma solidity >=0.4.25 <0.9.0;
contract Greeter {
address creator;
string greeting;
constructor(string memory _greeting) public{
creator = msg.sender;
greeting = _greeting;
}
function greet() public view returns(string memory) {
return greeting;
}
function setGreeting(string memory _newgreeting) public{
greeting = _newgreeting;
}
}
创建发布脚本:在migrations目录下创建2_deploy_contracts.js,内容如下:
var Greeter = artifacts.require("./Greeter.sol");
module.exports = function(deployer) {
deployer.deploy(Greeter,"Hello, World!");
};
修改truffle-config.js:
module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
// gas: 40000000
}
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
// version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
}
}
}
主要是网络设置,包括端口、id、gas等信息,这里就用8545端口,方便启动ganache调试。
gas是部署合约是支付的费用,我们后面会讲到。
0x03 部署合约
启动ganache:
ganache-cli
编译部署合约:
(另起一个终端)
truffle compile
truffle migrate
部署成功
部署成功
0x04 测试合约
进入truffle控制台:
truffle console
在控制台中输入Greeter可以查看对象的属性:
在truffle5中,之前的调用方式不支持,需要先实例化再调用
实例化对象:
Greeter.deployed().then((instance) => { greeter = instance } )
调用greet()方法:
greeter.greet()
合约部署成功,调用成功
0x05 部署到GETH中
敲黑板!!!这里会有各种意想不到的bug ......记录一下我踩的坑
先聊聊GAS(太坊的激励机制):ETH本质是EVM虚拟机,是具有图灵完备性 的,他不知道自己能不能停下来,更不知道自己什么时候能停下来......而为了防止恶意用户编写死循环程序消耗算力,以太坊设置了计算成本(GAS)。在每一步计算(包括交易、部署合约等),都会消耗一定的gas,当设置的gaslLimit消耗完毕或者用户愿意支付的gas消耗完毕时,计算将终止。无论交易、合约部署成功与否,gas均无法收回,因为消耗了网络算力;gas支付给矿工,作为给矿工的奖励。
关于GAS先放到这里,我们继续看报错
000. GETH各类报错
如果不知道怎么启动GETH的话可以看看DAY2
平常我们启动GETH就直接启动了:
geth --networkid "30" --nodiscover --datadir="mychain" console 2>>"mychain"/"err.log"
但是!!!这样的话我们的truffle是连接不了的,需要另外添加rpc参数以rpc方式启动:
geth --networkid "30" --nodiscover --datadir="./mychain" console 2>>"mychain"/"err.log" --rpc
然而,这玩意还不够——在部署合约的时候是需要支付燃料(GAS)的,在支付燃料的时候是需要账户转账的,在转账的时候是需要用户解锁的,然而刚刚那种启动方式可能会告诉你RPC不安全,不让你解锁......
于是我们得在启动GETH的时候就允许不安全解锁:
geth --networkid "30" --nodiscover --datadir="./mychain" console 2>>"mychain"/"err.log" --rpc --allow-insecure-unlock
001. Expected parameter 'from' not passed to function.
问题原因:GETH没连接到用户,部署合约的费用没人出......
解决方案:进入GETH控制台,创建用户:
personal.newAccount("xiabee")
010. Returned error: authentication needed: password or unlock.
问题原因:用户没解锁,还是付不了钱......
解决方案:进入GETH控制台,解锁用户:
personal.unlockAccount(eth.accounts[0])
# 这里的密码就是刚刚设置的"xiabee"
011. Error returned: error invalid opcode SHR
问题原因:这个问题 是个新版本truffle的bug,具体原因在DAY2 里讲创世块的时候提到了,可能是应为truffle更新后不能很好的解决拜占庭将军问题
解决方案:手动在GETH创世块的json 中添加拜占庭块 "byzantiumBlock": 0,然后整个区块链重新跑一遍(具体参考DAY2 )
"byzantiumBlock": 0,
"constantinopleBlock": 0
100. Returned error: insufficient funds for gas * price + value.
问题原因:运算比较复杂,支付的gas不足,不够完成合约的部署
解决方案:在truffle-config.js中,手动设置gas的值,将其增大,具体值可以参考eth.getBlock(0)中gasLimit的大小:
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
gas: 40000000
}
},
如果多次调试gas还不行......先挖会矿,可能是钱包里没钱了(x)
101. Returned error: tx fee (800000.00 ether) exceeds the configured cap (1.00 ether).
问题原因:超出设定的支付上限,拒绝支付。这个上限的设定是为了避免用户直接将gas设置为正无穷(最大值),这样gas可能会失去部分意义。
解决方案:在truffle-config.js中,手动设置gas的值,将其减小,具体值可以参考eth.getBlock(0)中gasLimit的大小
合适的gas具体是多少我也不太清楚,需要视具体合约计算量而定......所以我通过夹逼法找到了一个能用的gas
110. Blocks: 0 Seconds: 一直转圈
问题原因:链上没人挖矿了,没有节点承认你的交易/部署
解决方案:进入GETH控制台,继续挖矿
miner.start()
# 开始挖矿
miner.stop()
# 停止挖矿
最后的最后,如果看到这个界面说明部署成功了:
0x06 测试GETH
找到合约ABI
找到./build/contracts/Greeter.json
文件中的abi的键值对:
"abi": [
{
"inputs": [
{
"internalType": "string",
"name": "_greeting",
"type": "string"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [],
"name": "greet",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "string",
"name": "_newgreeting",
"type": "string"
}
],
"name": "setGreeting",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
在GETH控制台中定义变量:
var abi = [{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"_newgreeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
找到合约地址
GETH控制台中:
var con_add = "0x35aC45A744E17DA42D1e5017f1AEbdD37235Ffa1"
实例化
var hello = eth.contract(abi).at(con_add)
调用
hello.greet()
合约执行成功!
0x07 小结
整个过程其实不复杂,就是BUG贼多......网上教程大部分比较老,需要手动测试各种魔法操作
如果你用的是truffle5,那对着本片做下来,应该是不会有这么多BUG的
Comments | NOTHING