はじめに
こんにちは。
symbol-sdkの3系、トランザクションシリーズ第七弾です。
今回はシークレットロック・シークレットプルーフをやっていきます。
注意事項
書いてあるコードについて、正確性や完全性を保証するものではありません。あくまで参考程度として頂き、最新情報は公式ドキュメンテーションをご確認ください。
デッドラインと鍵ペア
まずはデッドラインと鍵ペアです。
import symbolSdk from 'symbol-sdk';
const network = symbolSdk.symbol.Network.TESTNET;
const deadline = network.fromDatetime(new Date(Date.now() + 7200000)).timestamp;
const facade = new symbolSdk.facade.SymbolFacade(network.name);
const privateKey = new symbolSdk.PrivateKey(PRIVATE_KEY);
const keyPair = new facade.constructor.KeyPair(privateKey);
TextEncoderとハッシュ関数
文字列をUint8Arrayに変換するので、 TextEncoder
をインポートします。
import { TextEncoder } from 'util';
const textEncoder = new TextEncoder();
ハッシュ関数ライブラリをインポートします。 symbol-sdk
の依存ライブラリでもあるので、追加でインストールしなくても使えます。
import { sha3_256 } from '@noble/hashes/sha3';
import { sha256 } from '@noble/hashes/sha256';
import { ripemd160 } from '@noble/hashes/ripemd160';
プルーフ
プルーフを先に作ります。文字列です。
const proof = `secret${Date.now()}`;
ハッシュアルゴリズム
使用するアルゴリズムに合わせて、 LockHashAlgorithm
を選択します。
const hashAlgorithm = symbolSdk.symbol.LockHashAlgorithm.SHA3_256;
const hashAlgorithm = symbolSdk.symbol.LockHashAlgorithm.HASH_160;
const hashAlgorithm = symbolSdk.symbol.LockHashAlgorithm.HASH_256;
シークレット
各種アルゴリズムに合わせてシークレットを作成します。
SHA3_256
SHA3_256
ではこのような形になります。この secretBuffer
は Uint8Array
型となります。
const secretBuffer = sha3_256.create().update(textEncoder.encode(proof)).digest();
トランザクションに取り込むときに様々な型が使用できます。
やってみた感じだと、Uint8Array
型そのままか、16進数文字列が使用できました。
const secret = secretBuffer;
const secret = symbolSdk.utils.uint8ToHex(secret);
HASH_160
HASH_160
ですと、SHA256でハッシュした後、RIPEMD160でハッシュします。ハッシュの出力が20バイトになるので、0で埋めて32バイトにしなければなりません。
16進数文字列で扱うのが慣れているので、文字列で0埋めしました。
const secret1 = sha256.create().update(textEncoder.encode(proof)).digest();
const secret2 = ripemd160.create().update(secret1).digest();
const secret = symbolSdk.utils.uint8ToHex(secret2).padEnd(64, "0");
HASH_256
HASH_256
ですと、SHA256で2回ハッシュします。
const secret1 = sha256.create().update(textEncoder.encode(proof)).digest();
const secretBuffer = sha256.create().update(secret1).digest();
トランザクションに取り込むときに様々な型が使用できます。
const secret = secretBuffer;
const secret = new Uint8Array(secretBuffer);
const secret = symbolSdk.utils.uint8ToHex(secretBuffer);
ロック期間
ブロック数でシークレットロックの有効期限を指定します。BigInt型か BlockDuration
型が使えます。
const duration = 86400n;
const duration = new symbolSdk.symbol.BlockDuration(86400n);
シークレットロックトランザクション
それではシークレットロックトランザクションを作ります。
上記で作成したパラメータと共に、 recipientAddress
と mosaic
を指定します。
const transaction = facade.transactionFactory.create({
type: 'secret_lock_transaction_v1',
signerPublicKey: keyPair.publicKey.toString(),
fee: 1000000n,
deadline,
recipientAddress: ADDRESS_RECEIVER_1,
secret,
mosaic: { mosaicId: 0xE74B99BA41F4AFEEn, amount: 1000000n },
duration,
hashAlgorithm,
});
署名と送信
署名して送信します。
const signature = facade.signTransaction(keyPair, transaction);
const jsonPayload = facade.transactionFactory.constructor.attachSignature(transaction, signature);
const hash = facade.hashTransaction(transaction).toString();
console.log(hash);
const sendRes = await axios.put(`${NODE_URL}/transactions`, jsonPayload).then((res) => res.data);
console.log(sendRes);
承認されるのを待つ
この次にシークレットプルーフトランザクションを送信したいので、ここでシークレットロックトランザクションの承認を待ちます。
while (true) {
await new Promise((resolve) => setTimeout(resolve, 5000));
const statusRes = await axios.get(`${NODE_URL}/transactionStatus/${hash}`).then((res) => res.data);
console.log(statusRes);
if (statusRes.group === 'confirmed') {
break;
}
if (statusRes.group !== 'unconfirmed') {
throw new Error();
}
}
シークレットプルーフトランザクション
シークレットロックトランザクションが承認されたので、シークレットプルーフトランザクションを作成していきます。
const transaction = facade.transactionFactory.create({
type: 'secret_proof_transaction_v1',
signerPublicKey: keyPair.publicKey.toString(),
fee: 1000000n,
deadline,
recipientAddress: ADDRESS_RECEIVER_1,
secret: secret,
hashAlgorithm,
proof: proof,
});
署名と送信とステータス確認
署名して送信してステータスを確認します。
const signature = facade.signTransaction(keyPair, transaction);
const jsonPayload = facade.transactionFactory.constructor.attachSignature(transaction, signature);
const hash = facade.hashTransaction(transaction).toString();
const sendRes = await node.put("/transactions", jsonPayload).then((res) => res.data);
console.log(sendRes);
await new Promise((resolve) => setTimeout(resolve, 1000));
const statusRes = await node.get("/transactionStatus/" + hash).then((res) => res.data);
console.log(statusRes);
おわりに
今回は、シークレットロックトランザクションとシークレットプルーフトランザクションをやっていきました。記事の都合上、2つのトランザクションをいっぺんに作りましたが、アプリへの実装の際には別々に使うことがほとんどだと思います。
シリーズ