Home

Solana in Rust & TypeScript

Solanaの開発環境について、動画を参考に動作確認とSolanaのウォレット、Phantomとの連携を試してみました。

https://github.com/solana-labs/example-helloworld

環境: Cargo 1.57.0, node 16.13.1 / WSL / Windows 11

インストール

sh -c “$(curl -sSfL https://release.solana.com/v1.9.5/install)”

export PATH=”/home/k/.local/share/solana/install/active_release/bin:$PATH”

solana config set –url localhost

solana-keygen new -o /home/k/.config/solana/id.json

solana-test-validator

Solana CLIをインストールしてデーモンが立ち上げます。そして別コンソールで、オンチェーンコード(Rust or C。ここではRustを選択)をビルド(SharedObject生成)、これをデプロイして、オフチェーンコードのTypeScriptを実行します。

npm install

npm run build:program-rust

solana program deploy dist/program/helloworld.so

npm run start

デプロイのとき、コインがないとエラーがでるので、エアドロップのコマンドを実行して受け取ります。(とりあえず10 SOL)

solana airdrop 10

これで実行されるのは、main.tsです。処理の詳細はhello_world.tsに記述されています。ここではコンソール実行していますが、TypeScriptで書かれているのでWebにもっていきやすいです。オンチェーンコードとオフチェーンコードがコンソールで一通り動作確認できるのは、使いやすいと感じました。

次に、Phantomウォレットとの連携ですが、ウォレットで秘密キーのimportができないので(ニーモニックのimportは当然できる、秘密キーのexportはできるのに・・なぜ)、ウォレットのニーモニックからキーペアを生成して、ここまでで生成したアカウントに上書きします。

参考

[recover] seed phrase .. でウォレットのニーモニックをコピペします。

ウォレットは、ローカルホストを選択します。ウォレットのアドレス(あれ、Localhostの表示がかぶっている^^ ; )とコンソールコマンドでアドレスが一致していることを確認します。また最近の活動はトランザクションIDなので、下記のようにコードを変更して確認できます。(TX: )

export async function sayHello(): Promise<void> { console.log('Saying hello to', greetedPubkey.toBase58()); const instruction = new TransactionInstruction({ keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}], programId, data: Buffer.alloc(0), // All instructions are hellos }); let tx = await sendAndConfirmTransaction( connection, new Transaction().add(instruction), [payer], ); console.log("TX: " + tx); }
Code language: JavaScript (javascript)

このような記事を書くとき、いろいろと試しながらやるため手順が前後してしまい、わかりづらくて・・m(_ _)m

C言語でも記述できてしまうところが拡張性を考えたとき面白いのと、Rust + TypeScript の組み合わせはなかないいと思いました。

参考動画

Blockchain in Rust

ブロックチェーンのしくみをもっと実装ベースで理解したいといろいろ調べていたところ、まさにそのものである学習用のRustを使ったコンパクトに実装されたコードとそのデモ動画があったため試してみました。(Solanaのような新しいブロックチェーン開発で使うRustとはまた別の話。いづれテスト予定)

https://github.com/GeekLaunch/blockchain-rust

参考書籍: 「ビットコインとブロックチェーン」(NTT出版)

参考動画: (最下部にリンク)

block作成、blockchain作成、トランザクション作成の機能がそれぞれモジュール化されており、main.rsファイルでそれらを利用して自由に作ることができるようになっています。

main.rs

use blockchainlib::*; fn main () { let difficulty = 0x000fffffffffffffffffffffffffffff; let mut genesis_block = Block::new(0, now(), vec![0; 32], vec![ Transaction { inputs: vec![ ], outputs: vec![ transaction::Output { to_addr: "Alice".to_owned(), value: 60, }, transaction::Output { to_addr: "Bob".to_owned(), value: 30, }, ], }, ], difficulty); genesis_block.mine(); let mut last_hash = genesis_block.hash.clone(); let mut blockchain = Blockchain::new(); blockchain.update_with_block(genesis_block).expect("Failed to add genesis block"); println!("{:?}", blockchain); let mut h = last_hash.clone(); let mut block = Block::new(1, now(), last_hash, vec![ Transaction { inputs: vec![ ], outputs: vec![ transaction::Output { to_addr: "Min1".to_owned(), value: 20, }, ], }, Transaction { inputs: vec![ blockchain.blocks[0].transactions[0].outputs[0].clone(), ], outputs: vec![ transaction::Output { to_addr: "Alice".to_owned(), value: 40, }, transaction::Output { to_addr: "Bob".to_owned(), value: 10, }, ], }, ], difficulty); block.mine(); last_hash = block.hash.clone(); blockchain.update_with_block(block).expect("Failed to add block"); println!("{:?}", blockchain); let mut block2 = Block::new(2, now(), last_hash, vec![ Transaction { inputs: vec![ ], outputs: vec![ transaction::Output { to_addr: "Min2".to_owned(), value: 30, }, ], }, Transaction { inputs: vec![ blockchain.blocks[1].transactions[0].outputs[0].clone(), ], outputs: vec![ transaction::Output { to_addr: "Alice".to_owned(), value: 9, }, ], }, ], difficulty); block2.mine(); last_hash = block2.hash.clone(); blockchain.update_with_block(block2).expect("Failed to add block"); println!("{:?}", blockchain); }
Code language: PHP (php)

実行結果

デモとは違い出力内容をかなり変えています。(下記変更)

blockchain.rs

#[derive(Debug)] pub struct Blockchain { pub blocks: Vec<Block>, unspent_outputs: HashSet<Hash>, }
Code language: HTML, XML (xml)
println!("coinbase output value: {:?} total fee: {:?}", coinbase.output_value(), total_fee); if coinbase.output_value() < total_fee { return Err(BlockValidationErr::InvalidCoinbaseTransaction); } else { block_created.extend(coinbase.output_hashes()); }
Code language: PHP (php)

ブロックチェーンを更新する際に、コインの計算が合わないとエラーになります。

最初はジェネシスブロックで、そこに追加していきます。それぞれのブロックの最初のトランザクションはコインベーストランザクション(INPUTなし)で、マイニングに勝ったマイナーによっておかれることを表現しています。unspent_outputs: にあるハッシュを見てみると、消費されたアウトプットは次になくなっていることも確認できます。

ブロックチェーンの基礎を勉強する教材としてとても素晴らしいため、自分用メモとして残しておきたいと思いました。

Plutus / Cardano

前回につづいてカルダノのPlutusスマートコントラクトを今回はHaskellプログラミングのPlayground環境で試してみました。

デモファイルがいくつもあるのですが、支払いのしくみがわかるStarterを選びました。(最後に参考の動画のリンク)

“Compile”->”Simulate”->”Evaluate”ボタンで実行しますが、まだこのコードではValidatorが実装されていないのでエラーがでます。
--validateSpend _myDataValue _myRedeemerValue _ = error () -- Please provide an implementation. validateSpend (MyDatum myDataValue) (MyRedeemer myRedeemerValue) _ = myDataValue == myRedeemerValue

35行目を上記のように変更してもう一度実行します。

成功したので、シミュレーションの結果を見てみます。(12345はパスワード, 30000000ADA送金)

Slot 0, 1, 2とみていきます。

アカウント方式でない、コインの所有者を変更していく、UTXO(ビットコインと同様の未使用トランザクションアウトプット)のしくみが、わかりやすく見ることができます。イーサリアムはUTXOを使っていませんが、後発ゆえにいいとこどりをしている部分でしょうか。

(E)UTXOについて https://iohk.io/jp/blog/posts/2021/04/13/plutus-what-you-need-to-know/

参考動画

コードの細部まで丁寧に説明されており、とてもわかりやすいです。(といっても難解ですが・・)

Playgroundがデフォルトでどこまで実装されているかを確認するために、”HelloWorld”を最初に実行するといいと思います。

Haskellを復習してまたチャレンジしてみたいと思います。(ここまで記録をとっておくと次からやりやすい。Githubアカウントが必要で”Save”で自分のGistに保存してくれる。)

https://decode.red/blog/tag/haskell/

Marlowe / Cardano

Haskellで開発さているカルダノですが、そのHaskellの難易度とは真逆のビジュアルプログラミング環境Marloweがあるのを知り、試してみました。

ブロックチェーンコントラクトの内容なのに、プログラミング教育についてのブログ、http://decode.red/ed/ で扱った方がいいのではと思えるほどのギャップに驚きました。

まるでスクラッチです。それもそのはず、URLからGoogleのBlocklyをベースにしているようです。結合できるプログラミングブロックを制約することによってミスを少なくできるので、セキュリティが要求されるコードに使うことは、ある意味理にかなっているかもしれません。下は、シミュレーション画面。コードが表示されます。

ここでは、下記動画のデモを実行してみました。(挙動をわかりやすくするため数値をかえています。)

以下完成したコード

内容は、Player1,Play2がそれぞれコインを預け入れ、次に両者が別々に入力する数字が一致し、かつPlayer1の数が1なら、Player1が払い戻しをうけ、1以外ならPlayer2が、一致していなかったらもらえない、という一種の賭けです。賭けなので平等な条件にならないといけないのですが、デバッグ表示がわかりづらいので、金額を変えました。ADAというのはカルダノで使われているコインです。

“Start Simulation”ボタンが押されたら、以下の順で画面がすすんでいきます。

Player1,2とも1の場合

Player1,2とも2の場合

Player1が2、Player2が1の場合

slotというのは、プログラムのすすむ時間のようなものと解釈しましたが、何もしないと預けいれたコインがもどされるような仕組みがはいっているようです。(間違っていたらすみません–; )

カルダノネットワークは、イーサリアムネットワークのERC20を変換してもってくることができるようになるということですが、これはとても魅力的に思います。ブロックチェーンの世界はコインも開発環境も個性的で面白いです。

Bitlife Coin

EthereumのERC20 token templateというものを使って、テストネットワーク上のマイコイン(Bitlife Coin)を作ってみました。マイコインをブラウザアドオンのMetaMaskからスマホのMetaMaskに送金します。(Ropstenテストネットワーク)

https://github.com/hackers-live/erc20-token-template

環境) Ubuntu20.04/Docker/Ubuntu20.04/WSL/Windows11

インストール

touch ~/.bash_profile
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
nvm install 11.11.0
nvm use 11.11.0
node -v && npm -v
npm i -g truffle
npm i -g truffle-flattener

最初、WSLのUbuntuで試みましたが、やはり上記バージョンの組み合わせでないとうまくいかないことからdockerで新たに構築しました。
dockerではrootユーザで操作するため以下のコマンドが必要になりました。

npm config set unsafe-perm true

また、開発環境等はいっていないため、gcc, pythonも必要となります。(ネイティブビルドをするため: node-gyp-build)

テンプレートの編集

git clone https://github.com/hackers-live/erc20-token-template.git
cd erc20-token-template/
npm install
vi contracts/MyCoin.sol

// 10 billion contract MyCoin is ERC20Capped(10000000000 ether) { string public name = "Bitlife Coin"; string public symbol = "BLC"; uint8 public decimals = 18; address public CFO; address public CEO;
Code language: PHP (php)

オリジナルの部分のみ変更する。

設定ファイルの作成

.infura_key infuraのプロジェクトID (https://infura.io/ で登録)
.account アカウントアドレス
.secret ブラウザMetaMaskでアカウント作成するときのニーモニックコード

まずローカルでデプロイテストをします。(truffle単体でシミュレートできるのですね。前はGanacheを使った)

truffle develop

コンソールで、

truffle(develop)> migrate

とするとデプロイします。各コマンドを実行してテストします。(テストネットと同じなので以下で説明)

確認ができたら、テストネットにアクセスします。

truffle console –network ropsten

同じくコンソールで、

truffle(ropsten)> migrate

1_initial_migration.js (Total cost:0.00196019 ETH)
2_deploy_coin.js (total cost:0.00945698 ETH)

がデプロイされる。

アカウント確認

truffle(ropsten)> accounts = await web3.eth.getAccounts()

5つ表示され、一番目がMetaMaskと同じアドレス。
マイコインの確認

truffle(ropsten)> token = await MyCoin.deployed()
truffle(ropsten)> token.name()
‘Bitlife Coin’
truffle(ropsten)> token.symbol()
‘BLC’
truffle(ropsten)> token.address
‘0x0eD1cf4ADc9c9E53Dc1581512ebE971a6501028E’

このアドレスをMetaMaskで”Import Tokens”に入力する。

コインを生成できるアカウントの確認

truffle(ropsten)> token.isMinter(accounts[0])
true
truffle(ropsten)> token.isMinter(accounts[1])
false

マイニング

truffle(ropsten)> token.mint(accounts[0], web3.utils.toWei(“1000”, “ether”))

マイニングするごとにブラウザのMetaMaskのBLCコインが増えていくことを確認。
またブラウザのMetaMaskを操作して、スマホのMetaMaskに送金する。スマホ側も新規トークンアドレスをインポートしておく。

GASS代が必要なので送金する前にETHがない場合は、フォーセットから取得。

https://faucet.egorfine.com/


コンソールで金額の確認(BNはBigNumberの略)

truffle(ropsten)> balance = await token.balanceOf(accounts[0])
undefined
truffle(ropsten)> balance
BN {
negative: 0,
words: [ 65011712, 54678630, 666133, <1 empty item> ],
length: 3,
red: null }
truffle(ropsten)> balance.toString()
‘3000000000000000000000’

truffle(ropsten)> supp = await token.totalSupply()
undefined
truffle(ropsten)> supp
BN {
negative: 0,
words: [ 41943040, 28165598, 888178, <1 empty item> ],
length: 3,
red: null }
truffle(ropsten)> supp.toString()
‘4000000000000000000000’

スマホ画面

スマホアプリでETHとならんで表示されているのを見て、ちょっと感動しました。

これはメインネットでも同様にできるのですが、GASS代がかかるのと今は用途がないので、テストネットで十分です。独自通貨の発行ってなんか夢がありますね。

Dapp Tools

ブロックチェーン技術を使った分散型アプリケーションとしてDappとかdAppとかあらわされますが、Ethereum上で動作するDappの開発言語Solidityのテスト環境としてdapptoolsというものを見つけたの試してみました。

https://github.com/dapphub/dapptools

(下記動画で学習中、テストパターンを与える部分がよくわからなかったので確認してみただけになります。ブロックチェーンは様々な種類があり、それぞれ開発環境も豊富です。それが一気に押し寄せてきた感じがあり、とりあえず気になるものからどんどんためして理解を深めようと思っています。)

動画では、Openzeppelinとか使用している部分もありますが、ここではDapp ToolsといいながらDappとは関係ない純粋にSolidity言語による一般ロジックのテストの部分に注目しています。

環境) VSCode + Ubuntu20.04/WSL/Windows11

インストール

curl -L https://nixos.org/nix/install | sh
. “$HOME/.nix-profile/etc/profile.d/nix.sh”
curl https://dapp.tools/install | sh

初期化

mkdir dapp_test2
cd dapp_test2
dapp init
code .

dappコマンドでファイルは自動生成され、以下の内容に編集します。(*.t.sol がテストコード)

Dapptest2.sol

// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.6; contract Dapptest2 { uint8[] internal arr; function remove(uint8 _index) internal{ require(_index < arr.length, "index out of bound"); for(uint i = _index; i < arr.length - 1 ; i++){ arr[i] = arr[i+1]; } arr.pop(); } }
Code language: JavaScript (javascript)

Dapptest2.t.sol

// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.6; import "ds-test/test.sol"; import "./Dapptest2.sol"; contract Dapptest2Test is Dapptest2, DSTest { Dapptest2 test; //event DebugLogEvent(string); uint8[] private copy; function test_remove(uint8[] memory _arr, uint8 _i) public { /* function test_remove() public { uint[] memory _arr = new uint[](4); _arr[0] = 1; _arr[1] = 2; _arr[2] = 3; _arr[3] = 4; uint _i = 2; */ if(_i >= _arr.length){ return; } arr = _arr; // delete copy; for(uint i=0;i<arr.length;i++){ if(i!=_i){ copy.push(arr[i]); } } remove(_i); assertEq(arr.length, copy.length); for(uint i=0;i<arr.length;i++){ assertEq(arr[i], copy[i]); } //emit DebugLogEvent("-- end --"); } function test_length(uint8[] memory _arr, uint8 _i) public{ if(_i >= _arr.length){ return; } assert(_arr.length == _i); } function setUp() public { test = new Dapptest2(); } function testFail_basic_sanity() public { assertTrue(false); } function test_basic_sanity() public { assertTrue(true); } }
Code language: JavaScript (javascript)

動画ではremove()関数のテストの様子がわかりますが、入力の配列とインデックスに何が与えられているのかわかりづらかったため、test_length()という関数を追加して試してみました。

dapp test –fuzz-runs 10

このコマンドを実行しても上記スクリーンショットのように成功するときもあれば、下記のように失敗するときもあります。

失敗したとき[FAIL]のCounterexampleという部分に、配列とインデックスがでてきますが、毎回違うためランダムに入力データが与えられていることが確認できました。(見やすくするためオリジナルのuint256をuint8に変更)

–fuzz-runsを指定しないと100がデフォルトで与えられるようです。

本来の目的のremove()関数のテストでは、リムーブされたことをテストするのが目的のため、配列やインデックスの数はなんでもいいのでチェックする必要はなく、テストしていないため毎回成功することになります。

VSCodeの環境(Solidity機能拡張あり)でSolidityのテストできる環境をためせたことは有益でした。

Cadence / Flow

NFTが得意とされるFlowブロックチェーンの記述言語、Cadence(ケーデンス)を試してみました。(音楽用語のカデンツのことですね)

https://docs.onflow.org/cadence/tutorial/01-first-steps/

CadenceにはPlaygroundが用意されており、これを使ってFlowの特徴であるアカウントごとのストレージについてチュートリアルを実行して理解を深めました。

まずは、単純な文字列を表示するだけのプログラムです。上記コントラクトをデプロイします。左に表示されているのはアカウントで、0x01からアドレスがふられています。これは0x01にプログラムとしてデプロイしたのでコントラクト名Helloがアドレスの下に表示されます。(言語はSwiftに似ている)

次にトランザクションのプログラムを記述して送信します。Transaction Signerを選んで送信しますが、ここではどのSignerを選んでもコントラクトの文字列を表示します。
次は、コントラクト(<-で表現されるものはリソースと呼ぶらしい)をストレージに保存して、他のアカウントからアクセスできないようにします。記述したら上と同様デプロイします。

保存されたリソースが実行できることを確認します。loadしたリソースは戻すか破棄する必要があるようです。次に他のアカウントからアクセスできる方法を考えます。

0x02アカウントのストレージにあるリソースをloadするのではなく、linkを取得します。ためしに0x03アカウントでトランザクションを送信しますがエラーになりまだアクセスできません。

0x02アカウントで送信するともちらん成功しました。

他のアカウントからアクセスするためには、少しトランザクションを変更したものを用意し送信してみます。今度は成功しました。

このような感じで、Playgroundではアカウントの切り替えなど直感的で、Flowのしくみを学ぶことができます。NFTが得意とされているようですので、また次の機会に試してみたいと思います。

参考動画

Lightning Network

ビットコインのマイクロペイメントに対応したオフチェーンネットワークLightning Networkを試してみました。

https://github.com/lightningnetwork/lnd/tree/master/docker

現在のブロックチェーンはスケーラビリティなど今後問題になってくる技術的な課題があり、それを克服するために(メインチェーン/オンチェーンに負担をかけないような)様々な工夫が用意されています。そのアプローチの仕方が各ブロックチェーンごとに個性的(Layer2、クロスチェーンなどなど)で、技術的好奇心が掻き立てられます。ビットコインではオンチェーンとは独自のしくみのオフチェーンでペイメントチャンネルを経由してコインの売買ができるようです。(ノード構築のインセンティブは手数料)

簡単に言えば取引ごとにオンチェーンに書き込むのではなく、まとまった取引の開始(チャンネルを開く)と終了(チャンネルを閉じる)のみとし、取引自体はオフチェーンで実行されることのようです。

ここでは上記サイトのdockerによる実装を試しました。3つのコンソールを開きメインコンソールではbitcoinデーモン、アリス用のコンソールではdockerで実装されたアリスノード、もう一つはボブ用です。アリスからボブへの送金を試みます。ちょっと長いため最後にまとめて配置しました。

ここ最近、にわかですがブロックチェーンの情報収集をしていて思うことは、ちょっと大げさですが人類があらゆる知恵を絞り出してこの分野に取り組んでいるような気がしてしまいます。ビットコインとならぶ存在のイーサリアム、新しい存在の、ポルカドット、カルダノ、ソラナ、コスモス、ニア、フローなど、様々な背景のある人たちが入り混じって取り組んでいます。エンジニアとして感じたことは暗号通貨のチャートにオープンソースのリンクがあることに、なんという時代になってきたんだ、と思ってしまいます。(資金調達・取引のメカニズムがコードになっていて、バーチャルだけでなくリアルの世界で通用してしまっている) またそれぞれのBlockchainには思想があり、それを支持する人の主義主張をきくのも興味深いです。ネットワークの安定維持(ノード構築のインセンティブ)、分散化(特定の人の影響力が大きくならないようなしくみ)、TPSのような性能をバランスよく最大化させるアイディアは、暗号通貨のことだけでなく、次世代の社会インフラを考える際のヒントになります。次世代ブロックチェーンの議論については、現在のしくみを生かして改善する考え方と、新しいしくみを根本から作ってしまう考え方とわかれる傾向にあるのかなと感じます。(Lightning Networkは前者)

かつてパソコンの黎明期国内外様々なメーカが独自規格のハード、OSを作っていましたが、今のブロックチェーンの規格は、なんかそのころに似ている気がしています。(とても活気があってエキサイティング) そこから思うことは、決してエンジニア目線で良いと思ったものが必ずしも強くないことです。WINTELが勝ち取ってきたようにユーザに近いところが充実しているものが強いのかもしれません。

とはいってもSolidity、Cadence、SimplicityといったBlockchainならではのキーワード、従来のキーワードでもあまり表に出てこなかった、Rust、WebAssembly、Haskellがクローズアップされており、反応してしまいます。今回もdockerが多用されていますが、これまでもテストしたWeb3, React / Node などもあわせて最近のフロントエンドからバックエンドまであらゆるものが活用されているのがわかります。こういったことからも世界中のエンジニアが総力戦で取り組んでいる気がして、前の大げさな表現になりました。

Blockchain、面白すぎです。

環境) Ubuntu20.4/WSL/Windows11

