エスクローコントラクトの作成でアグリゲートボンドトランザクションについて学習します。
エスクロー とは 第三者が主要取引当事者のために、金銭または書類を受け取り・支払い をする 契約上の取り決め です。この支払いは 取引当事者によって合意された条件 または 取引の完了または終了までの間、ブローカーの本人または他の人物に代わって 資産を保持するためにブローカーによって確立されたアカウント に依存します。より詳しい説明は Wikipedia を参照してください。
この例では2つの当事者が実質的なサービスに同意すると仮定して、エスクローが即時に実行できることを意味します:
マルチアセットエスクロートランザクション
前述の説明を Symbol 関連の概念に正規化します:
symbol.xym
単位を入金します。Alice とチケット販売者は以下のモザイクを交換したいと思っています。
保有者 | 総量 | MosaicId | 説明 |
---|---|---|---|
Alice | 100 | symbol.xym |
ネイティブ通貨モザイク |
チケット販売者 | 1 | 7cdf3b117a3c40cc |
ミュージアムチケットの表現 |
継続する前に symbol.xym
を保有した アカウントを2つ作成 します。チケット販売者アカウントで モザイクを作成 します。この新しいモザイクはチケットを表現します。
symbol.xym
を送信します7cdf3b117a3c40cc
(museum ticket) を送信する TransferTransaction注釈
博物館のチケットはネットワーク内の ID 7cdf3b117a3c40cc
を持っていません。前の手順で作成したものにモザイク識別子を置き換えてください。
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with alice private key
const alicePrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const aliceAccount = Account.createFromPrivateKey(alicePrivateKey, networkType);
// replace with ticket distributor public key
const ticketDistributorPublicKey =
'20330294DC18D96BDEEF32FB02338A6462A0469CB451A081DE2F05B4302C0C0A';
const ticketDistributorPublicAccount = PublicAccount.createFromPublicKey(
ticketDistributorPublicKey,
networkType,
);
// replace with ticket mosaic id
const ticketMosaicId = new MosaicId('7cdf3b117a3c40cc');
// replace with ticket mosaic id divisibility
const ticketDivisibility = 0;
// replace with symbol.xym id
const networkCurrencyMosaicId = new MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const aliceToTicketDistributorTx = TransferTransaction.create(
Deadline.create(epochAdjustment),
ticketDistributorPublicAccount.address,
[
new Mosaic(
networkCurrencyMosaicId,
UInt64.fromUint(100 * Math.pow(10, networkCurrencyDivisibility)),
),
],
PlainMessage.create('send 100 symbol.xym to distributor'),
networkType,
);
const ticketDistributorToAliceTx = TransferTransaction.create(
Deadline.create(epochAdjustment),
aliceAccount.address,
[
new Mosaic(
ticketMosaicId,
UInt64.fromUint(1 * Math.pow(10, ticketDivisibility)),
),
],
PlainMessage.create('send 1 museum ticket to customer'),
networkType,
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with alice private key
const alicePrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const aliceAccount = symbol_sdk_1.Account.createFromPrivateKey(
alicePrivateKey,
networkType,
);
// replace with ticket distributor public key
const ticketDistributorPublicKey =
'20330294DC18D96BDEEF32FB02338A6462A0469CB451A081DE2F05B4302C0C0A';
const ticketDistributorPublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
ticketDistributorPublicKey,
networkType,
);
// replace with ticket mosaic id
const ticketMosaicId = new symbol_sdk_1.MosaicId('7cdf3b117a3c40cc');
// replace with ticket mosaic id divisibility
const ticketDivisibility = 0;
// replace with symbol.xym id
const networkCurrencyMosaicId = new symbol_sdk_1.MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const aliceToTicketDistributorTx = symbol_sdk_1.TransferTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
ticketDistributorPublicAccount.address,
[
new symbol_sdk_1.Mosaic(
networkCurrencyMosaicId,
symbol_sdk_1.UInt64.fromUint(
100 * Math.pow(10, networkCurrencyDivisibility),
),
),
],
symbol_sdk_1.PlainMessage.create('send 100 symbol.xym to distributor'),
networkType,
);
const ticketDistributorToAliceTx = symbol_sdk_1.TransferTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
aliceAccount.address,
[
new symbol_sdk_1.Mosaic(
ticketMosaicId,
symbol_sdk_1.UInt64.fromUint(1 * Math.pow(10, ticketDivisibility)),
),
],
symbol_sdk_1.PlainMessage.create('send 1 museum ticket to customer'),
networkType,
);
NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
// replace with alice private key
String alicePrivatekey = "";
Account aliceAccount = Account.createFromPrivateKey(alicePrivatekey, networkType);
// replace with bob public key
String ticketDistributorPublicKey = "";
PublicAccount ticketDistributorPublicAccount = PublicAccount
.createFromPublicKey(ticketDistributorPublicKey, networkType);
// replace with ticket mosaic id
MosaicId ticketMosaicId = new MosaicId("7cdf3b117a3c40cc");
int ticketDivisibility = 0;
NetworkCurrency ticketCurrency = new NetworkCurrencyBuilder(ticketMosaicId, ticketDivisibility).build();
// replace with ticket mosaic id divisibility
NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture().get();
TransferTransaction aliceToTicketDistributorTx = TransferTransactionFactory
.create(networkType, ticketDistributorPublicAccount.getAddress(),
Collections.singletonList(networkCurrency.createRelative(BigInteger.valueOf(100))),
PlainMessage.create("send 100 symbol.xym to distributor")).build();
TransferTransaction ticketDistributorToAliceTx = TransferTransactionFactory
.create(networkType, aliceAccount.getAddress(),
Collections.singletonList(ticketCurrency.createRelative(BigInteger.ONE)),
PlainMessage.create("send 1 museum ticket to customer")).build();
2. Wrap the defined transactions in an AggregateTransaction and sign it with Alice’s account. An AggregateTransaction is complete if before announcing it to the network, all required cosigners have signed it. If valid, it will be included in a block. In case that signatures are required from other participants—the ticket distributor—it is considered bonded.
const aggregateTransaction = AggregateTransaction.createBonded(
Deadline.create(epochAdjustment),
[
aliceToTicketDistributorTx.toAggregate(aliceAccount.publicAccount),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount),
],
networkType,
[],
UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = aliceAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log('Aggregate Transaction Hash:', signedTransaction.hash);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createBonded(
symbol_sdk_1.Deadline.create(epochAdjustment),
[
aliceToTicketDistributorTx.toAggregate(aliceAccount.publicAccount),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount),
],
networkType,
[],
symbol_sdk_1.UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = aliceAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log('Aggregate Transaction Hash:', signedTransaction.hash);
AggregateTransaction aggregateTransaction = AggregateTransactionFactory.createBonded(networkType, Arrays
.asList(aliceToTicketDistributorTx.toAggregate(aliceAccount.getPublicAccount()),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount)))
.maxFee(BigInteger.valueOf(2000000)).build();
String generationHash = repositoryFactory.getGenerationHash().toFuture().get();
SignedTransaction signedTransaction = aliceAccount.sign(aggregateTransaction, generationHash);
3. When an AggregateTransaction is bonded, Alice will need to lock 10
symbol.xym
to prevent spamming the network.
Once the ticket distributor signs the AggregateTransaction, the amount of locked symbol.xym
becomes available again on Alice’s account, and the exchange will get through.
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 = aliceAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
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 = aliceAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
// 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 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 = aliceAccount.sign(hashLockTransaction, generationHash);
try (Listener listener = repositoryFactory.createListener()) {
listener.open().get();
TransactionService transactionService = new TransactionServiceImpl(repositoryFactory);
transactionService.announceHashLockAggregateBonded(listener, signedHashLockTransaction, signedTransaction).toFuture()
.get();
}
販売者はまだ AggregateBondedTransaction に署名していないため、交換は完了していません。
これは安全ではありません なぜなら:
AggregateTransaction 機能を使用して、すべての参加者が合意したときに複数のトランザクションが同時に実行されるようにします。
お探しのものは見つかりましたか? フィードバックをください。