本文歡迎轉(zhuǎn)載,轉(zhuǎn)載請標(biāo)明出處
freewolf 資深I(lǐng)T從業(yè)者,關(guān)注
微服務(wù)、區(qū)塊鏈、敏捷開發(fā)、前端技術(shù)等,不是大神,只是出于熱愛。有問題可以到 https://github.com/freew01f/blog 進(jìn)行交流。發(fā)布時間
BiTCoin #480499
寫在前面
最近一段時間一直關(guān)注區(qū)塊鏈的相關(guān)的領(lǐng)域和知識,今天本來想幫助小伙伴建立一個基于以太坊的智能合約Demo,發(fā)現(xiàn)很多過去的文檔都已經(jīng)過時了,無法正常工作。那就只能自己造個輪子,弄個版本新一些幫助大家入門。
本文以流程
tutorial為主,不過多去講技術(shù)原理,原理文章網(wǎng)絡(luò)大把。
目標(biāo)
本文目標(biāo)如下:
- 建立私有以太坊,設(shè)置第一個節(jié)點,挖礦
- 完成一筆轉(zhuǎn)賬交易
- 建立簡單的智能合約
- 建立第二個網(wǎng)絡(luò)節(jié)點
環(huán)境介紹
無論什么開發(fā)都離不開相應(yīng)的環(huán)境,我盡可能將所有軟件都升級到最新版本,以下是本文內(nèi)容相關(guān)的環(huán)境:
- 操作系統(tǒng) MacOS 10.12.6
- Geth 以太坊 CLI https://github.com/ethereum/go-ethereum v1.67
- Solidity 智能合約編譯器 Version: 0.4.15+commit.8b45bddb.Darwin.appleclang
安裝
安裝Node.js,這里不闡述了,源代碼自己編譯吧。
安裝Geth,這里直接去官方網(wǎng)站下載最新的可執(zhí)行程序,復(fù)制到/usr/local/bin,就OK。
最后安裝Solidity,本地要先有brew,才能進(jìn)行安裝:
brew tap ethereum/ethereum
brew install solidity
創(chuàng)建區(qū)塊鏈
創(chuàng)建自己的以太坊私有鏈很簡單,新建一個目錄,在目錄中先建立自己的創(chuàng)世區(qū)塊描述genesis.json文件。
文件內(nèi)容如下
{
"config": {
"chainId": 2017,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"difficulty": "100",
"gasLimit": "2000000",
"alloc": {}
}
為什么自己創(chuàng)建創(chuàng)世區(qū)塊描述,如果使用默認(rèn)值,
difficulty值非常高,這樣挖礦要急死人的。
首先創(chuàng)建兩個賬戶,本文后面需要用到這兩個賬戶,創(chuàng)建賬戶需要輸入兩次密碼。
?? > geth --datadir node1 account new
?? > geth --datadir node1 account new
用我們剛剛創(chuàng)建的描述文件,創(chuàng)建創(chuàng)世區(qū)塊。
?? > geth --datadir node1 --networkid 27027 init genesis.json
使用console連接節(jié)點1并且記錄log
?? > geth --datadir node1 --networkid 27027 console 2>>geth.log
使用geth完成挖礦和交易
連接成功后,看看有幾個賬戶
> eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428", "0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]
查詢下賬戶余額
> eth.getBalance(eth.accounts[0])
0
第一個賬戶沒有余額,accounts[0]默認(rèn)情況是coinbase賬戶,也就是挖礦的收益歸集賬戶,現(xiàn)在我們就來挖礦,賺取獎勵,由于difficulty值很低,挖礦秒出基本。
> miner.start(2);admin.sleepBlocks(1);miner.stop();
true
-
miner.start(2)開始挖礦,參數(shù)是開啟挖礦的計算的線程數(shù) -
admin.sleepBlocks(1)挖到1個區(qū)塊就停止 -
miner.stop()挖礦停止
第一次會創(chuàng)建DAG ,這里會花費一些時間,關(guān)于DAG,詳情見底部參考。出現(xiàn)true說明挖礦完畢,挖完查詢余額。
> eth.getBalance(eth.accounts[0])
5000000000000000000
需要注意,挖一個區(qū)塊,獲得5個以太幣作為獎勵,這里的顯示的單位是wei,并不是以太幣,下面轉(zhuǎn)換一下
> web3.fromWei(eth.getBalance(eth.accounts[0]), 'ether')
5
5個以太幣在accounts[0],現(xiàn)在轉(zhuǎn)2個給accounts[1],轉(zhuǎn)賬時候,單位是wei,但是注意,既然轉(zhuǎn)賬,別忘先解鎖賬戶accounts[0],這里要輸入賬戶密碼。
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:
true
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(2, "ether")})
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"
最后出現(xiàn)的是這個交易的hash,查一下有沒有待處理的交易
> txpool.status
{
pending: 1,
queued: 0
}
> web3.fromWei(eth.getBalance(eth.accounts[1]), 'ether')
0
果然有一筆pending,查詢賬戶accounts[1],并沒有發(fā)現(xiàn)以太幣,這里需要曠工來挖礦,打包這個交易到最新區(qū)塊。交易才能生效,繼續(xù)挖
> miner.start(2);admin.sleepBlocks(1);miner.stop();
true
查下余額
> web3.fromWei(eth.getBalance(eth.accounts[1]), 'ether')
2
已經(jīng)到賬,再看下剛才交易的詳情
> eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
{
blockHash: "0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",
blockNumber: 2,
from: "0x65070d1d224114fd3c8358e9614fd948daecc428",
gas: 90000,
gasPrice: 18000000000,
hash: "0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",
input: "0x",
nonce: 0,
r: "0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s: "0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to: "0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
transactionIndex: 0,
v: "0xfe5",
value: 2000000000000000000
}
到這里整個交易就完成了
簡單的智能合約
下面我們來創(chuàng)建一個極簡單的智能合約,geth 1.6變化蠻大的,以前編譯智能合約的方法都有一些問題,沒什么簡單的辦法,browser-solidity是個不錯的在線編譯選擇,我們還是選擇在本地進(jìn)行操作,前面已經(jīng)通過brew安裝了solidity,創(chuàng)建一個contract文件夾,在文件夾中創(chuàng)建一個hello.sol智能合約文件
pragma solidity ^0.4.13;
contract Hello {
function sum(uint _a, uint _b) returns (uint o_sum, string o_author) {
o_sum = _a + _b;
o_author = "freewolf";
}
}
然后我們來編譯,完成后,會多出兩個文件,abi文件就是智能合約相關(guān)的接口,bin文件就是智能合約編譯代碼。
這里是Mac命令行環(huán)境,不是geth,
?? >開頭的都是命令行
?? > solc -o . --bin --abi hello.sol
?? > ls
Hello.abi Hello.bin hello.sol
在geth中加載這些文件很復(fù)雜,這里我們修改下剛生成的文件
Hello.abi 文件內(nèi)容修改成
var HelloContract = eth.contract([{"constant":false,"inputs":[{"name":"_a","type":"uint256"},{"name":"_b","type":"uint256"}],"name":"sum","outputs":[{"name":"o_sum","type":"uint256"},{"name":"o_author","type":"string"}],"payable":false,"type":"function"}])
Hello.bin 文件內(nèi)容修改成
personal.unlockAccount(eth.accounts[0])
var hello = HelloContract.new({
from: eth.accounts[0],
data: "0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
gas: 500000
})
注意別忘了data必須
0x開頭,New合約就得解鎖自己的賬戶,這里解鎖也寫在這里了
回到geth,加載剛修改的文件,加載bin文件需要輸入賬戶密碼
文件夾
contract就在運行geth命令的目錄
> loadScript("contract/Hello.abi")
true
> loadScript("contract/Hello.bin")
Unlock account 0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:
true
現(xiàn)在智能合約已經(jīng)部署到區(qū)塊鏈上了,但是要挖礦才能生效,挖完就可以盡情玩耍了。
> hello
{
abi: [{
constant: false,
inputs: [{...}, {...}],
name: "sum",
outputs: [{...}, {...}],
payable: false,
type: "function"
}],
address: undefined,
transactionHash: "0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
}
> hello.sum
undefined
> miner.start(1);admin.sleepBlocks(1);miner.stop();
true
> hello.sum
function()
> hello.sum.call(1,2)
[3, "freewolf"]
追加 - 如何建立其他節(jié)點
追加一段,如何創(chuàng)建其他的P2P節(jié)點,首先在原先節(jié)點執(zhí)行下面代碼,查看當(dāng)前節(jié)點信息
> admin.nodeInfo
{
enode: "enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",
id: "bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
ip: "192.168.1.2",
listenAddr: "[::]:30303",
name: "Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
ports: {
discovery: 30303,
listener: 30303
},
protocols: {
eth: {
difficulty: 655652,
genesis: "0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
head: "0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",
network: 27027
}
}
}
拿到node相關(guān)信息,就可以創(chuàng)建新節(jié)點了,這里就不過多解釋了,前面基本都介紹過了
?? > mkdir node2
?? > geth --datadir node2 account new
?? > geth --datadir node2 --networkid 27027 init genesis.json
?? > geth --datadir node2 --networkid 27027 --port 30304 --bootnodes "enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303" console
需要注意的也就是最后一行,寫入你自己的node信息,其他的也沒什么了,進(jìn)入geth后,數(shù)據(jù)同步后,可以使用下面命令
> eth.getBlock('latest')
> admin.peers
以上就可以在第二個節(jié)點看到之前節(jié)點1的信息了。