先开始按照网上提示,直接用brew安装以太坊环境。
brew tap ethereum/ethereum
brew install ethereum
安装过程没问题,悲伤的故事是,安装完成执行:
geth version
来测试的时候,被告知可执行文件非法,不能执行。google了一下,没结果,但不止我一个人碰到,看来这个版本有问题。
卸载掉刚刚装的东西:
brew uninstall ethereum
然后重头再来,直接到git上clone代码编译:
git clone https://github.com/ethereum/go-ethereum.git
cd go-ethereum
make geth
没有问题。编译完成之后,执行
./build/bin/geth version
Geth
Version: 1.9.7-unstable
Git Commit: 0ce5e113be8c54c7c30a3a797827114adb0df19c
Git Commit Date: 20191102
Architecture: amd64
Protocol Versions: [64 63]
Network Id: 1
Go Version: go1.13.4
Operating System: darwin
GOPATH=/Users/xxxxx
GOROOT=/usr/local/Cellar/go/1.13.4/libexec
说明没问题,把geth link到环境可执行的目录下,比如/user/local/bin:
ln -s /xxxx/build/bin/geth /user/local/bin/geth
就可以在任意地方都执行geth启动以太坊了。
先要初始化创世区块。新建一个目录作为项目目录,然后在目录中创建一个名为:genesis.json的文件,内容如下:
{
"nonce": "0x0000000000000042",
"difficulty": "0x020000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x4c4b40",
"config": {
"chainId": 15,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {}
}
具体啥意思我也不知道。都是网上找的,几乎某度找出来的,都是这个一样的文件。我就直接用了。
使用此文件初始化创世区块:
geth init ./genesis.json --datadir ./mychain
WARN [03-21|09:54:38] No etherbase set and no accounts found as default
INFO [03-21|09:54:38] Allocated cache and file handles database=/Users/jianjiangwang/Desktop/mychain/chain/geth/chaindata cache=16 handles=16
INFO [03-21|09:54:38] Successfully wrote genesis state database=chaindata hash=ee3898…ae7194
INFO [03-21|09:54:38] Allocated cache and file handles database=/Users/jianjiangwang/Desktop/mychain/chain/geth/lightchaindata cache=16 handles=16
INFO [03-21|09:54:38] Successfully wrote genesis state database=lightchaindata
参数--datadir ./mychain
用来指定区块数据存放的目录。打开mychain
目录,可以看到geth
目录和keystore
目录。可以认为创世区初始化成功了。
网上所言,有两种启动方式:
geth --datadir ./mychain --nodiscover console 2>>eth_output.log
参数说明:
执行tail -f eth_output.log
, 可以看到输出日志。
这个方法启动,只能在命令行来执行命令测试。
geth --networkid 10 --datadir ./mychain --rpc --rpcapi "admin,debug,eth,miner,net,personal,shh,txpool,web3" --rpcaddr "0.0.0.0" --rpccorsdomain "*" --nodiscover --dev console>>eth.log
然后打开另一个终端,输入:
geth attach "http://127.0.0.1:8545"
进入以太坊的操作终端。
这种方式启动,打开了网络连接的入口,在后面安装了remix-ide之后,在线发布合约的话,需要这种模式。
上述两种都是正常启动方式。启动起来之后,你会发现,这个链,不挖矿。不挖矿就很多东西没法测试了。毕竟以太坊做任何交易都需要消耗gas。
不挖矿的原因,据说是只有发送交易的时候才会挖矿。
继续找资料,找到一个说法:新版本-dev
模式增加了新的参数项:
--dev Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
--dev.period value Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)
据说以前的版本只要加上--dev
就可以通过执行miner.start()
挖矿。反正我直接下载最新版本是不行的,执行都返回null
,根据这个说明是只有交易发生时候才会挖矿。但我发送交易,就直接告诉我没有gas不让做。所以我始终没有得到以太币。不知道别人实际操作,可能需要在配置创世区的json文件中的alloc
参数来设置一个默认账号的初始的以太币吧。
我为了尽快测试,所以修改启动参数,让他自己开始挖矿了。
geth --datadir ./mychain --nodiscover --dev --dev.period 1 console 2>>eth_output.log
启动起来之后,会发现已经内置了一个测试账号,密码是空。并且已经内置了巨量的以太币。其实如果不管挖矿,只有有–dev参数,就会测试模式启动,就有一个带有海量以太币的默认账户。但每次关闭重新启动都会服务到初始状态。
启动geth
之后,在控制台输入命令测试验证。
查看当前账户:
> web3.eth.accounts
创建账户,创建时需要设置密码:
> web3.personal.newAccount('123456')
"0xdaa65af5d348c25266a5588148a9c0e9e4c056f8"
输入的是密码,返回的一长串是账号。
再执行web3.eth.accounts
命令,可以看到多了一个账户。
开始挖矿:
先设置挖矿的账户:
> miner.setEtherbase("0xdaa65af5d348c25266a5588148a9c0e9e4c056f8")
里面的长串是已经存在的账号。
也可以:
miner.setEtherbase(eth.accounts[0])
获取账号列表中的第一个。
可以执行:
>eth.coinbase
看当前设置的挖矿账户是哪个。
开始挖矿:
>miner.start()
停止挖矿:
>miner.stop()
查看账户余额:
web3.eth.getBalance(eth.accounts[0])
eth的最小单位是wei, 1 ether = 1e18 wei
发起转账。转账前需要先解锁账户:
personal.unlockAccount(eth.accounts[0])
personal.unlockAccount(eth.accounts[1])
解锁之后,进行转账:
amount = web3.toWei(5,'ether')
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
从0账户给1账户转5个以太币。
锁上账户:
personal.lockAccount(eth.accounts[0])
personal.lockAccount(eth.accounts[1])
到这里,以太坊测试环境基本就安装完成了。
这东西也是满满的坑。这东西需要nodejs环境。先用brew把nodejs装好,但根据我的尝试,版本号有要求,反复踩坑之后,找了无数页面。最后才安装起来。
基本的环境要求:
$ node -v
v7.10.1
$ npm -v
4.2.0
有说node用8也行的,我没尝试。npm版本感觉影响不大,但有人说也有关系。我原来是6.x,一起给降了。
node的版本太复杂,还好提供了版本管理工具n。先装上:
npm install -g n
然后通过n可以指定需要的node版本。
sudo n 7.10.1
可以随便指定node的版本号,把电脑中的node版本调整到所需要的。
npm的版本直接用npm自己指定就行:
npm install npm@4 -g
其他版本的node,尝试了很多次,不管是直接npm安装还是从git下载代码编译,都失败,主要都是[email protected]
这个东西装不上。更新node版本之后,源代码编译也不成功。大家可以试试,下载源码编译:
git clone https://github.com/ethereum/remix-ide.git
cd remix-ide
npm install
npm start
我直接通过npm安装。但安装过程报错,找不到npx
的执行路径。
先执行:
npm install npx
然后把npx link到可执行目录中,比如/user/local/bin:
ln -s /usr/local/Cellar/node/12.7.0/bin/npx npx
可能是由于我最初是的node是12.7,虽然我后来通过n更换了版本,但是我看了通过npm安装的新的包,仍然在12.7.0的目录中。scryptsy scrypt.js
npm install remix-ide -g
remix-ide
然而,运行的时候,报错!!!
错误大概这样:
module.js:442
throw err;
^
Error: Cannot find module './build/Release/scrypt'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
这就惆怅了。继续找了资料,类似的错误。先单独安装上scrypt
:
npm install -g scrypt
安装好了之后,到remix的安装模块路径:
/usr/local/lib/node_modules/remix-ide/node_modules/scrypt/build/Release
对比单独安装的scrypt,发现少了一个文件。从单独安装的目录中,把这个文件link过来:
link -s /usr/local/Cellar/node/12.7.0/lib/node_modules/scrypt/build/Release/scrypt.node scrypt.node
再次运行remix-ide
,大功告成:
$ remix-ide
setup notifications for /Users/arthurlee/program/ethereum/mychain
Starting Remix IDE at http://localhost:8080 and sharing /Users/arthurlee/program/ethereum/mychain
Tue Nov 05 2019 11:35:45 GMT+0800 (CST) Remixd is listening on 127.0.0.1:65520
打开浏览器,输入地址:http://localhost:8080
。
geth
和remix-ide
都安装好之后,启动,geth
记得用方式二启动。打开浏览器进入合约开发界面。已经有一些例子。我们先搞一个更简单的:
pragma solidity ^0.4.18;
contract HelloWorld {
string msg1;
function HelloWorld(string _msg) public {
msg1 = _msg;
}
function say() constant public returns (string) {
return msg1;
}
}
别的我还没细看。但就发现了一个很讨厌的事情pragma solidity ^0.4.18;
这个,声明了这个合约用什么版的solidity来编写。简单了找了一下,居然没有完整的说明这个这个版本是如何定义的,可能是某种常态吧。但我是不相信这些程序能版本兼容的。至少我看到0.4和0.5的就很大不一样了。管他,按照这个例子来做。
左边工具栏第二个是浏览文件。点+
符号。新建一个合约。起个文件名。把合约内容复制过去。
左边工具栏第三个是编译。在Compiler
选择和合约对应的版本。进行编译。如果有错,在下面会显示错误信息。没错就啥都不显示了。
最先面的Details,可以看到这个合约被编译之后的内容。其中的WEB3DEPLOY
部分应该就是可以复制出来部署的。这部分内容如下:
var _msg = /* var of type string here */ ;
var helloworldContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_msg","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var helloworld = helloworldContract.new(
_msg,
{
from: web3.eth.accounts[0],
data: '0x6060604052341561000f57600080fd5b6040516102b83803806102b8833981016040528080518201919050508060009080519060200190610041929190610048565b50506100ed565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061008957805160ff19168380011785556100b7565b828001600101855582156100b7579182015b828111156100b657825182559160200191906001019061009b565b5b5090506100c491906100c8565b5090565b6100ea91905b808211156100e65760008160009055506001016100ce565b5090565b90565b6101bc806100fc6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc61017c565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101725780601f1061014757610100808354040283529160200191610172565b820191906000526020600020905b81548152906001019060200180831161015557829003601f168201915b5050505050905090565b6020604051908101604052806000815250905600a165627a7a723058201893c1926120c368172f8f8211f6e1a723f12b417d25c83f4982b31ce3ca1d210029',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
})
其中,第一行的内容需要自己修改:
var _msg = /* var of type string here */ ;
后面注释部分改成需要的显示的内容,比如hello world
。
还可以改:
from: web3.eth.accounts[0],
表示那个账户来执行这个合约。
按说可以把这段内容复制下来到控制终端直接黏贴过去发布合约的。黏贴的时候,一行就应该相当于一个命令,会返回好几次undefined
。不用管。
最后如果显示:
Contract mined! address: 0x461b37e377da6ff6d73bef4c29142e160a1e4eeb transactionHash: 0x44b68decc1b692369b003cdd6c547b703546274a00653af81015a3ed843476dc
就算发布成功了。
对应账号的以太币会减少。但此时不一定能执行,需要等若干区块建立之后才能确认,做法就是执行转账了,执行十来次转账之后,然后执行:
helloworld.say()
应该能成功。
也可以直接用remix-ide来发布。
选择左边工具栏第四个,部署和运行。
先选择环境,选择Web3 Provider
。会弹出一个窗口,让输入geth的地址和端口。我没有改,默认的。
http://localhost:8545
如果有问题连不上,会报错,但错误信息一闪而过。如果没问题,下面的Account
会列出来当前节点可以控制的账号。选择一个相当于修改了
from: web3.eth.accounts[0],
再下面,选择Gas啥的。在下面Deploy
边上的输入框,随便输入点东西,相当于修改了
var _msg = /* var of type string here */ ;
但一定要记住,两边用双引号圈起来。否则部署的时候会报错:
Unexpected token h in JSON at position 1
点一下Deploy
,开始部署。完成会显示类似信息:
[block:555 txIndex:0]from:0xcbb...b29dfto:HelloWorld.(constructor)value:0 weidata:0x606...00000logs:0hash:0x3ec...e23f9
哪个终端发布的合约,只能在自己终端来执行。换个终端无法执行。如果要在其他终端执行,需要拿出合约地址和abi。
在remix-ide部署的时候,从右下角的Debug
边上的下拉箭头,点开,找到部署地址。然后在编译详情里面,复制出abi。
然后,去掉回车空格tab之类的,执行:
abi=[{"inputs": [{"name": "_msg","type": "uint256"}],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"constant": true,"inputs": [],"name": "say","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"}]
=
后面的内容就是复制出来的abi。
然后执行:
tt=eth.contract(abi).at("0x692a70D2e424a56D2C6C27aA97D1a86395877b3A")
长串的就是合约地址。之后就可以执行这个合约:
tt.say()
但我怎么看都有问题,如果合约返回的是uint256,一直返回0,其实我设置的参数是2.
如果是字符串,则直接报错:
Error: new BigNumber() not a base 16 Numver:
查了一下,应该是web3的bug。后学再看。
给某个账号发布合约有可能说这个账户没有解锁。解锁的时候,会显示:
Error: account unlock with HTTP access is forbidden
如果这样,就在启动geth的时候增加如下参数:
--allow-insecure-unlock