Main Console

$ export NETWORK="simnet" $ docker volume create simnet_lnd_alice simnet_lnd_alice $ docker volume create simnet_lnd_bob simnet_lnd_bob $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1dd1039ed38b lnd "./start-lnd.sh" 12 seconds ago Up 11 seconds 9735/tcp, 10009/tcp alice e4fc59546fff btcd "./start-btcd.sh" 15 seconds ago Up 12 seconds 8333-8334/tcp, 18333-18334/tcp, 18555-18556/tcp, 28901-28902/tcp btcd (alice console) $ MINING_ADDRESS=rX6B5SfUFCeCDi8mei55RVctaLh8nJUXEX docker-compose up -d btcd Recreating btcd ... done $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 900848493f15 btcd "./start-btcd.sh" 5 seconds ago Up 4 seconds 8333-8334/tcp, 18333-18334/tcp, 18555-18556/tcp, 28901-28902/tcp btcd 1dd1039ed38b lnd "./start-lnd.sh" 4 minutes ago Up 4 minutes 9735/tcp, 10009/tcp alice $ docker exec -it btcd /start-btcctl.sh generate 400 [ "30513adbb2ea06a2f41ff6e820f28c0541f74add61db7221288695ca0fedfdf0", "63e8712d9c82c85c3420747b9d0c2dcb0830054154f39fbbd69b650d6a08d335", "5d63dd30dba612b8fcb98583439a278b3189240064366c2943fcb5acab7710a4", "5440ecd7403fd4cbafc54cf2463dc970c774b6cc42a8fd7c3aa7bb511a9937c9", "60730b05b5ad1b420ed8423a4ba399cf673e1dde3dbb0158558a92424f28f7f7", "403844d8e249f4977d143dc60ba8bd4954f98e1ef75ec81c2b0650201928cc86", "78ca8280c6a6de6e18122759459a7f385f290c48d99cfee6a5314fc36508cb6f", "413968a4d9a6377b8fe791aa3168778ac2525f162078124a0b25423259afdf9c", "5e4b2583234d35c0669418546593325f08efe450fc469691831aea84c1983ac0", "19d4d6ec5229152e3b95e0b65d4db5ed3c75dcee23a75c9159025813a490b8dd", "77d779970fc355f5f6820710907433570a1e9e47e68262a1f280f5913213bcbd", "781d8f2adc43261023e45562b169a9e3a60d08f68cf858f8ea5afd7e8f804844", "363204521e8f8e36e0a6be1428958c1a2f43a875156e6623fd60612b99588f76", "6ae88c7fbe26730e5590df3785ac6758c999a696fa8b1268b828550bf6727a8b", "1ad55b323913f41fe25d8dd5c284526a446192d43710e3fd4bfe2e1a71920ccd", ... $ docker exec -it btcd /start-btcctl.sh getblockchaininfo | grep -A 1 segwit "segwit": { "status": "active", $ (bob console) $ docker inspect bob | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "", "IPAddress": "172.20.0.4", (alice consolからbob-nodeに接続addressddress) (alice でoepnchannelel) $ docker exec -it btcd /start-btcctl.sh generate 3 [ "04a28809c867e71fa0c70630cf81885fadddafc2a9759588e5d6dfcdea07a424", "6e121d9e71bb4ad2e5f35d14bbf3f00e7a943cd18c87b3a4cc531421308a9fb5", "4c4e90c4e94321fe425d6280535cb26c2068e367577ef3f3acec9507c76bcce6" ] (alice でclosechannelel) $ docker exec -it btcd /start-btcctl.sh generate 3 [ "62dab67d832a50f2d69c3f86e88eea425fae796100751e3554845271542001d9", "7f58bd2257d43be5f4853d73bcf220f2ef6bf65c2a703d6fa9961cd41c81b014", "538ae071300bfa5f437ccd815d737812d4753dd2bd6cf7b0484b4d6d042362e6" ]
Code language: JavaScript (javascript)

Alice Console

$ docker-compose run -d --name alice --volume simnet_lnd_alice:/root/.lnd lnd Creating btcd ... done alice $ docker exec -i -t alice bash bash-5.1# bash-5.1# lncli --network=simnet walletbalance { "total_balance": "0", "confirmed_balance": "0", "unconfirmed_balance": "0", "account_balance": { "default": { "confirmed_balance": "0", "unconfirmed_balance": "0" } } } bash-5.1# lncli --network=simnet channelbalance { "balance": "0", "pending_open_balance": "0", "local_balance": { "sat": "0", "msat": "0" }, "remote_balance": { "sat": "0", "msat": "0" }, "unsettled_local_balance": { "sat": "0", "msat": "0" }, "unsettled_remote_balance": { "sat": "0", "msat": "0" }, "pending_open_local_balance": { "sat": "0", "msat": "0" }, "pending_open_remote_balance": { "sat": "0", "msat": "0" } } bash-5.1# lncli --network=simnet newaddress np2wkh { "address": "rX6B5SfUFCeCDi8mei55RVctaLh8nJUXEX" } (addressをmainin colsoleのMINING_ADDRESSSS.Recreating btcdするとwalletbalanceが変化nceが変化変化-5.1# lncli --network=simnet walletbalance { "total_balance": "1505000000000", "confirmed_balance": "1505000000000", "unconfirmed_balance": "0", "account_balance": { "default": { "confirmed_balance": "1505000000000", "unconfirmed_balance": "0" } } } (main consoleからaddressress,bob consoleからpubkey所得してbob得してbobてbobob nodeに接続接続-5.1# bash-5.1# lncli --network=simnet connect 0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac@172.20.0.4 { } bash-5.1# lncli --network=simnet listpeers { "peers": [ { "pub_key": "0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac", "address": "172.20.0.4:9735", "bytes_sent": "391", "bytes_recv": "391", "sat_sent": "0", "sat_recv": "0", "inbound": false, "ping_time": "0", "sync_type": "ACTIVE_SYNC", "features": { "0": { "name": "data-loss-protect", "is_required": true, "is_known": true }, "5": { "name": "upfront-shutdown-script", "is_required": false, "is_known": true }, "7": { "name": "gossip-queries", "is_required": false, "is_known": true }, "9": { "name": "tlv-onion", "is_required": false, "is_known": true }, "12": { "name": "static-remote-key", "is_required": true, "is_known": true }, "14": { "name": "payment-addr", "is_required": true, "is_known": true }, "17": { "name": "multi-path-payments", "is_required": false, "is_known": true }, "23": { "name": "anchors-zero-fee-htlc-tx", "is_required": false, "is_known": true }, "31": { "name": "amp", "is_required": false, "is_known": true }, "45": { "name": "explicit-commitment-type", "is_required": false, "is_known": true }, "2023": { "name": "script-enforced-lease", "is_required": false, "is_known": true } }, "errors": [ ], "flap_count": 1, "last_flap_ns": "1642158005985969341", "last_ping_payload": null } ] } bash-5.1# bash-5.1# lncli --network=simnet openchannel --node_key=0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac --local_amt=1234567 { "funding_txid": "05b707addfd5aae36fdf62610376e45d4d6859f9e559a5e24957ce173e1a284b" } (main coloseでfundingng transaction) bash-5.1# lncli --network=simnet listchannels { "channels": [ { "active": true, "remote_pubkey": "0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac", "channel_point": "05b707addfd5aae36fdf62610376e45d4d6859f9e559a5e24957ce173e1a284b:0", "chan_id": "887305883680768", "capacity": "1234567", "local_balance": "1231097", "remote_balance": "0", "commit_fee": "3140", "commit_weight": "772", "fee_per_kw": "2500", "unsettled_balance": "0", "total_satoshis_sent": "0", "total_satoshis_received": "0", "num_updates": "0", "pending_htlcs": [ ], "csv_delay": 148, "private": false, "initiator": true, "chan_status_flags": "ChanStatusDefault", "local_chan_reserve_sat": "12345", "remote_chan_reserve_sat": "12345", "static_remote_key": false, "commitment_type": "ANCHORS", "lifetime": "38", "uptime": "38", "close_address": "", "push_amount_sat": "0", "thaw_height": 0, "local_constraints": { "csv_delay": 148, "chan_reserve_sat": "12345", "dust_limit_sat": "354", "max_pending_amt_msat": "1222222000", "min_htlc_msat": "1", "max_accepted_htlcs": 483 }, "remote_constraints": { "csv_delay": 148, "chan_reserve_sat": "12345", "dust_limit_sat": "354", "max_pending_amt_msat": "1222222000", "min_htlc_msat": "1", "max_accepted_htlcs": 483 } } ] } (bob consoleで-5.1# lncli --network=simnet sendpayment --pay_req=lnsb555550n1ps7zkvupp5fd6kx42j5dgd2dc0znmev8et60vqnkcl09nt4al48sjeelf4d76qdqqcqzpgxqyz5vqsp5atu4jx427nlgrwmchg32scz2e3qjdflrxjswcynee34vp3rh3c8s9qyyssqlqfdy6ncp6lm8fs6uyec0yyrqz5aazcum9ehq355x4qpl5c36tj88u5mc636w7aw7n8xjetcgek47hh3u4qp3nprj8k5r6k0cpp837gqusyazn Payment hash: 4b75635552a350d5370f14f7961f2bd3d809db1f7966baf7f53c259cfd356fb4 Description: Amount (in satoshis): 55555 Fee limit (in satoshis): 55555 Destination: 0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac Confirm payment (yes/no): yes +------------+--------------+--------------+--------------+-----+----------+-----------------+-------+ | HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE | +------------+--------------+--------------+--------------+-----+----------+-----------------+-------+ | SUCCEEDED | 0.025 | 0.227 | 55555 | 0 | 852 | 887305883680768 | | +------------+--------------+--------------+--------------+-----+----------+-----------------+-------+ Amount + fee: 55555 + 0 sat Payment hash: 4b75635552a350d5370f14f7961f2bd3d809db1f7966baf7f53c259cfd356fb4 Payment status: SUCCEEDED, preimage: 2cf46eed79d3aa72f942ea8a2eeb4f7aa50c437670d51e9e98d350fd232ba8ab bash-5.1# bash-5.1# lncli --network=simnet channelbalance { "balance": "1175542", "pending_open_balance": "0", "local_balance": { "sat": "1175542", "msat": "1175542000" }, "remote_balance": { "sat": "55555", "msat": "55555000" }, "unsettled_local_balance": { "sat": "0", "msat": "0" }, "unsettled_remote_balance": { "sat": "0", "msat": "0" }, "pending_open_local_balance": { "sat": "0", "msat": "0" }, "pending_open_remote_balance": { "sat": "0", "msat": "0" } } bash-5.1# lncli --network=simnet listchannels { "channels": [ { "active": true, "remote_pubkey": "0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac", "channel_point": "05b707addfd5aae36fdf62610376e45d4d6859f9e559a5e24957ce173e1a284b:0", "chan_id": "887305883680768", "capacity": "1234567", "local_balance": "1175542", "remote_balance": "55555", "commit_fee": "2810", "commit_weight": "1116", "fee_per_kw": "2500", "unsettled_balance": "0", "total_satoshis_sent": "55555", "total_satoshis_received": "0", "num_updates": "2", "pending_htlcs": [ ], "csv_delay": 148, "private": false, "initiator": true, "chan_status_flags": "ChanStatusDefault", "local_chan_reserve_sat": "12345", "remote_chan_reserve_sat": "12345", "static_remote_key": false, "commitment_type": "ANCHORS", "lifetime": "438", "uptime": "438", "close_address": "", "push_amount_sat": "0", "thaw_height": 0, "local_constraints": { "csv_delay": 148, "chan_reserve_sat": "12345", "dust_limit_sat": "354", "max_pending_amt_msat": "1222222000", "min_htlc_msat": "1", "max_accepted_htlcs": 483 }, "remote_constraints": { "csv_delay": 148, "chan_reserve_sat": "12345", "dust_limit_sat": "354", "max_pending_amt_msat": "1222222000", "min_htlc_msat": "1", "max_accepted_htlcs": 483 } } ] } (上記-5.1# lncli --network=simnet closechannel --funding_txid=05b707addfd5aae36fdf62610376e45d4d6859f9e559a5e24957ce173e1a284b --output_index=0 { "closing_txid": "1b8f588256cedc8e31ed5818bdcd1edc5deff96ced9bfebe59acfc3fca8f7629" } (main coloseでfundingng transaction) bash-5.1# lncli --network=simnet walletbalance { "total_balance": "1534999932848", "confirmed_balance": "1534999932848", "unconfirmed_balance": "0", "account_balance": { "default": { "confirmed_balance": "1534999932848", "unconfirmed_balance": "0" } } } bash-5.1#
Code language: PHP (php)

Bob Console

$ docker-compose run -d --name bob --volume simnet_lnd_bob:/root/.lnd lnd Starting btcd ... done bob $ docker exec -i -t bob bash bash-5.1# lncli --network=simnet walletbalance { "total_balance": "2029999990000", "confirmed_balance": "2029999990000", "unconfirmed_balance": "0", "account_balance": { "default": { "confirmed_balance": "2029999990000", "unconfirmed_balance": "0" } } } bash-5.1# lncli --network=simnet channelbalance { "balance": "0", "pending_open_balance": "0", "local_balance": { "sat": "0", "msat": "0" }, "remote_balance": { "sat": "0", "msat": "0" }, "unsettled_local_balance": { "sat": "0", "msat": "0" }, "unsettled_remote_balance": { "sat": "0", "msat": "0" }, "pending_open_local_balance": { "sat": "0", "msat": "0" }, "pending_open_remote_balance": { "sat": "0", "msat": "0" } } bash-5.1# lncli --network=simnet getinfo { "version": "0.14.1-beta commit=v0.14.1-beta-dirty", "commit_hash": "6042004edaaa5b3cad0a0808ff23dba4716f7178", "identity_pubkey": "0204e6918ae3239cc8da262715e5ada2d3540cc9fec6db7d6b72e544e2c77f6cac", "alias": "0204e6918ae3239cc8da", "color": "#3399ff", "num_pending_channels": 0, "num_active_channels": 0, "num_inactive_channels": 0, "num_peers": 0, "block_height": 806, "block_hash": "56e1d3d993da053d4b50cdd08b8101f72d91fac1a42845cef06eebc3a4293d2d", "best_header_timestamp": "1642157626", "synced_to_chain": true, "synced_to_graph": false, "testnet": false, "chains": [ { "chain": "bitcoin", "network": "simnet" } ], "uris": [ ], "features": { "0": { "name": "data-loss-protect", "is_required": true, "is_known": true }, "5": { "name": "upfront-shutdown-script", "is_required": false, "is_known": true }, "7": { "name": "gossip-queries", "is_required": false, "is_known": true }, "9": { "name": "tlv-onion", "is_required": false, "is_known": true }, "12": { "name": "static-remote-key", "is_required": true, "is_known": true }, "14": { "name": "payment-addr", "is_required": true, "is_known": true }, "17": { "name": "multi-path-payments", "is_required": false, "is_known": true }, "23": { "name": "anchors-zero-fee-htlc-tx", "is_required": false, "is_known": true }, "30": { "name": "amp", "is_required": true, "is_known": true }, "31": { "name": "amp", "is_required": false, "is_known": true }, "45": { "name": "explicit-commitment-type", "is_required": false, "is_known": true }, "2023": { "name": "script-enforced-lease", "is_required": false, "is_known": true } } } (pubkey->alice console) bash-5.1# bash-5.1# lncli --network=simnet listpeers { "peers": [ { "pub_key": "035a928e2fdb16f8356735d189028bd2a424f10d6dea8b997b66ac27811c6e996a", "address": "172.20.0.3:44330", "bytes_sent": "391", "bytes_recv": "391", "sat_sent": "0", "sat_recv": "0", "inbound": true, "ping_time": "0", "sync_type": "ACTIVE_SYNC", "features": { "0": { "name": "data-loss-protect", "is_required": true, "is_known": true }, "5": { "name": "upfront-shutdown-script", "is_required": false, "is_known": true }, "7": { "name": "gossip-queries", "is_required": false, "is_known": true }, "9": { "name": "tlv-onion", "is_required": false, "is_known": true }, "12": { "name": "static-remote-key", "is_required": true, "is_known": true }, "14": { "name": "payment-addr", "is_required": true, "is_known": true }, "17": { "name": "multi-path-payments", "is_required": false, "is_known": true }, "23": { "name": "anchors-zero-fee-htlc-tx", "is_required": false, "is_known": true }, "31": { "name": "amp", "is_required": false, "is_known": true }, "45": { "name": "explicit-commitment-type", "is_required": false, "is_known": true }, "2023": { "name": "script-enforced-lease", "is_required": false, "is_known": true } }, "errors": [ ], "flap_count": 1, "last_flap_ns": "1642158005986281830", "last_ping_payload": null } ] } (alice consoleでopenen channel) bash-5.1# bash-5.1# lncli --network=simnet addinvoice --amt=55555 { "r_hash": "4b75635552a350d5370f14f7961f2bd3d809db1f7966baf7f53c259cfd356fb4", "payment_request": "lnsb555550n1ps7zkvupp5fd6kx42j5dgd2dc0znmev8et60vqnkcl09nt4al48sjeelf4d76qdqqcqzpgxqyz5vqsp5atu4jx427nlgrwmchg32scz2e3qjdflrxjswcynee34vp3rh3c8s9qyyssqlqfdy6ncp6lm8fs6uyec0yyrqz5aazcum9ehq355x4qpl5c36tj88u5mc636w7aw7n8xjetcgek47hh3u4qp3nprj8k5r6k0cpp837gqusyazn", "add_index": "1", "payment_addr": "eaf9591aaaf4fe81bb78ba22a8604acc4126a7e334a0ec1279cc6ac0c4778e0f" } (payment_request->alice console.alice console: send payment from alice to bob) bash-5.1# lncli --network=simnet channelbalance { "balance": "55555", "pending_open_balance": "0", "local_balance": { "sat": "55555", "msat": "55555000" }, "remote_balance": { "sat": "1175542", "msat": "1175542000" }, "unsettled_local_balance": { "sat": "0", "msat": "0" }, "unsettled_remote_balance": { "sat": "0", "msat": "0" }, "pending_open_local_balance": { "sat": "0", "msat": "0" }, "pending_open_remote_balance": { "sat": "0", "msat": "0" } } bash-5.1# bash-5.1# lncli --network=simnet walletbalance { "total_balance": "2030000045555", "confirmed_balance": "2030000045555", "unconfirmed_balance": "0", "account_balance": { "default": { "confirmed_balance": "2030000045555", "unconfirmed_balance": "0" } } } bash-5.1#
Code language: PHP (php)

※code中、漢字がまざると文字化けが・・まだこのテーマになれない・・

Web3.js + React.js

ユーザが使うDapps(Decentralized Applications)という形にするにはUIが必要で、Webブラウザを使う方法は代表的な形になるのでしょう。

フロントエンドでよく使われるweb3.js と react.jsを、ここまで作ったしくみに追加してみました。

https://zenn.dev/shunp110/articles/630a0166f468e4

上記、参考にさせていただきました。

環境) Ubuntu20.04/WSL/Windows11

$ node -v
v16.13.1
$ npm -v
8.1.2
$ npx -v
8.1.2
$ npm install web3
$ npm install -g serve
$ npx clear-npx-cache
$ npx create-react-app my-app
$ cd my-app
$ export NODE_OPTIONS=”–max-old-space-size=1024″

src/App.js

import "./App.css"; import Web3 from "web3/dist/web3.min.js" import ABI from "./contracts/Hello.json"; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); const address = "0x482a64967a50076C6F00b0bD859F56514eA07402"; const abi = ABI.abi; const contract = new web3.eth.Contract(abi, address); function App() { const handleCall = async () => { const msg = await contract.methods.get().call(); alert(msg); }; return ( <div className="App"> <h1>web3.js + react.js DEMO</h1> <button onClick={handleCall}>Call</button> </div> ); } export default App;
Code language: JavaScript (javascript)

