このガイドでは、マルチシグアカウントからトランザクションを発行する方法を説明します。
この例では、Alice と Bob は 1-of-2 マルチシグ アカウントの署名者です。このマルチシグ構成により、Alice と Bob は別のアカウントでアセットを共有することができ、トランザクションにはいずれかの署名のみが必要です。
10 symbol.xym
を共有アカウントから3番目のアドレスへ送信しましょう。
モザイクとメッセージを2つのアカウント間で送信する ガイドを完了している
マルチシグアカウントへの変換 ガイドを完了している
1-of-2 マルチシグアカウントに 10 symbol.xym
単位を入金します。
Bob のアカウントに手数料を支払うために十分な symbol.xym
単位を入金してください。
マルチシグの署名者アカウントのいずれかにログインします。
"transfer" タブをクリックします。
転送トランザクションの適切な情報を入力します。“FROM” フィールドのドロップダウンメニューからマルチシグアカウントを選択します。受信者のアドレスを入力します。送信するモザイクとその量を選択します。“送信” をクリックします。ポップアップの情報を確認し、ウォレットのパスワードを入力します。 “確認” をクリックします
マルチシグアカウントの "最小承認数" が 1 より大きい数に設定されている場合は、別の連署者アカウントにログインし トランザクションに署名します 。最小承認数が満たされるまで、この手順を繰り返します。
トランザクションが承認されると、それぞれのアカウント残高の変化が表示されます。
新しい変数でマルチシグ署名者のうち1つの秘密鍵を定義します。そして、共有アカウントの公開鍵を定義します。
// replace network type
const networkType = NetworkType.TEST_NET;
// replace with cosignatory private key
const cosignatoryPrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const cosignatoryAccount = Account.createFromPrivateKey(
cosignatoryPrivateKey,
networkType,
);
// replace with multisig account public key
const multisigAccountPublicKey =
'3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const multisigAccount = PublicAccount.createFromPublicKey(
multisigAccountPublicKey,
networkType,
);
// replace with recipient address
const recipientRawAddress = 'TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI';
const recipientAddress = Address.createFromRawAddress(recipientRawAddress);
// replace with symbol.xym id
const networkCurrencyMosaicId = new MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
// replace network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with cosignatory private key
const cosignatoryPrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const cosignatoryAccount = symbol_sdk_1.Account.createFromPrivateKey(
cosignatoryPrivateKey,
networkType,
);
// replace with multisig account public key
const multisigAccountPublicKey =
'3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const multisigAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
multisigAccountPublicKey,
networkType,
);
// replace with recipient address
const recipientRawAddress = 'TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI';
const recipientAddress = symbol_sdk_1.Address.createFromRawAddress(
recipientRawAddress,
);
// replace with symbol.xym id
const networkCurrencyMosaicId = new symbol_sdk_1.MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
// replace with cosignatory private key
String cosignatoryPrivateKey = "";
Account cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, networkType);
// replace with cosignatory private key
String multisigAccountPublicKey = "";
Account multisigAccount = Account.createFromPrivateKey(multisigAccountPublicKey, networkType);
// replace with recipient address
String recipientRawAddress = "TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI";
Address recipientAddress = Address.createFromRawAddress(recipientRawAddress);
NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture().get();
次のように TransferTransaction を定義します:
プロパティ |
値 |
---|---|
タイプ |
TransferTransaction |
Recipient |
トランザクションを受け取るアカウントのアドレス |
Mosaics |
[10 |
メッセージ |
10 |
const transferTransaction = TransferTransaction.create(
Deadline.create(epochAdjustment),
recipientAddress,
[
new Mosaic(
networkCurrencyMosaicId,
UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
),
],
PlainMessage.create('sending 10 symbol.xym'),
networkType,
);
const transferTransaction = symbol_sdk_1.TransferTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
recipientAddress,
[
new symbol_sdk_1.Mosaic(
networkCurrencyMosaicId,
symbol_sdk_1.UInt64.fromUint(
10 * Math.pow(10, networkCurrencyDivisibility),
),
),
],
symbol_sdk_1.PlainMessage.create('sending 10 symbol.xym'),
networkType,
);
TransferTransaction transferTransaction = TransferTransactionFactory.create(networkType, recipientAddress,
Collections.singletonList(networkCurrency.createRelative(BigInteger.valueOf(10))),
PlainMessage.create("sending 10 symbol.xym")).build();
TransferTransaction を アグリゲートトランザクション でラップしてマルチシグの公開鍵をトランザクションの署名者として添付します。
const aggregateTransaction = AggregateTransaction.createComplete(
Deadline.create(epochAdjustment),
[transferTransaction.toAggregate(multisigAccount)],
networkType,
[],
UInt64.fromUint(2000000),
);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createComplete(
symbol_sdk_1.Deadline.create(epochAdjustment),
[transferTransaction.toAggregate(multisigAccount)],
networkType,
[],
symbol_sdk_1.UInt64.fromUint(2000000),
);
AggregateTransaction aggregateTransaction = AggregateTransactionFactory.createComplete(networkType,
Collections.singletonList(transferTransaction.toAggregate(multisigAccount.getPublicAccount())))
.maxFee(BigInteger.valueOf(2000000)).build();
次に、連署者アカウントでトランザクションに署名してアナウンスします。
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = cosignatoryAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = cosignatoryAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
String generationHash = repositoryFactory.getGenerationHash().toFuture().get();
SignedTransaction signedTransaction = cosignatoryAccount.sign(aggregateTransaction, generationHash);
try (Listener listener = repositoryFactory.createListener()) {
listener.open().get();
TransactionService transactionService = new TransactionServiceImpl(repositoryFactory);
transactionService.announce(listener, signedTransaction).toFuture().get();
}
トランザクションの受信者は、資産が承認されて、資産を受け取ります。
今回は TransactionTransaction が AggregateCompleteTransaction でラップされています。これはトランザクションのアナウンスに必要なアカウントが 1 つだけのためです。トランザクションのアナウンスに複数の署名が必要な (たとえば 2-of-2 マルチシグアカウント) トランザクションはアグリゲート ボンデッド として定義する必要があり、また、他のすべての必要なマルチシグ参加者は、承認のために署名する必要があります。
2-of-2 マルチシグ からトランザクションを発行するには、前のコードを変更して、トランザクションをボンデッドとして定義します。
const aggregateTransaction = AggregateTransaction.createBonded(
Deadline.create(epochAdjustment),
[transferTransaction.toAggregate(multisigAccount)],
networkType,
[],
UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = cosignatoryAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createBonded(
symbol_sdk_1.Deadline.create(epochAdjustment),
[transferTransaction.toAggregate(multisigAccount)],
networkType,
[],
symbol_sdk_1.UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = cosignatoryAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
// replace with cosignatory private key
String cosignatoryPrivateKey = "";
Account cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, networkType);
// replace with cosignatory private key
String multisigAccountPublicKey = "";
Account multisigAccount = Account.createFromPrivateKey(multisigAccountPublicKey, networkType);
// replace with recipient address
String recipientRawAddress = "TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI";
Address recipientAddress = Address.createFromRawAddress(recipientRawAddress);
NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture().get();
TransferTransaction transferTransaction = TransferTransactionFactory.create(networkType, recipientAddress,
Collections.singletonList(networkCurrency.createRelative(BigInteger.valueOf(10))),
PlainMessage.create("sending 10 symbol.xym")).build();
AggregateTransaction aggregateTransaction = AggregateTransactionFactory.createBonded(networkType,
Collections.singletonList(transferTransaction.toAggregate(multisigAccount.getPublicAccount())))
.maxFee(BigInteger.valueOf(2000000)).build();
String generationHash = repositoryFactory.getGenerationHash().toFuture().get();
SignedTransaction signedTransaction = cosignatoryAccount.sign(aggregateTransaction, generationHash);
2. When an AggregateTransaction is bonded, an account needs to lock at least 10
symbol.xym
to prevent spamming the network.
Once all cosigners sign the transaction, the amount of symbol.xym
locked becomes available again in the account that has locked the funds.
After HashLockTransaction has been confirmed, announce the AggregateBondedTransaction with a cosignatory.
In our case, we will sign the transaction with Bob's account.
const hashLockTransaction = HashLockTransaction.create(
Deadline.create(epochAdjustment),
new Mosaic(
networkCurrencyMosaicId,
UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
),
UInt64.fromUint(480),
signedTransaction,
networkType,
UInt64.fromUint(2000000),
);
const signedHashLockTransaction = cosignatoryAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new TransactionService(transactionHttp, receiptHttp);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
const hashLockTransaction = symbol_sdk_1.HashLockTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
new symbol_sdk_1.Mosaic(
networkCurrencyMosaicId,
symbol_sdk_1.UInt64.fromUint(
10 * Math.pow(10, networkCurrencyDivisibility),
),
),
symbol_sdk_1.UInt64.fromUint(480),
signedTransaction,
networkType,
symbol_sdk_1.UInt64.fromUint(2000000),
);
const signedHashLockTransaction = cosignatoryAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new symbol_sdk_1.TransactionService(
transactionHttp,
receiptHttp,
);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
HashLockTransaction hashLockTransaction = HashLockTransactionFactory
.create(networkType, networkCurrency.createRelative(BigDecimal.valueOf(10)), BigInteger.valueOf(480),
signedTransaction).build();
SignedTransaction signedHashLockTransaction = cosignatoryAccount.sign(hashLockTransaction, generationHash);
try (Listener listener = repositoryFactory.createListener()) {
listener.open().get();
TransactionService transactionService = new TransactionServiceImpl(repositoryFactory);
transactionService
.announceHashLockAggregateBonded(listener, signedHashLockTransaction, signedTransaction).toFuture()
.get();
}
トランザクションがネットワークに到達すると、定足数に到達するために必要な他のすべてのマルチシグ連署者がトランザクションに署名する必要があります。
トランザクションに署名するには (2) のトランザクションハッシュを置き換えて CLI コマンド transaction cosign
を使用します。
symbol-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile alice
トランザクションに最低でも minApproval
分の署名が集まると、受取者は資金を受け取るでしょう。そして 10 symbol.xym
をロックしているアカウントには、ロックされた資金が返還されます。