Mac安裝以太坊、remix-ide和智能合約初步
前提條件
-
安裝好brew
brew類似apt-get一樣,在mac友善的安裝各種軟體。
-
安裝golang
我以前是下載下傳代碼編譯的,一段時間沒用,估計版本更新的比較厲害。不能用。直接從新用brew安裝了golang。
安裝geth踩坑
先開始按照網上提示,直接用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
參數說明:
- –datadir : 指定區塊鍊網絡資料的存放位置
- console : 啟動指令行模式,可以在geth中執行指令
- –nodiscover : 指定為私有鍊,不會被網上看到
- >>eth_output.log:表示在目前檔案夾下用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])
到這裡,以太坊測試環境基本就安裝完成了。
智能合約Remix Solidity IDE安裝
這東西也是滿滿的坑。這東西需要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 [email protected] -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