はじめに
Facebook の仮想通貨(暗号通貨, 暗号資産)である Libra を、Node.js 環境で触ることのできるクライアント libra-core
の使い方をまとめた。
インストール方法は Mac に Facebook の仮想通貨 Libra の Node.js クライアント libra-core をインストールする を参照。
TL;DR
目次
- はじめに
- TL;DR
- 環境・条件
- 詳細
- ライブラリロード
- ウォレット 作成
- ウォレット 復元
- アカウント(アドレス) 作成
- アカウント(アドレス) 復元
- 鋳造(mint)
- 残高の確認
- 送金
- 2回目以降の送金ができない場合
- シーケンス番号の確認
- トランザクションの確認
- まとめ
- 参考文献
環境・条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.5 BuildVersion: 18F132
$ nodenv --version nodenv 1.3.0
$ node --version v12.7.0
$ npm --version 6.10.0
$ npm ls libra-core libra-core@1.0.5 | MIT | deps: 9 | versions: 5
|
詳細
Node.js のコンソール上で検証を行った。
ライブラリロード
Node.js は(2019/08/10 現時点の v12.7.0 では) import/export
を正式サポートしていないため、require
で読み込む必要がある。
参考記事: node.js モジュール(ES Module)のimport/exportにハマる。 - かもメモ
1 2 3
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraClient = libra_core.LibraClient;
|
なお、TypeScript を使うのであれば 公式: Usage 通りに import
を使えば良い。(libra-core
は TypeScript で書かれている。)
1
| import { LibraWallet, LibraClient } from 'libra-core';
|
ウォレット 作成
公式: Creating an Account
new LibraWallet()
で新規ウォレットを作成できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const wallet = new LibraWallet(); console.log(wallet);
|
ウォレット 復元
既存のウォレットを復元したい場合には、mnemonic phrases を渡せば OK。
mnemonic は config.mnemonic
で取得できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const wallet = new LibraWallet(); const mnemonic = wallet.config.mnemonic; const wallet2 = new LibraWallet({mnemonic: mnemonic}) console.log(wallet2)
|
アカウント(アドレス) 作成
LibraWallet.newAccount()
でアカウントを作成できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const wallet = new LibraWallet(); const account = wallet.newAccount(); console.log(account);
|
アカウント作成すると(当然だが) wallet.accounts
に内容が追加されている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| console.log(wallet)
|
アカウント(アドレス) 復元
LibraAccount.fromSecretKey()
でアカウントの復元。引数として、秘密鍵を Hex String で渡す必要がある。
秘密鍵は LibraAccount.keyPair.getSecretKey()
で取得できるので、Buffer.from().toString("hex")
と組み合わせることで Hex String に変換できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account;
const wallet = new LibraWallet(); const account = wallet.newAccount(); const secretKey = account.keyPair.getSecretKey() console.log(secretKey)
const secretKeyHexStr = Buffer.from(secretKey).toString("hex") console.log(secretKeyHexStr)
const account2 = LibraAccount.fromSecretKey(secretKeyHexStr) console.log(account2)
console.log(account.getAddress().toString())
console.log(account2.getAddress().toString())
|
LibraWallet.addAccount()
で、復元したアカウントをウォレットに追加できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| wallet2.addAccount(account2) console.log(wallet2) LibraWallet { lastChild: 0, accounts: { '8b45xxxx3bd5': Account { keyPair: [KeyPair], address: [AccountAddress] } }, config: { mnemonic: 'pitch peasant (略) drive fly' }, keyFactory: KeyFactory { seed: Seed { data: <Buffer 75 11 (略) 8f a2> }, hkdf: Hkdf { hashAlgorithm: 'sha3-256' }, masterPrk: Uint8Array [ 53, 69, (略), 180, 104 ] } }
|
鋳造(mint)
公式: Minting Amount
LibraClient.mintWithFaucetService()
で Testnet で遊ぶための Libra Coin を鋳造できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account; const LibraClient = libra_core.LibraClient; const LibraNetwork = libra_core.LibraNetwork;
async function mint(address, amount=20e6) { const client = new LibraClient({ network: LibraNetwork.Testnet });
await client.mintWithFaucetService(address, amount); }
const wallet = new LibraWallet(); const account = wallet.newAccount();
mint(account.getAddress(), 20e6); console.log(`https://www.libravista.com/address/${account.getAddress().toString()}`)
|
LibraVista で残高などを簡単に確認できるので、鋳造できているかを確認してみると良い。
残高の確認
公式: Checking an address balance
LibraClient.getAccountState()
で、アカウントの状態(accountState
)を取得できる。accountState.balance
に残高情報が入っている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account; const LibraClient = libra_core.LibraClient; const LibraNetwork = libra_core.LibraNetwork;
async function checkBalance(accountAddress) { const client = new LibraClient({ network: LibraNetwork.Testnet }); const accountState = await client.getAccountState(accountAddress); console.log(accountState.balance.toString()); }
const wallet = new LibraWallet(); const account = wallet.newAccount();
checkBalance(account.getAddress());
|
送金
公式: Transfering Libra Coins
LibraClient.transferCoins()
で送金。送金元アカウント、送金先アドレス、送金額を引数として渡す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account; const LibraClient = libra_core.LibraClient; const LibraNetwork = libra_core.LibraNetwork;
async function send(srcAccount, destAddress, amount) { const client = new LibraClient({ network: LibraNetwork.Testnet }); const response = await client.transferCoins(srcAccount, destAddress, amount);
await response.awaitConfirmation(client); }
const wallet = new LibraWallet(); const account = wallet.newAccount(); const account2 = wallet.newAccount();
send(account, account2.getAddress().toString(), 1e5); console.log(`https://www.libravista.com/address/${account.getAddress().toString()}`)
|
同じように LibraVista で確認する。
2回目以降の送金ができない場合
本問題は v1.0.6 で解消済み。
libra-core v1.0.5 にはバグがあるため、2回目以降の送金を行うには Pull Request #33 の内容を取り込む必要がある。
Pull Request の内容を参考に以下の2ファイルを修正することで、2回目以降の送金ができるようになる。
node_modules/libra-core/build/common/CursorBuffer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| diff --git a/node_modules/libra-core/build/common/CursorBuffer.js b/node_modules/libra-core/build/common/CursorBuffer.js index 21590ac..9587bb6 100644
@@ -52,5 +52,18 @@ class CursorBuffer { this.bytePositon += x; return value; } + + /** + * Read bool as 1 byte + * + */ + readBool() { + const value = this.dataView.getUint8(this.bytePositon); + this.bytePositon += 1; + if(value !== 0 && value !== 1) { + throw new Error(`bool must be 0 or 1, found ${value}`); + } + return value !== 0; + } } exports.CursorBuffer = CursorBuffer;
|
node_modules/libra-core/build/wallet/Accounts.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| diff --git a/node_modules/libra-core/build/wallet/Accounts.js b/node_modules/libra-core/build/wallet/Accounts.js index 51e74ea..258a036 100644
@@ -19,24 +19,26 @@ class AccountState { * Returns an empty AccountState */ static default(address) { - return new AccountState(new Uint8Array(Buffer.from(address, 'hex')), new bignumber_js_1.default(0), new bignumber_js_1.default(0), new bignumber_js_1.default(0), new bignumber_js_1.default(0)); + return new AccountState(new Uint8Array(Buffer.from(address, 'hex')), new bignumber_js_1.default(0), new bignumber_js_1.default(0), new bignumber_js_1.default(0), new bignumber_js_1.default(0), true); } static fromBytes(bytes) { const cursor = new CursorBuffer_1.CursorBuffer(bytes); const authenticationKeyLen = cursor.read32(); const authenticationKey = cursor.readXBytes(authenticationKeyLen); const balance = cursor.read64(); + const delegatedWithdrawalCapability = cursor.readBool(); const receivedEventsCount = cursor.read64(); const sentEventsCount = cursor.read64(); const sequenceNumber = cursor.read64(); - return new AccountState(authenticationKey, balance, receivedEventsCount, sentEventsCount, sequenceNumber); + return new AccountState(authenticationKey, balance, receivedEventsCount, sentEventsCount, sequenceNumber, delegatedWithdrawalCapability); } - constructor(authenticationKey, balance, receivedEventsCount, sentEventsCount, sequenceNumber) { + constructor(authenticationKey, balance, receivedEventsCount, sentEventsCount, sequenceNumber, delegatedWithdrawalCapability) { this.balance = balance; this.sequenceNumber = sequenceNumber; this.authenticationKey = authenticationKey; this.sentEventsCount = sentEventsCount; this.receivedEventsCount = receivedEventsCount; + this.delegatedWithdrawalCapability = delegatedWithdrawalCapability; } } exports.AccountState = AccountState;
|
シーケンス番号の確認
残高取得 でも使用した LibraClient.getAccountState()
の戻り値 accountState
内に現在のシーケンス番号(次に使用されるシーケンス番号)が入っており、accountState.sequenceNumber
で確認できる。
※Libra は送金を行うごとにシーケンス番号が 1ずつ増えていく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account; const LibraClient = libra_core.LibraClient; const LibraNetwork = libra_core.LibraNetwork;
async function checkSequenceNumber(address) { const client = new LibraClient({ network: LibraNetwork.Testnet }); const accountState = await client.getAccountState(address); console.log(accountState.sequenceNumber.toString()); }
const wallet = new LibraWallet(); const account = wallet.newAccount();
checkSequenceNumber(account.getAddress());
|
トランザクションの確認
公式: Query Transaction with Sequence Number
LibraClient.getAccountTransaction()
でトランザクションの確認。対象のアドレスと、シーケンス番号を引数として渡す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| const libra_core = require("libra-core"); const LibraWallet = libra_core.LibraWallet; const LibraAccount = libra_core.Account; const LibraClient = libra_core.LibraClient; const LibraNetwork = libra_core.LibraNetwork;
async function checkTransaction(address, seq) { const client = new LibraClient({ network: LibraNetwork.Testnet }); const transaction = await client.getAccountTransaction(address, seq); console.log(transaction); }
const wallet = new LibraWallet(); const account = wallet.newAccount();
checkTransaction(account.getAddress(), 0);
|
まとめ
参考文献
関連記事