自動的なアグリゲートボンデッドトランザクションの署名

アカウントの署名が必要なトランザクションへ自動的に署名をするボットを作成します。

前提条件

ボットの開発

  1. AggregateBondedTransaction を共同署名する関数を作成します。
const cosignAggregateBondedTransaction = (
  transaction: AggregateTransaction,
  account: Account,
): CosignatureSignedTransaction => {
  const cosignatureTransaction = CosignatureTransaction.create(transaction);
  return account.signCosignatureTransaction(cosignatureTransaction);
};
const cosignAggregateBondedTransaction = (transaction, account) => {
  const cosignatureTransaction = symbol_sdk_1.CosignatureTransaction.create(
    transaction,
  );
  return account.signCosignatureTransaction(cosignatureTransaction);
};
            BiFunction<AggregateTransaction, Account, CosignatureSignedTransaction> cosignAggregateBondedTransaction = ((transaction, account) -> CosignatureTransaction
                .create(transaction).signWith(account));
  1. あなたのアカウントの署名が必要な、新しい AggregateBondedTransaction を通知する Listener を作ります。
  2. コネクションを開きます。一度コネクションが張られれば、望むすべてのチャンネルへつながるでしょう。
  3. あなたのアカウントで aggregateBondedAdded チャンネルを購読して、新しいトランザクションの監視を始めます。

注釈

マルチシグの連署者による署名が必要なアグリゲートボンドトランザクションへ自動的に署名するには、代わりにマルチシグアドレスを参照してください。アカウントが連署者になっているマルチシグアカウントの取得の仕方 を参照してください。

5. For each received transaction, check if you have signed it. At this point, you might want to do some extra checks, like verifying the contents of the transaction. Cosign each pending AggregateBondedTransaction using the previously created function.

  1. TransactionHttp リポジトリを使って CosignatureSignedTransaction をネットワークへアナウンスします。
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = Account.createFromPrivateKey(privateKey, networkType);
// 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();
const listener = repositoryFactory.createListener();

listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      filter((_) => !_.signedByAccount(account.publicAccount)),
      map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      mergeMap((signedCosignatureTransaction) => {
        listener.close();
        return transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        );
      }),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = symbol_sdk_1.Account.createFromPrivateKey(
  privateKey,
  networkType,
);
// 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();
const listener = repositoryFactory.createListener();
listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      operators_1.filter((_) => !_.signedByAccount(account.publicAccount)),
      operators_1.map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      operators_1.mergeMap((signedCosignatureTransaction) => {
        listener.close();
        return transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        );
      }),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            // replace with cosigner private key
            String privateKey = "";
            Account account = Account.createFromPrivateKey(privateKey, networkType);

            TransactionRepository transactionRepository = repositoryFactory.createTransactionRepository();

            try (Listener listener = repositoryFactory.createListener()) {
                listener.open().get();
                listener.aggregateBondedAdded(account.getAddress())
                    .filter(a -> a.signedByAccount(account.getPublicAccount()))
                    .map(a -> cosignAggregateBondedTransaction.apply(a, account))
                    .flatMap(transactionRepository::announceAggregateBondedCosignature).toFuture().get();
            }

次は?

トランザクションがいくつかの制約に従う場合において、前の関数を拡張してトランザクションを署名します。たとえば、次の条件に一致するアグリゲートトランザクションのみに署名するようにボットを適合させます:

  • このアグリゲートは 2つのインナートランザクション を持っています。
  • このインナートランザクションは 転送トランザクション でなければなりません。
  • この資産を送信するトランザクションは あなたが署名者 である必要があります。
  • 資産を送信するトランザクションは 100 symbol.xym 未満の 1つのモザイクだけ を持つようにしてください。

こちらに可能な実装をご用意しました:

const validTransaction = (
  transaction: Transaction,
  publicAccount: PublicAccount,
): boolean => {
  return (
    transaction instanceof TransferTransaction &&
    transaction.signer!.equals(publicAccount) &&
    transaction.mosaics.length === 1 &&
    transaction.mosaics[0].id.equals(
      new MosaicId('5E62990DCAC5BE8A') ||
        transaction.mosaics[0].id.equals(new NamespaceId('symbol.xym')),
    ) &&
    transaction.mosaics[0].amount.compare(
      UInt64.fromUint(100 * Math.pow(10, 6)),
    ) < 0
  );
};

