ATourofEthereum——區塊鏈與智慧合約之旅
Welcome
以前粗略瞭解過一些比特幣和區塊鏈的理論知識,利用本次休假的時間,趁機熟悉一下Ethereum 和 Solidity,嘗試建立一個簡單的只能合約。
What is geth
本文中所有的操作都在 geth 中執行,geth是一個全功能的以太坊節點,它使用golang語言實現。geth 為我們提供了區塊資訊檢視、挖礦、執行智慧合約等功能。
ethereum 和 bitcoin 的一些區別
1、ethereum 的裡的指令碼程式碼是圖靈完備的,而比特幣的指令碼則是基於棧的,有一定侷限性。
2、ethereum 基於賬戶模型,沒有UXTO(Unspent Transaction Outputs)概念,交易速度會慢一些。
How to install geth
如果你已經安裝了 homebrew Mac 上 安裝 geth 非常容易:
brew tap ethereum/ethereum
brew install ethereum
其他的安裝方式可以參考文件:
https://ethereum.gitbooks.io/frontier-guide/content/installing_linux.html
Run geth
在使用geth前有兩個重要的引數需要指定:
1、 –datadir “~/ethmain” datadir 指定在哪裡儲存區塊的資料(包含賬戶)
2、–networkid “1” 指定網路ID,約定1代表主網路也就是以太坊的公共網路, 通常用2代表測試網路, 我們可以指定任意非負ID,作為自己的私有鏈。
Create own private ethereum
本文的目標是建立一個私有鏈,並在該鏈上挖礦和執行一個簡單的智慧合約。
1、設定資料的儲存目錄為: “~/ethprivate”
2、網路ID :“9527”
Before create
在建立私有鏈前我們先建立幾個外部賬戶,方便後續操作,以太坊的賬號分為外部賬戶和合約賬戶兩種,本次將建立兩個外部賬戶。
使用 geth account 指令可以進行賬戶相關的操作。
使用 geth account new 指令建立賬戶,如果在js 控制檯中(後面會說明) 則可以用 personal.newAccount(“passphrase”) 指令
geth --datadir "~/ethprivate" --networkid 9527 account new
指令執行後要求輸入 passphrase 設定一個簡單的口令即可 如:“12345”:
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {0df066922b375d8641b992352958664ff26b570d}
執行 geth account list 可以檢視已有的賬戶(在js控制檯裡使用 eth.accounts)
geth --datadir "~/ethprivate" --networkid 9527 account list
可以檢視到剛才建立的賬戶
Account #0: {0df066922b375d8641b992352958664ff26b570d} keystore:///${HOME}/ethprivate/keystore/UTC--2018-02-13T03-27-14.788253000Z--0df066922b375d8641b992352958664ff26b570d
The genesis block
現在準備建立第一個區塊,通常稱該區塊為創世塊。在創世塊裡我們需要進行一些簡單的配置,主要是挖礦的難度設定、發放一些貨幣到剛建立的賬戶中。通過一個json檔案設定,下面是詳細配置
{
"config": {
"chainId": 9527,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"nonce": "0x0000000000000042",
"timestamp": "0x2537",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x2537",
"gasLimit": "0x80000000",
"difficulty": "0x10",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"alloc": {
"0df066922b375d8641b992352958664ff26b570d":{
"balance":"10"
}
}
}
將上面的配置儲存為檔案.json 的檔案,然後執行初始化指令:
geth --datadir "~/ethprivate" --networkid 9527 init genesis.json
成功建立後會輸出以下資訊
INFO [02-13|11:51:54] Allocated cache and file handles database=${HOME}/ethprivate/geth/chaindata cache=16 handles=16
INFO [02-13|11:51:54] Writing custom genesis block
INFO [02-13|11:51:54] Successfully wrote genesis state database=chaindata hash=eb3e89…697b61
INFO [02-13|11:51:54] Allocated cache and file handles database=${HOME}/ethprivate/geth/lightchaindata cache=16 handles=16
INFO [02-13|11:51:54] Writing custom genesis block
INFO [02-13|11:51:54] Successfully wrote genesis state database=lightchaindata hash=eb3e89…697b61
The JavaScript Console
geth 提供了一個Javascript 執行環境,使用 geth console 命令可以進入JS Console。在JS Console可以使用更多的操作,後面的智慧合約部署也將在 JS Console進行。
這裡建議將標準錯誤重定向到另一個檔案裡
geth console 2>>geth.log
並在另一個終端使用 tail -f geth.log 監控輸出。
geth --datadir "~/ethprivate" --networkid 9527 console 2>>geth.log
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable/darwin-amd64/go1.9.4
coinbase: 0x0df066922b375d8641b992352958664ff26b570d
at block: 0 (Thu, 01 Jan 1970 10:38:47 CST)
datadir: ~/ethprivate
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
查詢一下第一個賬戶的餘額,我們在創世塊裡向其預先分配了 10個幣:
> eth.getBalance(eth.accounts[0])
10
檢視一下創世塊的資訊
>eht.getBlock(0)
{
difficulty: 16,
extraData: "0x00",
gasLimit: 2147483648,
gasUsed: 0,
hash: "0xeb3e8947d2e01afbb6651c0276921f5f44b4c549a89942285716d935aa697b61",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x3333333333333333333333333333333333333333",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000042",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 507,
stateRoot: "0x2b4a324f7683f0f14efef5b1f5c6ac9432b223da4e9ce56ecbc47482402c13d1",
timestamp: 9527,
totalDifficulty: 16,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
輸入 exit 可以退出 JS console;
Other Commands
進入 console 時能看到其載入的js modules
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable/darwin-amd64/go1.9.4
coinbase: 0x0df066922b375d8641b992352958664ff26b570d
at block: 0 (Thu, 01 Jan 1970 10:38:47 CST)
datadir: ~/ethprivate
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
-
admin 檢視一些網路和 節點相關的資訊
如:admin.nodeInfo
-
eth 進行一些區塊相關的操作
如: eth.accounts() 可以檢視當前的賬戶列表eth.getBlock(N) 可以檢視具體的區塊資訊。 eth.getBalance() 檢視某個賬戶的金額
- admin 檢視一些網路和 節點相關的資訊
如:admin.nodeInfo - personal 可以進行一些賬號相關的操作,
如果控制檯丟擲以下錯誤
Error: authentication needed: password or unlock
at web3.js:3143:20
at web3.js:6347:15
at web3.js:5081:36
at web3.js:4137:16
at <anonymous>:1:1
說明某項命令需要解鎖賬戶,可以通過:
personal.unlockAccount(eth.accounts[0],"passwd")
因為本身是JS 所以可以在控制檯裡直接檢視某個Module物件支援的屬性和方法。
The miner
通過miner 提供的函式可以進行挖礦,挖礦會將算出新的區塊,並將交易池中的交易打包到區塊裡。挖礦的獎勵預設發放到第一個賬戶中。
在控制檯中輸入下面指令啟動挖礦。
miner.start()
在我們監控的 log 檔案裡能看到挖礦的資訊
Commit new mining work number=8 txs=0 uncles=0 elapsed=139.017µs
INFO [02-13|13:47:08] Successfully sealed new block number=8 hash=39f5db…99b268
使用eth.blockNumber 可以看到當前的已經挖出了多少區塊。
使用 eth.getBlock(N) 可以檢視具體的區塊資訊。
> eth.getBalance(eth.accounts[0])
115000000000000000010
可以看到挖礦的獎勵已經發放到第一個賬戶中。
使用
miner.stop()
可以停止挖礦。
Transactions
因為挖礦的獎勵都投放到了第一個賬戶中,我們再建立一個賬戶,將部分獎勵轉到新賬戶上。這就是傳說中的“轉賬”功能。
> eth.getBalance(eth.accounts[0])
150000000000000000010
> eth.getBalance(eth.accounts[1])
0
在js 控制檯上的轉賬很簡單,參考一下指令
> user1=eth.accounts[0]
"0x0df066922b375d8641b992352958664ff26b570d"
> user2=eth.accounts[1]
"0x87eaff177048b0931475efd0b7837b67c91b7232"
> personal.unlockAccount(user1,"12345")
true
> eth.sendTransaction({from:user1,to:user2,value:web3.toWei(100,"ether")})
轉賬前如果賬戶是被鎖住的,需要使用密碼進行解鎖。
查詢一下,指令執行後的結果
> eth.getBalance(user1)
150000000000000000010
> eth.getBalance(user2)
0
發現金額並沒有減少, 原因是這個交易只是進入了交易池,還沒有打包到區塊裡,也就是沒有被真正的認可。
使用txpool.status 可以待交易的資料。
> txpool.status
{
pending: 1,
queued: 0
}
啟動挖礦完成交易
> miner.start(1)
null
> miner.stop()
true
> eth.getBalance(user2)
3000000000000000000
> txpool.status
{
pending: 0,
queued: 0
}
檢視挖礦時的日誌輸出:
INFO [02-22|10:32:09] Starting mining operation
INFO [02-22|10:32:09] Commit new mining work number=31 txs=1 uncles=0 elapsed=416.345µs
INFO [02-22|10:32:18] Successfully sealed new block number=31 hash=6b9c9d…0fd396
INFO [02-22|10:32:18] ? mined potential block number=31 hash=6b9c9d…0fd396
INFO [02-22|10:32:18] Commit new mining work number=32 txs=0 uncles=0 elapsed=102.669µs
INFO [02-22|10:32:19] Successfully sealed new block number=32 hash=94bdc9…2c48da
INFO [02-22|10:32:19] ? mined potential block number=32 hash=94bdc9…2c48da
可以看到在第31一個區塊下有一個 “txs=1” 交易被打包。
第31個Block的資訊:
> eth.getBlock(31)
{
difficulty: 131072,
extraData: "0xd883010703846765746887676f312e392e348664617277696e",
gasLimit: 2083415374,
gasUsed: 21000,
hash: "0x6b9c9d4fec6a61b4b4294393340613084e96f6b14c3931697225ecdb130fd396",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x0df066922b375d8641b992352958664ff26b570d",
mixHash: "0xe66e02b7e4e0797c5e125b42c2e6ac333de3d05c20c6050922652a1466c8b5f4",
nonce: "0x2edd4febb730901b",
number: 31,
parentHash: "0xb04cbb2b541c7a5240d7d5bef817ca8717fdf156fb1e1372971e63676c2a86e1",
receiptsRoot: "0x1b6be00820be77d7acc990b0fba653a758eeadd7018c9854f09645c0dd00a259",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 653,
stateRoot: "0xe06d4c3ec4245c189ee415b965876c8c937f5321b7f8f1175e7f1bd97bcead31",
timestamp: 1519266729,
totalDifficulty: 4080784,
transactions: ["0x4a9e719868f7713cd450c9c69d0ad87995999fac1be27d119d1ba24b9bd31c96"],
transactionsRoot: "0x5ca299ef95bfc52addf5a3a344d71fff750562ab97316476a5b44954089aefd7",
uncles: []
}
看到 transactions 裡包含了:[“0x4a9e719868f7713cd450c9c69d0ad87995999fac1be27d119d1ba24b9bd31c96”] 這是該交易的hash索引。
> eth.getTransaction("0x4a9e719868f7713cd450c9c69d0ad87995999fac1be27d119d1ba24b9bd31c96")
{
blockHash: "0x6b9c9d4fec6a61b4b4294393340613084e96f6b14c3931697225ecdb130fd396",
blockNumber: 31,
from: "0x0df066922b375d8641b992352958664ff26b570d",
gas: 90000,
gasPrice: 18000000000,
hash: "0x4a9e719868f7713cd450c9c69d0ad87995999fac1be27d119d1ba24b9bd31c96",
input: "0x",
nonce: 0,
r: "0xfcb2fc5a36cd29b2af329714a1776b35fa6f45f8b0b68d4e6b08269e95fbde67",
s: "0x64afcee6177262bf84ce416ac9c9ff6983394f9a6c686ee8f72f3d6fb88619d7",
to: "0x87eaff177048b0931475efd0b7837b67c91b7232",
transactionIndex: 0,
v: "0x4a91",
value: 3000000000000000000
}
> eth.accounts
["0x0df066922b375d8641b992352958664ff26b570d", "0x87eaff177048b0931475efd0b7837b67c91b7232"]
從 from 、to 、value 的資料裡可以看到剛才的轉賬資訊。
至此已經成功完成一筆轉賬交易。
A sample contract
我們將定義一個簡單的智慧合約,該合約提供付費刻字功能,可以將使用者的名字燒錄到區塊鏈裡,類似於“到此一遊” %>_<%
Writing a contract
下面是相關的solidity 程式碼,呼叫carve 時需要支付大於 costForCarve 的費用。
pragma solidity ^0.4.20;
contract CarveName {
address owner;
string[] public names;
mapping (address=>uint) nameToOwner;
uint costForCarve = 1 wei;
event OnNameCarve(uint nameId,string name);
function CarveName() public {
owner = msg.sender;
}
function carve(string _name) external payable returns (uint) {
require(msg.value>=costForCarve);
uint id = names.push(_name);
nameToOwner[msg.sender] = id;
OnNameCarve(id,_name);
return id;
}
function getNameById(uint _id) external view returns(string) {
return names[_id];
}
function setCost(uint _cost) external onlyOwner {
costForCarve = _cost;
}
function withdrawCash() external onlyOwner {
owner.transfer(this.balance);
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
}
Compiling and deploying
為了簡單方便,本次先使用線上的編譯器Solidity瀏覽器–Remix 進行編譯。
編譯後獲得ABI
[{"constant":false,"inputs":[{"name":"_cost","type":"uint256"}],"name":"setCost","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"names","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"carve","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getNameById","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawCash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nameId","type":"uint256"},{"indexed":false,"name":"name","type":"string"}],"name":"OnNameCarve","type":"event"}]
對應的BYTECODE
606060405266038d7ea4c68000600355341.................
回到控制檯,利用上面的資料部署合約。
1、通過獲得的“ABI”建立合約物件,其實就是建立一個js 物件。
//將ABI 資料轉換為json
> carveNameAbi=JSON.parse(`....`)
//建立 Contract 物件
> carveNameContract = eth.contract(carveNameAbi)
//輸入bytecod,注意需要以16進製表示,即0x 開頭
> carveNameBytecode="0x..............."
//預估一下部署合約損耗的Gas
> eth.estimateGas({data:carveNameBytecode})
615383
> eth.getBalance(eth.coinbase)
182000000000000000010
//部署合約,即提交一個交易
> contractInterface = carveNameContract.new({data:carveNameBytecode,gas:625383,from:eth.coinbase})
//以下是輸出:
{
abi: [....],
address: undefined,
transactionHash: "0x30a506b4d5c970ca0316c03e840ab8094346f5d981b18d5e09ce477f208f938f"
}
> txpool.status
{
pending: 1,
queued: 0
}
此時合約的部署還在待交易池中,啟動挖礦完成交易。檢視挖礦時的輸出日誌輸出:
Submitted contract creation fullhash=0x559ad19c5e70c433febef9eb494c827d831e41b15ca596d35ee20a546aed2b5e contract=0x75ce54374b6f9CC304Ec2a912dCd54078BD163cd
Starting mining operation
INFO [02-22|15:21:29] Commit new mining work number=42 txs=1 uncles=0 elapsed=561.894µs
INFO [02-22|15:21:29] Successfully sealed new block number=42 hash=4426e8…df6472
INFO [02-22|15:21:29] ? block reached canonical chain number=37 hash=dcbccd…141029
INFO [02-22|15:21:29] ? mined potential block number=42 hash=4426e8…df6472
可以看到交易已記錄在第42個區塊上,同時獲得了合約的地址:contract=0x75ce54374b6f9CC304Ec2a912dCd54078BD163cd ,該地址在執行合約時使用。
完整的 ContractInterface 資訊如下:
{
abi: [....],
address: "0x75ce54374b6f9cc304ec2a912dcd54078bd163cd",
transactionHash: "0x559ad19c5e70c433febef9eb494c827d831e41b15ca596d35ee20a546aed2b5e",
OnNameCarve: function(),
allEvents: function(),
carve: function(),
getNameById: function(),
names: function(),
setCost: function(),
withdrawCash: function()
}
The transactions about contract
在js 控制檯,通過 eth.getBlock 查詢區塊上的交易資訊,本例中合約部署的資訊寫在了第42個區塊上,
使用eth.getBlock 檢視區塊的資訊,找到交易的地址。
> eth.getBlock(42)
{
difficulty: 131072,
extraData: "0xd883010703846765746887676f312e392e348664617277696e",
gasLimit: 2069214973,
gasUsed: 615383,
.....
transactions: ["0x559ad19c5e70c433febef9eb494c827d831e41b15ca596d35ee20a546aed2b5e"],
transactionsRoot: "0xa731fe9030b335a49e46c557d37717fba5fc045fd39400575e58d21282da0eb7",
uncles: []
}
通過eth.getTransactionReceipt 查詢交易的詳細資訊:
> eth.getTransactionReceipt("0x559ad19c5e70c433febef9eb494c827d831e41b15ca596d35ee20a546aed2b5e")
{
blockHash: "0x4426e8e1680f290432a571871f2b2f97bb83b0b6603874665bcab44792df6472",
blockNumber: 42,
contractAddress: "0x75ce54374b6f9cc304ec2a912dcd54078bd163cd",
cumulativeGasUsed: 615383,
from: "0x0df066922b375d8641b992352958664ff26b570d",
gasUsed: 615383,
........
transactionHash: "0x559ad19c5e70c433febef9eb494c827d831e41b15ca596d35ee20a546aed2b5e",
transactionIndex: 0
}
可以看到交易的詳細資訊裡包含了合約的地址:contractAddress: “0x75ce54374b6f9cc304ec2a912dcd54078bd163cd”,
Other people run The code
在使用他人釋出的合約前需要通過 ABI 和 contract Address 可以獲取建立合約物件,和部署合約程式碼不同的是,部署時使用new方法,而獲取則使用 at 方法。
> contract=eth.contract(abi)
> interface = contract.at(address)
有了介面物件後就可以呼叫合約上面的方法了,方法的呼叫可以分成兩種。
> interface.carve.call("Hello 9527",{from:eth.accounts[0],value:web3.toWei(1)})
1
這裡使用 call 方式呼叫,此時只是本地呼叫,不會產生交易,不會損耗Gas。
> interface.carve.sendTransaction("Hello 9527",{from:eth.accounts[0],value:web3.toWei(1)})
"0xb20a62ce226e77f766cbb540ba7dccdd15981227cc1f9932db9db12affdbca15"
使用sendTransaction 的方式抵用合約函式,會建立一個交易。
啟動挖礦,等待礦工打包交易完成,完成後可以通過下面的指令查詢到已寫入的資料
> interface.getNameById.call(0)
"Hello 9527"
至此一個簡單的智慧合約已執行完備。
Thinking about blockchain
Q:如何看待比特幣、區塊鏈 ?前景如何?
A: 我的回答是看不透。
第一次接觸比特幣大概是在2012年的時候(年輕不懂事?,錯過了一次暴富的機會),最近區塊鏈的技術又火了起來。
區塊鏈通過技術手段,建立一種去中心權威的 共識 機制,在一定條件下這個共識是可信任的。現實世界中往往需要第三方的權威機構(如各種企業公司、銀行、**部門等)來保證這份信任。第三方機構也是人管理的,未必就萬無一失。各種虛擬貨幣就是利用其“可信任性”和相對的“稀缺性”,將自身轉換為資源。資源的存在可以使人投入價值,進行買賣,也就演變成了現在的炒幣。 網上還有一種說法:“以前的網路是資訊互聯,而比特幣是價值互聯”。
在我看來這種“共識”機制,會觸發權力的變革。仔細想想一旦機制成熟,很多的權威機構將失去權力,甚至不復存在。可是“權力的慾望” 可比普通的“物質欲” 高階多了。
對開發者而言,程式設計的模式又多了一種”智慧合約”,你可以直接程式碼的執行中獲利。一個成熟的公有鏈上執行著無數的智慧合約,執行產生的費用一部分給了礦工,一部分給了合約的創造者。說不定會形成一個 “合約市場” 客戶根據需求找尋合適的“合約”。
以上都是我自己的一些胡亂想法,愚見莫笑,區塊鏈技術到底會如何?
參考連結
1、https://ethereum.gitbooks.io/
2、https://cryptozombies.io/zh/course/
3、https://ethereum.gitbooks.io/frontier-guide/content/jsre.html
相關文章
- 白話智慧合約與區塊鏈技術區塊鏈
- 區塊鏈智慧合約開發區塊鏈
- 區塊鏈智慧合約是什麼?區塊鏈
- 區塊鏈智慧合約解決方案區塊鏈
- 區塊鏈——以太坊、智慧合約簡介區塊鏈
- 區塊鏈BSC智慧合約DAPP開發區塊鏈APP
- 區塊鏈合同智慧合約上鍊聯盟鏈區塊鏈
- 區塊鏈學習-Golang 與智慧合約的互動(一)區塊鏈Golang
- 智慧合約-區塊鏈核心技術之一區塊鏈
- 區塊鏈100講:Hyperledger Fabric 中的鏈碼(智慧合約)區塊鏈
- 區塊鏈2.0以太坊智慧合約solidity之helloworld區塊鏈Solid
- 使用truffle部署以太坊智慧合約到區塊鏈區塊鏈
- 區塊鏈智慧合約技術系統開發區塊鏈
- 區塊鏈構建和履行智慧合約的步驟區塊鏈
- JAva智慧合約DAPP系統開發(區塊鏈)JavaAPP區塊鏈
- 區塊鏈Hyperledger Fabric 2.x 自定義智慧合約區塊鏈
- 南京區塊鏈智慧合約交易系統開發方案區塊鏈
- 鄭州區塊鏈智慧合約開發技術公司區塊鏈
- 區塊鏈DApp開發 | 跨鏈智慧合約技術搭建開發區塊鏈APP
- 區塊鏈趣步DAPP智慧合約合約系統技術開發詳情區塊鏈APP
- 區塊鏈之--2小時構建以太坊智慧合約區塊鏈
- 區塊鏈平臺EOSIO開發智慧合約和dapp(一)區塊鏈APP
- 在業務過程中使用區塊鏈和智慧合約區塊鏈
- 長沙區塊鏈DAPP智慧合約系統開發方案區塊鏈APP
- MMMBSC6.0互助區塊鏈智慧合約系統開發區塊鏈
- 區塊鏈底層公共鏈,聯盟鏈,baas系統智慧合約開發區塊鏈
- 淺談LikeLib公鏈及智慧合約區塊鏈技術中應用區塊鏈
- SAP雲平臺,區塊鏈,超級賬本和智慧合約區塊鏈
- 區塊鏈100講:淺析以太坊網路智慧合約原理區塊鏈
- 百度安全研究院:區塊鏈智慧合約介紹區塊鏈
- 【區塊鏈】實戰·以太坊智慧合約程式設計引導區塊鏈程式設計
- 區塊鏈3.0,人工智慧與區塊鏈的完美融合區塊鏈人工智慧
- 區塊鏈與金融的結合區塊鏈
- 區塊鏈技術精華:四十種智慧合約支援平臺(四)區塊鏈
- 區塊鏈技術精華:四十種智慧合約支援平臺(三)區塊鏈
- 英國就區塊鏈智慧合約應用開展法律改革研究區塊鏈
- 區塊鏈-智慧合約開發微信線上語音培訓區塊鏈
- 區塊鏈/趣步DAPP/智慧合約系統開發/合約跟單/python技術詳情區塊鏈APPPython