この記事は nem Advent Calendar 2021 6日目の記事です。
はじめに
開発用コンソールで検証するためのスクリプト置き場です。今まで何度かQiitaでも動作検証のためのスクリプトを紹介してきましたが、今回はほぼすべてのメソッドを検証できる完全版です。
Twitterで「動かない」とツイートしたら秒で私からの解決リプを経験された方がいるかもしれませんが、以下のスクリプトで再現のための検証環境を構築しています。
接続先
- メインネット
- テストネット
Import
gitpage 上のnem2-browserifyからsymbol-sdk-packをページに読み込みます。
(script = document.createElement('script')).src = 'https://xembook.github.io/nem2-browserify/symbol-sdk-pack-1.0.3.js';
document.getElementsByTagName('head')[0].appendChild(script);
Script
NODE = window.origin;
//require
sym = require("/node_modules/symbol-sdk");
op = require("/node_modules/rxjs/operators");
rxjs = require("/node_modules/rxjs");
qr = require("/node_modules/symbol-qr-library");
hd = require("/node_modules/symbol-hd-wallets");
cat = require("/node_modules/catbuffer-typescript");
sha3_256 = require('/node_modules/js-sha3').sha3_256;
sha256 = require('/node_modules/js-sha256').sha256;
//repository
repo = new sym.RepositoryFactoryHttp(NODE);
accountRepo = repo.createAccountRepository();
txRepo = repo.createTransactionRepository();
tsRepo = repo.createTransactionStatusRepository();
receiptRepo = repo.createReceiptRepository();
nwRepo = repo.createNetworkRepository();
blockRepo = repo.createBlockRepository();
chainRepo = repo.createChainRepository();
nodeRepo = repo.createNodeRepository();
finRepo = repo.createFinalizationRepository();
nsRepo = repo.createNamespaceRepository();
metaRepo = repo.createMetadataRepository();
mosaicRepo = repo.createMosaicRepository();
msigRepo = repo.createMultisigRepository();
hlRepo = repo.createHashLockRepository();
slRepo = repo.createSecretLockRepository();
resAccountRepo = repo.createRestrictionAccountRepository();
resMosaicRepo = repo.createRestrictionMosaicRepository();
//properties
(async() =>{
epochAdjustment = await repo.getEpochAdjustment().toPromise();
generationHash = await repo.getGenerationHash().toPromise();
currencyId = (await repo.getCurrencies().toPromise()).currency.mosaicId.toHex();
networkCurrency = (await repo.getCurrencies().toPromise()).currency;
networkType = await repo.getNetworkType().toPromise();
transactionFees = await nwRepo.getTransactionFees().toPromise();
averageFeeMultiplier = transactionFees.averageFeeMultiplier;
medianFeeMultiplier = transactionFees.medianFeeMultiplier;
minFeeMultiplier = transactionFees.minFeeMultiplier;
})();
//service
transactionService = new sym.TransactionService(txRepo, receiptRepo);
stateProofService = new sym.StateProofService(repo);
metaService = new sym.MetadataTransactionService(metaRepo);
mosaicResService = new sym.MosaicRestrictionTransactionService(resMosaicRepo,nsRepo)
nsService = new sym.NamespaceService(nsRepo);
//listener
wsEndpoint = NODE.replace('http', 'ws') + "/ws";
listener = new sym.Listener(wsEndpoint,nsRepo,WebSocket);
リファレンス
例えば、accountRepoについては以下のドキュメントを参考にしてください。
- Module infrastructure/AccountRepository
- Interface AccountRepository
- Class AccountHttp
- Interface AccountRepository
検証スクリプトの重要性
Symbolの衝撃で、トランザクションベースのスマートコントラクトが任意のプログラム言語で構築可能であることは説明しました。得意な言語が別にあったとしても異なる経路の検証手段を持っておくことは重要です。万が一うまく動かない場合に、アプリケーション側に問題があるのかSDKにクセのある仕様があるのか、問題点を切り分けやすくなります。そのためには検証用のスクリプトはより気軽に使える必要があります。
利用例
ブラウザを開き、メインネット・あるいはテストネットのノードにアクセスします。
F12で開発者コンソールを開き、Import、Scriptの順にソースコードをコピペしてEnterキーで実行します。
これで準備完了です。
基本的な使い方はこちらで紹介しています。
@nembear さんに実際の使用手順を動画で紹介していただいてます。
検証スクリプトで宣言した変数名を打ち込むと、使えるメソッドがサジェストされるので大変便利です。
サンプル集
アカウント周り
sym.Account.generateNewAccount(networkType);
> Account {address: Address, keyPair: {…}}
address: Address {address: 'NADOSCNQK4YT2EYUHV4JTYEONRPWNHDK22WG3OY',
privateKey: "9A2ED7D9351B317D220A780AD5C25F8698C4E7451C173CB4F42CEC8ECD0B8***"
publicKey: "D6BC800058D649B9DC22AE47C390D3D0B36F6D08A517367D7A65582E8DCB2B09"
var account =
sym.Account.createFromPrivateKey(
"9A2ED7D9351B317D220A780AD5C25F8698C4E7451C173CB4F42CEC8ECD0B8***",
networkType
)
var address =
sym.Address.createFromRawAddress(
"NADOSCNQK4YT2EYUHV4JTYEONRPWNHDK22WG3OY"
)
mnemonic = hd.MnemonicPassPhrase.createRandom();
mnemonic.plain
subscribe
非同期でsubscribe内に記述された処理を行います。
address = sym.Address.createFromRawAddress(
"NCESRRSDSXQW7LTYWMHZOCXAESNNBNNVXHPB6WY"
)
accountRepo.getAccountInfo(address)
.subscribe(x=>console.log(x))
var address =
sym.Address.createFromRawAddress(
"NADOSCNQK4YT2EYUHV4JTYEONRPWNHDK22WG3OY"
);
var addQR = new qr.AddressQR("alice",address);
addQR.toBase64().subscribe(x => {
(tag= document.createElement('img')).src = x;
document.getElementsByTagName('body')[0].appendChild(tag);
});
await ... toPromise()
awaitの後ろのメソッドが完了するまで、次行へ進まずに処理を待ちます。
//ネットワークプロパティの確認
await repo.networkProperties.toPromise();
//ネットワーク通貨の確認
(await repo.networkCurrencies.toPromise()).currency;
//ネットワーク誕生時の時刻とハッシュ値の確認
await repo.epochAdjustment.toPromise();
await repo.generationHash.toPromise();
//最新ブロック表示
(await blockRepo.search({order: nem.Order.Desc}).toPromise()).data[0]
//直近の承認トランザクション表示
(await txRepo.search({
order: sym.Order.Desc,
group:sym.TransactionGroup.Confirmed
}).toPromise()).data[0]
listener
レポジトリに追加された情報を検知します。
listenerは都度subscribeされるので、 await ... toPromise()に置き換えることはできません。
listener.open().then(() => {
listener.newBlock()
.subscribe(x=>console.log(x));
});
var address =
sym.Address.createFromRawAddress(
"NADOSCNQK4YT2EYUHV4JTYEONRPWNHDK22WG3OY"
);
listener.open().then(() => {
listener.aggregateBondedAdded(address)
.subscribe(x=>console.log(x))
});
service
複数レポジトリにまたがる処理を一括で行います。
account = sym.Account.createFromPrivateKey(
"9A2ED7D9351B317D220A780AD5C25F8698C4E7451C173CB4F42CEC8ECD0B8***",
networkType
);
address = sym.Address.createFromRawAddress(
"NADOSCNQK4YT2EYUHV4JTYEONRPWNHDK22WG3OY"
);
tx = sym.TransferTransaction.create(
sym.Deadline.create(epochAdjustment),
address,
[],
sym.EmptyMessage,
networkType
).setMaxFee(200);
signedTx = account.sign(tx,generationHash);
listener.open().then(() => {
txService.announce(signedTx,listener)
.subscribe(confirmedTx=>{
console.log(confirmedTx)
})
});
alice = sym.Account.createFromPrivateKey(
"9A2ED7D9351B317D220A780AD5C25F8698C4E7451C173CB4F42CEC8ECD***",
networkType
);
bob = sym.Account.generateNewAccount(networkType);
tx = sym.TransferTransaction.create(
sym.Deadline.create(epochAdjustment),
alice.address,
[networkCurrency.createRelative(2)],
sym.PlainMessage.create('test'),
networkType
);
feeTx = sym.TransferTransaction.create(
sym.Deadline.create(epochAdjustment),
bob.address,
[networkCurrency.createRelative(1)],
sym.EmptyMessage,
networkType
);
aggregateArray = [
feeTx.toAggregate(alice.publicAccount),
tx.toAggregate(bob.publicAccount),
]
aggregateTx = sym.AggregateTransaction.createBonded(
sym.Deadline.create(epochAdjustment),
aggregateArray,
networkType,
[],
).setMaxFeeForAggregate(200, 1);
signedAggregateTx = alice.sign(aggregateTx, generationHash);
hashLockTx = sym.HashLockTransaction.create(
sym.Deadline.create(epochAdjustment),
networkCurrency.createRelative(10),
sym.UInt64.fromUint(480),
signedAggregateTx,
networkType
).setMaxFee(200);
signedLockTx = alice.sign(hashLockTx, generationHash);
listener.open().then(() => {
transactionService.announceHashLockAggregateBonded(
signedLockTx,
signedAggregateTx,
listener
).subscribe(aggTx => console.log(aggTx))
});
さいごに
検証スクリプトの使用方法を駆け足で紹介しました。
もし、symbol-sdkがうまく動かないなどありましたら、ツイッターで #symbolsdkama とハッシュタグを入力してツイートしてみてください。先人達が救いの手を差し伸べてくれるかもしれません。
紹介した検証スクリプトは、今後さまざまな技術解説を行うときに引用します。
今回の記事が少し物足りなかったかもしれませんが、より具体的な内容の投稿を予定していますので、ご期待ください。