アグリゲートコンプリートトランザクションに連署名を追加する

このガイドでは、パーシャルキャッシュを使用せずに、アグリゲートコンプリートトランザクションに連署名を追加する方法を説明します。

この方法は、セキュリティ上の理由で、秘密鍵をオフラインデバイス (コールドウォレット) に保管している場合に有用です。オフラインで連署名を追加することで、連署者は秘密鍵の安全を保ったまま、コールドウォレットからトランザクションを実行できます。さらに aggregate bonded transactions による、意図しない資産ロックを回避することもできます。

ユースケース

../../_images/aggregate-escrow-11.png

マルチアセットエスクロートランザクション

Alice (TDPXWF2H5G7U2NKZRJD47QR4KZPRULPAMQ4O54IK) は 100 symbol.xym を Bob (TCHS3AOXFGWGTN2QUUHDCXJ4SBYLIQIPNUPHHA2N) に送信し、1 collectible モザイクを返却する、アグリゲートコンプリートトランザクションを構築していきます。

前提条件

  • 2 つのアカウント アカウント
  • アカウントに手数料を支払うために十分な symbol.xym を入金してください。
  • アカウントのうち 1 つは symbol.xym 以外のモザイクを所有している必要があります。
  • オフラインとオンラインの両方で ワークステーション は Symbol-CLI と Symbol-SDK 用に設定されています。

ステップ 1: アグリゲートコンプリートトランザクションの構築

  1. テキストエディタを開きます。そして、Alice のアカウントを使用して、アグリゲートコンプリートトランザクションを構築します。
const networkType = NetworkType.TEST_NET;

// replace with alice private key
const alicePrivatekey = '';
const aliceAccount = Account.createFromPrivateKey(alicePrivatekey, networkType);

// replace with bob public key
const bobPublicKey = '';
const bobPublicAccount = PublicAccount.createFromPublicKey(
  bobPublicKey,
  networkType,
);

const aliceTransferTransaction = TransferTransaction.create(
  Deadline.create(epochAdjustment),
  bobPublicAccount.address,
  [NetworkCurrencies.PUBLIC.currency.createRelative(1000)],
  PlainMessage.create('payout'),
  networkType,
);

const bobTransferTransaction = TransferTransaction.create(
  Deadline.create(epochAdjustment),
  aliceAccount.address,
  [new Mosaic(new NamespaceId('collectible'), UInt64.fromUint(1))],
  PlainMessage.create('payout'),
  networkType,
);

const aggregateTransaction = AggregateTransaction.createComplete(
  Deadline.create(epochAdjustment),
  [
    aliceTransferTransaction.toAggregate(aliceAccount.publicAccount),
    bobTransferTransaction.toAggregate(bobPublicAccount),
  ],
  networkType,
  [],
  UInt64.fromUint(2000000),
);
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with alice private key
const alicePrivatekey = '';
const aliceAccount = symbol_sdk_1.Account.createFromPrivateKey(
  alicePrivatekey,
  networkType,
);
// replace with bob public key
const bobPublicKey = '';
const bobPublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
  bobPublicKey,
  networkType,
);
const aliceTransferTransaction = symbol_sdk_1.TransferTransaction.create(
  symbol_sdk_1.Deadline.create(epochAdjustment),
  bobPublicAccount.address,
  [symbol_sdk_1.NetworkCurrencies.PUBLIC.currency.createRelative(1000)],
  symbol_sdk_1.PlainMessage.create('payout'),
  networkType,
);
const bobTransferTransaction = symbol_sdk_1.TransferTransaction.create(
  symbol_sdk_1.Deadline.create(epochAdjustment),
  aliceAccount.address,
  [
    new symbol_sdk_1.Mosaic(
      new symbol_sdk_1.NamespaceId('collectible'),
      symbol_sdk_1.UInt64.fromUint(1),
    ),
  ],
  symbol_sdk_1.PlainMessage.create('payout'),
  networkType,
);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createComplete(
  symbol_sdk_1.Deadline.create(epochAdjustment),
  [
    aliceTransferTransaction.toAggregate(aliceAccount.publicAccount),
    bobTransferTransaction.toAggregate(bobPublicAccount),
  ],
  networkType,
  [],
  symbol_sdk_1.UInt64.fromUint(2000000),
);
            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture()
                .get();

            // replace with alice private key
            String alicePrivatekey = "";
            Account aliceAccount = Account.createFromPrivateKey(alicePrivatekey, networkType);

            // replace with bob public key
            String bobPublicKey = "";
            PublicAccount bobPublicAccount = PublicAccount
                .createFromPublicKey(bobPublicKey, networkType);

            TransferTransaction aliceTransferTransaction = TransferTransactionFactory
                .create(networkType, bobPublicAccount.getAddress(),
                    Collections
                        .singletonList(networkCurrency.createRelative(BigInteger.valueOf(1000))),
                    PlainMessage.create("payout")).build();

            TransferTransaction bobTransferTransaction = TransferTransactionFactory
                .create(networkType, aliceAccount.getAddress(),
                    Collections.singletonList(
                        new Mosaic(new NamespaceId("collectible"), BigInteger.valueOf(1))),
                    PlainMessage.create("payout")).build();

            AggregateTransaction aggregateTransaction = AggregateTransactionFactory
                .createComplete(networkType, Arrays
                    .asList(aliceTransferTransaction.toAggregate(aliceAccount.getPublicAccount()),
                        bobTransferTransaction.toAggregate(bobPublicAccount)))
                .maxFee(BigInteger.valueOf(2000000)).build();