contracts/Hello.json は下記でコンパイルした結果のbuildディレクトリのものを使用します。

http://bitlife.me/bc/2022/01/08/

また、上記で実行したように、Ganache-cliを立ち上げ、Deployをしたものとします。(conract address使用)

ビルド・実行

$ npm run build
$ serve -s build

rpm startでは、javascript heap out of memoryとなったため、このようにしました。

“scripts”: {
“start”: “react-scripts start”,
“build”: “GENERATE_SOURCEMAP=false react-scripts build”,
“test”: “react-scripts test”,
“eject”: “react-scripts eject”
},

不要かもしれませんが、package.jsonもこのようにしました。(-max-old-spaceも同様、いろいろ試行錯誤した結果なのでのこしておきます)

Call ボタンをクリックすると、上記Alertが表示されました。

何気ない画面ですが、いろんなものが詰まっています。ここまで効率的に開発できるのもやはりフロントエンド、バックエンドのJavaScript環境が強力だからでしょう。この充実ぶりはホットであることの証明ですね。

(web3.js という名前自体が、Web3.0の時代もJSだよね、と)

Ethereum IDE (Remix) & MetaMask

いよいよGUIツールを使って送金やコントラクトの動作確認をしてみました。

ベースとなる環境は前回のGanacheでデフォルトのID二つ使います。