const cosignAggregateBondedTransaction = (
  transaction: AggregateTransaction,
  account: Account,
): CosignatureSignedTransaction => {
  const cosignatureTransaction = CosignatureTransaction.create(transaction);
  return account.signCosignatureTransaction(cosignatureTransaction);
};

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = Account.createFromPrivateKey(privateKey, networkType);
// 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();
const listener = repositoryFactory.createListener();

listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      filter((_) => _.innerTransactions.length === 2),
      filter((_) => !_.signedByAccount(account.publicAccount)),
      filter(
        (_) =>
          validTransaction(_.innerTransactions[0], account.publicAccount) ||
          validTransaction(_.innerTransactions[1], account.publicAccount),
      ),
      map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      mergeMap((signedCosignatureTransaction) =>
        transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        ),
      ),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
const validTransaction = (transaction, publicAccount) => {
  return (
    transaction instanceof symbol_sdk_1.TransferTransaction &&
    transaction.signer.equals(publicAccount) &&
    transaction.mosaics.length === 1 &&
    transaction.mosaics[0].id.equals(
      new symbol_sdk_1.MosaicId('5E62990DCAC5BE8A') ||
        transaction.mosaics[0].id.equals(
          new symbol_sdk_1.NamespaceId('symbol.xym'),
        ),
    ) &&
    transaction.mosaics[0].amount.compare(
      symbol_sdk_1.UInt64.fromUint(100 * Math.pow(10, 6)),
    ) < 0
  );
};
const cosignAggregateBondedTransaction = (transaction, account) => {
  const cosignatureTransaction = symbol_sdk_1.CosignatureTransaction.create(
    transaction,
  );
  return account.signCosignatureTransaction(cosignatureTransaction);
};
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = symbol_sdk_1.Account.createFromPrivateKey(
  privateKey,
  networkType,
);
// 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();
const listener = repositoryFactory.createListener();
listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      operators_1.filter((_) => _.innerTransactions.length === 2),
      operators_1.filter((_) => !_.signedByAccount(account.publicAccount)),
      operators_1.filter(
        (_) =>
          validTransaction(_.innerTransactions[0], account.publicAccount) ||
          validTransaction(_.innerTransactions[1], account.publicAccount),
      ),
      operators_1.map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      operators_1.mergeMap((signedCosignatureTransaction) =>
        transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        ),
      ),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
            NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture().get();

            BiFunction<Transaction, PublicAccount, Boolean> validTransaction = ((transaction, account) -> {
                if (transaction instanceof TransferTransaction) {
                    return false;
                }
                if (transaction.getSigner().map(s -> !s.equals(account)).orElse(true)) {
                    return false;
                }

                TransferTransaction transferTransaction = (TransferTransaction) transaction;
                return transferTransaction.getMosaics().size() == 1 && transferTransaction.getMosaics().stream()
                    .allMatch(m -> {
                        BigInteger maxBalance = networkCurrency.createAbsolute(BigInteger.valueOf(100)).getAmount();
                        if (m.getAmount().compareTo(maxBalance) > 0) {
                            return false;
                        }
                        return networkCurrency.getMosaicId().map(mosaicId -> mosaicId.equals(m.getId())).orElse(false)
                            || networkCurrency.getNamespaceId().map(namespaceId -> namespaceId.equals(m.getId()))
                            .orElse(false);
                    });

            });

            BiFunction<AggregateTransaction, Account, CosignatureSignedTransaction> cosignAggregateBondedTransaction = ((transaction, account) -> CosignatureTransaction
                .create(transaction).signWith(account));

            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            // replace with cosigner private key
            String privateKey = "";
            Account account = Account.createFromPrivateKey(privateKey, networkType);

            TransactionRepository transactionRepository = repositoryFactory.createTransactionRepository();

            try (Listener listener = repositoryFactory.createListener()) {
                listener.open().get();
                listener.aggregateBondedAdded(account.getAddress())
                    .filter(a -> a.signedByAccount(account.getPublicAccount())).filter(
                    a -> a.getInnerTransactions().stream()
                        .anyMatch(t -> validTransaction.apply(t, account.getPublicAccount())))
                    .map(a -> cosignAggregateBondedTransaction.apply(a, account))
                    .flatMap(transactionRepository::announceAggregateBondedCosignature).toFuture().get();
            }