[前回] Web3.0検証(8)-ブロックチェーンのプライベートネットワーク
はじめに
Blockchainプライベートネットワークを構築できました。
今回は、スマートコントラクトを用いたBlockchain内部構造の検証です。
おさらい
- Web3.0の分散型アプリ(DApp)のアーキテクチャ(Ethereumの例)
-
スマートコントラクト(Smart Contract)とは
- Ethereum仮想マシン(EVM)上で実行されるブロックチェーン専用プログラム
- ブロックチェーン上のトランザクション(取引)によりトリガー、事前定義ルールに従って自動実行
- スマートコントラクトの定義は、Solidity(構文はJava Scriptライク)言語を用いる(Ethereumの場合)
-
コントラクト・コードとは
- Ethereumネットワーク上で、EVM Code(Ethereum Virtual Machine Code)と呼ばれるバイトコードの形式で記述され処理される
- 今回の検証は、Ethereumパブリックネットワークではなく、ローカルのBlockchainプライベートネットワークを使用
- コントラクト・コードは、Solidityなど可読性の高い高水準言語で記述し、EVM Codeコンパイル(コンパイラは
solc
)
- Ethereumネットワーク上で、EVM Code(Ethereum Virtual Machine Code)と呼ばれるバイトコードの形式で記述され処理される
検証シナリオ
スマートコントラクトを作成し実行します。
手順は、Ethereum: スマートコントラクトを作成し実行するを参考にしました。
- スマートコントラクトの作成から実行までの流れ
- コントラクト・コードの作成
- Solidity言語でスマートコントラクトの内容を記述したコントラクト・コードを作成
- コントラクト・コードのコンパイル
- コントラクト・コードを、solcを使ってコンパイル
- Contractアカウントを生成
- コンパイル済みのコードをトランザクションに付加し、Blockchainプライベートネットワーク送信
- トランザクションを受信掘者が、トランザクションをブロックチェーンに登録
- Contractアカウントが生成され、アドレスが発行される
- スマートコントラクトへのアクセスと実行
- ユーザーが、Contractアカウントへトランザクションを発行すると、スマートコントラクトが実行される
- コントラクト・コードの作成
検証環境準備
環境情報
CPU: Intel(R) Core(TM) 3.30GHz(4コア8スレッド)
メモリ: 16 GB
OS: Ubuntu 20.04 LTS
Geth: 1.10.18
ちなみに、Ubuntuは、Windows 10で以下のように管理者権限で導入しました。
> wsl --install -d Ubuntu-20.04
Solidityコンパイラsolc
のインストール
$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install solc
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.8.13+commit.abaa5c0e.Linux.g++
Gethを起動
- Genesisファイルのconfigに3行追記
"petersburgBlock": 0,
"istanbulBlock": 0,
"constantinopleBlock": 0
完全なGenesis設定ファイルの例
$ cat $HOME/eth_private_net/TestGenesis.json
{
"config": {
"chainId": 1234,
"homesteadBlock": 0,
"petersburgBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"istanbulBlock": 0,
"constantinopleBlock": 0
},
"difficulty": "0x4000",
"gasLimit": "0x8000000",
"alloc": {}
}
- ノード二つを初期化
$ geth --datadir $HOME/eth_private_net/data1 init $HOME/eth_private_net/TestGenesis.json
$ geth --datadir $HOME/eth_private_net/data2 init $HOME/eth_private_net/TestGenesis.json
- ノード二つをバックグラウンド起動
$ nohup geth --networkid 1234 --port 30303 --nodiscover --datadir $HOME/eth_private_net/data1 2>> $HOME/eth_private_net/data1/geth_error.log &
$ nohup geth --networkid 1234 --port 30304 --nodiscover --datadir $HOME/eth_private_net/data2 2>> $HOME/eth_private_net/data2/geth_error.log &
- ノード1にアタッチ
$ geth attach $HOME/eth_private_net/data1/geth.ipc
アカウントの残高を増やす
検証にはgasが必要です。
- アカウントとコインベースを確認
> eth.accounts
["0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a"]
> eth.coinbase
"0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a"
- 残高を確認
> eth.getBalance(eth.accounts[0])
0
- マイニングスタート
> miner.start()
null
> eth.mining
true
> eth.blockNumber
0
- しばらく待って、再度GenesisブロックのblockNumberを確認
> eth.blockNumber
64
blockNumber増えました、マイニングやっていますね。。。
- ブロックの中身を確認
> eth.getBlock(64)
{
difficulty: 135070,
extraData: "0xd883010a12846765746888676f312e31382e31856c696e7578",
gasLimit: 126082130,
gasUsed: 0,
hash: "0xbb3d9ed065abee93826e4fe12d36f7bb1ef85dd7c40543185acee8632fc2890a",
logsBloom: "...",
miner: "0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a",
mixHash: "0xd1d63c8a0395ba6217e3645963dc8fb31ffb2d487502f2ce9c03491bfc9120a2",
nonce: "0x780dc568a4d1fbed",
number: 64,
parentHash: "0x1fb267c1ce515826fa2c8de5b261dc30fdb524bf19f9171a23b895debb6e52e6",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 537,
stateRoot: "0xcc09514a54af66b44b3cfe29c22b037d4e5350689c787ee2df6e25a1d231e0cf",
timestamp: 1651703855,
totalDifficulty: 8530449,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
- 再度残高を確認
> eth.getBalance(eth.accounts[0])
4.767e+21
だいぶ増えています。やったー。
スマートコントラクトを作成し実行
コントラクト・コードを作成
最も簡単な、get()関数とset()関数のみのスマートコントラクトを作成し、
SingleNumRegister.sol
に保存。
contract
で、スマートコントラクトSingleNumRegister
を宣言。
複数ユーザーがset()関数を用いて同時にデータ更新しても、ブロックチェーンがその整合性を担保してくれます。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract SingleNumRegister {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint retVal) {
return storedData;
}
}
コントラクト・コードのコンパイル
$ solc --abi --bin SingleNumRegister.sol
実行結果、BinaryとContract JSON ABIが表示される
======= SingleNumRegister.sol:SingleNumRegister =======
Binary:
608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220f61e58e1289d29475ea836cc319ed91fd9ef87f5a8da22f11219d57901b595a764736f6c634300080d0033
Contract JSON ABI
[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contractアカウントを生成
コンパイル済みコードをBlockchainプライベートネットワークに送信し、ブロックチェーンに登録、
これで、他のユーザーもこのContractにアクセス出来るようになる。
- gethプロンプトで、コンパイル結果を変数に格納
> var bin = "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220f61e58e1289d29475ea836cc319ed91fd9ef87f5a8da22f11219d57901b595a764736f6c634300080d0033"
undefined
> var abi = [{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]
undefined
- アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
- Contractのオブジェクトを生成
> var contract = eth.contract(abi)
undefined
- オブジェクト情報を含んだトランザクションをBlockchainプライベートネットワークに送信
> var myContract = contract.new({ from: eth.accounts[0], data: bin, gas:1000000})
undefined
- myContractオブジェクトを確認
> myContract
{
abi: [{
inputs: [],
name: "get",
outputs: [{...}],
stateMutability: "view",
type: "function"
}, {
inputs: [{...}],
name: "set",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}],
address: "0x3a988a9ed87358000889ae2a7a92d9833239e053",
transactionHash: "0x209de73abb36302a47ff7bc5f109503f76544d99fc0ed837b31bac77f408f154",
allEvents: function bound(),
get: function bound(),
set: function bound()
}
Blockchainプライベートネットワークに、Contractアカウントが下記アドレスで登録されました。
address: "0x3a988a9ed87358000889ae2a7a92d9833239e053"
スマートコントラクトへのアクセスと実行
登録されたContractアカウントの情報を参照したり、
トランザクションを発行しスマートコントラクト実行可能。
自身の作成したスマートコントラクトを、他のユーザーに利用してもらうため、
以下2点パラメータを渡す必要あり
- Contractのアドレス
- アクセス対象のスマートコントラクトを表す
- ContractのABI(Application Binary Interface)
- ABIとは、Contractの取り扱い説明書
- Contractに定義された関数名
- 関数の呼び出しに必要なパラメータのデータ型
- 関数実行結果のデータ型
- ABIとは、Contractの取り扱い説明書
ノード1で、スマートコントラクトへアクセスし実行
- アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
- デフォルトアカウントを設定
> eth.defaultAccount=eth.accounts[0]
- Contractアカウントへアクセスするオブジェクトを生成
> var cnt = eth.contract(myContract.abi).at(myContract.address);
undefined
- オブジェクトcntを用いてContractにアクセス
> cnt.get()
"0x7b8227cfadcd4ef90c8cd2a59dd46ee71e8b2f98a81d068ab79a86456515773d"
ノード2で、スマートコントラクトへアクセスし実行
- 別端末でノード2にアタッチ
$ geth attach $HOME/eth_private_net/data2/geth.ipc
- アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
- デフォルトアカウントを設定
> eth.defaultAccount=eth.accounts[0]
- 上述Contractのabi、addressをパラメータとして渡し、Contractアカウントアクセス用オブジェクトを生成
> var abi=[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]
undefined
> var address="0x3a988a9ed87358000889ae2a7a92d9833239e053"
undefined
> var cnt = eth.contract(abi).at(address);
undefined
- オブジェクトcntを用いてContractにアクセス
> cnt.set.sendTransaction(6,{from:eth.accounts[0]})
"0x09d90e66dd11b9cb219adde57139d08430db5d389a185be3039af707be194b9f"
> cnt.get()
"0x820eb6699cbfd5408adcb437a7634374467e0740b73148aa12a3da6e2abe59a8"
おわりに
スマートコントラクトの生成と使用を検証しました。
次回は、Blockchainのブロックの中身を深掘りします。
お楽しみに。