Ganache-cli立ち上げ時に表示されるPrivateKeyをMetaMaskにインポートしてアカウントを作成します。(デフォルトでアカウントはアンロックされている) ネットワークはGanacheをdevelopmentとして追加します。(127.0.0.1:8545にRPC接続。いろいろと調べてチェーンIDは1337とした)

MetaMaskでは、0x827..がAccount3、0xA28…がAccount4で最初は同じ100ETHの画面。

Account3から4に10ETH送金しました。ガス代として0.00042ETHがひかれているのを確認できます。
送金前後[0][1]の差異
アクティビティから詳細が確認できます。

Account4にはアクティビティがありませんでした。

次に、Remixを試してみます。

Solidityのサンプルサイトからカウントをインクリメント、ディクリメントするコードを新規作成し、コンパイル、デプロイデバッグ実行しました。(まず上記コンパイルボタン押す)
デプロイ画面で、ENVIRONMENTでWeb3Providerを選択し、ローカルのGanacheに接続します。この時デフォルトで[0]番目のアカウントが選択されました。デプロイボタンを押すと、下にメニューが追加されます。

自動的に関数のボタンが作成され、デバッグ体制を作ってくれます。

inc,decで値を増減させ、getまたはcount(変数)で数値を表示します。(上記incを一回押し、getを押した様子)

他にもいろいろと動かしみましたが、まだエラー解決に時間がかかります。(ネットの記事、YouTube動画などで動作確認済のはずのものが思い通り動かせないなど・・書籍などでも古めで、2018年くらいの情報が多い。。しかしブロックチェーンの本質はかわらないので、対応していくのみ)

これらのツールを使って効率よく開発をすすめていきたいと思います。(Ganache便利! これもGUI版あるみたいなのでまた)