Alice の秘密鍵と Bob の公開鍵が適切に設定されていることを確認してください。

  1. Alice のキーでトランザクションに署名します。
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const generationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';

const signedTransactionNotComplete = aliceAccount.sign(
  aggregateTransaction,
  generationHash,
);
console.log(signedTransactionNotComplete.payload);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const generationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransactionNotComplete = aliceAccount.sign(
  aggregateTransaction,
  generationHash,
);
console.log(signedTransactionNotComplete.payload);
            String generationHash = repositoryFactory.getGenerationHash().toFuture().get();

            SignedTransaction signedTransactionNotComplete = aliceAccount
                .sign(aggregateTransaction, generationHash);
            System.out.println(signedTransactionNotComplete.getPayload());

3. Save the TypeScript file, then run it on your terminal. Copy the returned payload and send it over to Bob.

ステップ 2: 二つ目の連署名を追加

  1. Bob は前回のステップで取得したペイロードに連署名します。
// replace with bob private key
const bobPrivateKey = '';
const bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
const cosignedTransactionBob = CosignatureTransaction.signTransactionPayload(
  bobAccount,
  signedTransactionNotComplete.payload,
  generationHash,
);
console.log(cosignedTransactionBob.signature);
console.log(cosignedTransactionBob.parentHash);
// replace with bob private key
const bobPrivateKey = '';
const bobAccount = symbol_sdk_1.Account.createFromPrivateKey(
  bobPrivateKey,
  networkType,
);
const cosignedTransactionBob = symbol_sdk_1.CosignatureTransaction.signTransactionPayload(
  bobAccount,
  signedTransactionNotComplete.payload,
  generationHash,
);
console.log(cosignedTransactionBob.signature);
console.log(cosignedTransactionBob.parentHash);
            // replace with bob private key
            String bobPrivateKey = "";
            Account bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
            CosignatureSignedTransaction cosignedTransactionBob = CosignatureTransaction
                .create(aggregateTransaction)
                .signWith(bobAccount);

            System.out.println(cosignedTransactionBob.getSignature());
            System.out.println(cosignedTransactionBob.getParentHash());

2. Bob runs the code snippet in the terminal and obtains the transaction signature and the parent hash. Finally, he shares the information back with Alice.

ステップ 3: アグリゲートコンプリートトランザクションのアナウンス

Bob の公開鍵、連署トランザクションハッシュ、署名を使用してトランザクションを再作成し、コンプリートとしてネットワークへアナウンスします。

const cosignatureSignedTransactions = [
  new CosignatureSignedTransaction(
    cosignedTransactionBob.parentHash,
    cosignedTransactionBob.signature,
    cosignedTransactionBob.signerPublicKey,
  ),
];
const rectreatedAggregateTransactionFromPayload = TransactionMapping.createFromPayload(
  signedTransactionNotComplete.payload,
) as AggregateTransaction;

const signedTransactionComplete = aliceAccount.signTransactionGivenSignatures(
  rectreatedAggregateTransactionFromPayload,
  cosignatureSignedTransactions,
  generationHash,
);
console.log(signedTransactionComplete.hash);

// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();

transactionHttp.announce(signedTransactionComplete).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
const cosignatureSignedTransactions = [
  new symbol_sdk_1.CosignatureSignedTransaction(
    cosignedTransactionBob.parentHash,
    cosignedTransactionBob.signature,
    cosignedTransactionBob.signerPublicKey,
  ),
];
const rectreatedAggregateTransactionFromPayload = symbol_sdk_1.TransactionMapping.createFromPayload(
  signedTransactionNotComplete.payload,
);
const signedTransactionComplete = aliceAccount.signTransactionGivenSignatures(
  rectreatedAggregateTransactionFromPayload,
  cosignatureSignedTransactions,
  generationHash,
);
console.log(signedTransactionComplete.hash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransactionComplete).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
            BinarySerialization serialization = BinarySerializationImpl.INSTANCE;

            AggregateTransactionFactory rectreatedAggregateTransactionFromPayload = (AggregateTransactionFactory) serialization
                .deserializeToFactory(
                    ConvertUtils.getBytes(signedTransactionNotComplete.getPayload()));

            //Added a new cosignature.
            rectreatedAggregateTransactionFromPayload.addCosignatures(cosignedTransactionBob);

            SignedTransaction signedTransactionComplete = aliceAccount
                .sign(rectreatedAggregateTransactionFromPayload.build(), generationHash);
            System.out.println(signedTransactionComplete.getHash());

            TransactionRepository transactionHttp = repositoryFactory.createTransactionRepository();

            transactionHttp.announce(signedTransactionComplete).toFuture().get();

成功すれば、Alice は 100 symbol.xym を Bob へ送信し、1 collectible モザイクを返却します。