公式ドキュメント
Writing your first application
Creating an escrow with aggregate bonded transaction
Signing announced aggregate bonded transaction
前提
- javascript
- NEM2-SDK
- tech-bureau/catapult-service-bootstrap
概要
AliceのXEMとTicketDistributorのBARモザイク交換を、1トランザクションで処理します。
これにより、支払いをしたのに、チケットが手に入らないといった問題などが回避できます。
このタイプのトランザクションは、少しテクニカルです。
transfer transaction作成
まず、ALICEはXEMを送るトランザクションと、BARをもらうトランザクションを作ります。
-
AliceToTicketTx
(ALICE → TICKET DISTRIBUTOR) -
TicketToAliceTx
(TICKET DISTRIBUTOR → ALICE)
この時点で、署名はしません。(できません)
aggregate transaction作成
次に、aggregate transactionを作ります。
この中に、先ほどのAliceToTicketTx
とTicketToAliceTx
を入れます。
そして、このトランザクションにALICEが署名しますが、まだネットワークには送信しません。
また、このaggregate txを、aggregate bonded transactionと呼ぶようです。
lock funds transaction作成
ALICEは、このaggregate txを送信するために、いくらかのXEMを保証金としてネットワーク上でロックしなければなりません。
そのためのトランザクションとして、lock funds transactionを作成します。
この中に、先ほどのaggregate tx(のハッシュ)と、保証金の10XEMを入れます。
ALICEはこれに署名をして、ネットワークに送信し、ブロックに取り込まれるまで待ちます。
Ticket Distributorによるaggregate transaction署名
先ほどのlock funds txがブロックに取り込まれたら、aggregate txの準備が完了です。
なので、ALICEはaggregate txをネットワークに送信します。
そして、Ticket Distributorは、ネットワークからこのトランザクションを入手し、連署(cosign)します。
この条件がそろって初めて、aggregate txは成立します。
やがてハーベストにより、ブロックに取り込まれ、トランザクションが完結し、保証金も返却されます。
コード
transaction作成
- transfer transaction作成
- aggregate transaction作成
- lock funds transaction作成
こちらを動かします。
Creating an escrow with aggregate bonded transaction
ただ、自分の環境では、// announce signed transaction
以下のannounceAggregateBonded
がうまく動かなかったので、時限で発動するようにしました。
transactionHttp.announce(lockFundsTransactionSigned).subscribe(x => console.log(x),
err => console.error(err));
setTimeout(() => transactionHttp.announceAggregateBonded(signedTransaction).subscribe(x => console.log(x),
err => console.error(err)), 30000);
また、ハッシュと署名者を表示するようにしました。
console.log('lockFundsTransactionSigned.hash : ' + lockFundsTransactionSigned.hash);
console.log('lockFundsTransactionSigned.signer: ' + lockFundsTransactionSigned.signer);
console.log('aggregateTransactionSigned.hash : ' + signedTransaction.hash);
console.log('aggregateTransactionSigned.signer: ' + signedTransaction.signer);
ぜんぶ
const nem2Sdk = require("nem2-sdk");
const Address = nem2Sdk.Address,
Deadline = nem2Sdk.Deadline,
Account = nem2Sdk.Account,
UInt64 = nem2Sdk.UInt64,
NetworkType = nem2Sdk.NetworkType,
PlainMessage = nem2Sdk.PlainMessage,
TransferTransaction = nem2Sdk.TransferTransaction,
Mosaic = nem2Sdk.Mosaic,
MosaicId = nem2Sdk.MosaicId,
TransactionHttp = nem2Sdk.TransactionHttp,
XEM = nem2Sdk.XEM,
AggregateTransaction = nem2Sdk.AggregateTransaction,
PublicAccount = nem2Sdk.PublicAccount,
LockFundsTransaction = nem2Sdk.LockFundsTransaction,
Listener = nem2Sdk.Listener;
// Replace with private key
const alicePrivateKey = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
// Replace with public key
const ticketDistributorPublicKey = '5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C';
const aliceAccount = Account.createFromPrivateKey(alicePrivateKey, NetworkType.MIJIN_TEST);
const ticketDistributorPublicAccount = PublicAccount.createFromPublicKey( ticketDistributorPublicKey, NetworkType.MIJIN_TEST);
const aliceToTicketDistributorTx = TransferTransaction.create(
Deadline.create(),
ticketDistributorPublicAccount.address,
[new Mosaic( new MosaicId('nem:xem'), UInt64.fromUint(100000000))],
PlainMessage.create('send 100 nem:xem to distributor'),
NetworkType.MIJIN_TEST,
);
const ticketDistributorToAliceTx = TransferTransaction.create(
Deadline.create(),
aliceAccount.address,
[new Mosaic( new MosaicId('foo:bar'), UInt64.fromUint(1))],
PlainMessage.create('send 1 foo:bar to alice'),
NetworkType.MIJIN_TEST,
);
const aggregateTransaction = AggregateTransaction.createBonded(Deadline.create(),
[
aliceToTicketDistributorTx.toAggregate(aliceAccount.publicAccount),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount),
],
NetworkType.MIJIN_TEST);
const signedTransaction = aliceAccount.sign(aggregateTransaction);
// Creating the lock funds transaction
const lockFundsTransaction = LockFundsTransaction.create(
Deadline.create(),
new Mosaic( new MosaicId('nem:xem'), UInt64.fromUint(10000000)),
UInt64.fromUint(480),
signedTransaction,
NetworkType.MIJIN_TEST);
const lockFundsTransactionSigned = aliceAccount.sign(lockFundsTransaction);
const transactionHttp = new TransactionHttp('http://localhost:3000');
transactionHttp.announce(lockFundsTransactionSigned).subscribe(x => console.log(x),
err => console.error(err));
setTimeout(() => transactionHttp.announceAggregateBonded(signedTransaction).subscribe(x => {console.log(x); console.log('foo')},
err => console.error(err)),30000);
console.log('lockFundsTransactionSigned.hash : ' + lockFundsTransactionSigned.hash);
console.log('lockFundsTransactionSigned.signer: ' + lockFundsTransactionSigned.signer);
console.log('aggregateTransactionSigned.hash : ' + signedTransaction.hash);
console.log('aggregateTransactionSigned.signer: ' + signedTransaction.signer);
Ticket Distributorによるaggregate transaction署名
こちらを動かします。
Signing announced aggregate bonded transaction
ぜんぶ
const nem2Sdk = require("nem2-sdk");
const Address = nem2Sdk.Address,
Deadline = nem2Sdk.Deadline,
Account = nem2Sdk.Account,
UInt64 = nem2Sdk.UInt64,
NetworkType = nem2Sdk.NetworkType,
PlainMessage = nem2Sdk.PlainMessage,
TransferTransaction = nem2Sdk.TransferTransaction,
Mosaic = nem2Sdk.Mosaic,
MosaicId = nem2Sdk.MosaicId,
TransactionHttp = nem2Sdk.TransactionHttp,
XEM = nem2Sdk.XEM,
AggregateTransaction = nem2Sdk.AggregateTransaction,
PublicAccount = nem2Sdk.PublicAccount,
LockFundsTransaction = nem2Sdk.LockFundsTransaction,
Listener = nem2Sdk.Listener,
CosignatureTransaction = nem2Sdk.CosignatureTransaction,
AccountHttp = nem2Sdk.AccountHttp;
const cosignAggregateBondedTransaction = (transaction, account) => {
const cosignatureTransaction = CosignatureTransaction.create(transaction);
return account.signCosignatureTransaction(cosignatureTransaction);
};
// Replace with private key
const privateKey = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);
const accountHttp = new AccountHttp('http://localhost:3000');
const transactionHttp = new TransactionHttp('http://localhost:3000');
accountHttp.aggregateBondedTransactions(account.publicAccount)
.flatMap((_) => _)
.filter((_) => !_.signedByAccount(account.publicAccount))
.map(transaction => cosignAggregateBondedTransaction(transaction, account))
.flatMap(cosignatureSignedTransaction => transactionHttp.announceAggregateBondedCosignature(cosignatureSignedTransaction))
.subscribe(announcedTransaction => console.log(announcedTransaction),
err => console.error(err));
結果
事前確認
Alice
private: 31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E
public: 3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57
address: SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC
# nem2-cli account info -a SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC
Account: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
-------------------------------------------------------
Mosaics
nem:xem: 409090909
十分な量のXEM
を持っています。BAR
モザイクは持ってません。
Ticket Distributor
private: 7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4
public: 5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C
address: SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK
$ nem2-cli account info -a SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK
Account: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
-------------------------------------------------------
Mosaics
nem:xem: 409090759
foo:bar: 1000000
foo:bar
モザイクを持っています。持っていなければ、作ります。namespace→参考。mosaic→参考。
transaction作成
- transfer transaction作成
- aggregate transaction作成
- lock funds transaction作成
$ node aggescrow.js
lockFundsTransactionSigned.hash : 590DC17009013885F9E8A3C16E186475C427D8EF42651CA792C2C893A183F645
lockFundsTransactionSigned.signer: 3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57
aggregateTransactionSigned.hash : 3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
aggregateTransactionSigned.signer: 3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57
TransactionAnnounceResponse {
message: 'packet 9 was pushed to the network via /transaction' }
TransactionAnnounceResponse {
message: 'packet 500 was pushed to the network via /transaction/partial' }
lock funds tx
nem2-cliだとエラーになる。(なぜ?)
$ nem2-cli transaction info -h 590DC17009013885F9E8A3C16E186475C427D8EF42651CA792C2C893A183F645
Error Error: fully qualified id is invalid due to empty part (:-3087871471161192663)
at throwInvalidFqn (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-library/dist/coders/idGenerator.js:38:8)
at findMosaicSeparatorIndex (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-library/dist/coders/idGenerator.js:45:34)
at Object.generateMosaicId (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-library/dist/coders/idGenerator.js:87:30)
at Object.mosaicId (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-library/dist/transactions/NamespaceMosaicId.js:18:32)
at new MosaicId (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-sdk/dist/src/model/mosaic/MosaicId.js:41:50)
at CreateStandaloneTransactionFromDTO (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-sdk/dist/src/infrastructure/transaction/CreateTransactionFromDTO.js:97:249)
at Object.exports.CreateTransactionFromDTO (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-sdk/dist/src/infrastructure/transaction/CreateTransactionFromDTO.js:66:16)
at MapSubscriber.Observable_1.Observable.fromPromise.map [as project] (/usr/local/lib/node_modules/nem2-cli/node_modules/nem2-sdk/dist/src/infrastructure/TransactionHttp.js:55:47)
at MapSubscriber._next (/usr/local/lib/node_modules/nem2-cli/node_modules/rxjs/operators/map.js:79:35)
at MapSubscriber.Subscriber.next (/usr/local/lib/node_modules/nem2-cli/node_modules/rxjs/Subscriber.js:93:18)
$ curl http://localhost:3000/transaction/CFA3A4175E854B51B7D18A6DD206EF562FEC622D50AF3C03B3E333AF70C34ACC
{
"meta": {
"height": [
11,
0
],
"hash": "590DC17009013885F9E8A3C16E186475C427D8EF42651CA792C2C893A183F645",
"merkleComponentHash": "590DC17009013885F9E8A3C16E186475C427D8EF42651CA792C2C893A183F645",
"index": 0,
"id": "5B0D007249F94300011CA572"
},
"transaction": {
"signature": "C8D1FD4D337F94194195516A95AB3A399CE611D0CF797E3F0577D7EE8F45227F6A63ECC02031388B75B01EA6E0DFF2B09CC629B3BD53AD2C18EDF6557C25EC04",
"signer": "3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57",
"version": 36867,
"type": 16716,
"fee": [
0,
0
],
"deadline": [
3692602683,
15
],
"duration": "480",
"mosaicId": "-3087871471161192663",
"amount": "10000000",
"hash": "PC6RI4lWCWobmSAQ1l5k6vVlTI8YpmOPTgmoSaYpCQE="
}
}
"hash": "PC6RI4lWCWobmSAQ1l5k6vVlTI8YpmOPTgmoSaYpCQE="
がaggregate tx hashだと思う。
aggregate bonded tx
$ curl http://localhost:3000/transaction/3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
{"code":"ResourceNotFound","message":"no resource exists with id '3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901'"}
ブロックには入らないんだと思う。
この段階でのAliceとTicket Distributorの状態
Alice。10XEM減りました。
$ nem2-cli account info -a SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC
Account: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
-------------------------------------------------------
Mosaics
nem:xem: 409090899
Ticket Distributor。変わらず。
$ nem2-cli account info -a SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK
Account: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
-------------------------------------------------------
Mosaics
nem:xem: 409090759
foo:bar: 1000000
また、Ticket Distributorは、この段階でaggregate bonded txを検知できるようになります。
$ nem2-cli account aggregatebonded -p 5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C
AggregateTransaction:
InnerTransactions: [
TransferTransaction:
Recipient: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
Message: "send 100 nem:xem to distributor"
Mosaics:
d525ad41d95fcf29: 100000000
Signer: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Deadline: 2018-05-29
TransferTransaction:
Recipient: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Message: "send 1 foo:bar to alice"
Mosaics:
ec673e105521b12f: 1
Signer: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
Deadline: 2018-05-29
]
Signer: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Deadline: 2018-05-29
Hash: 3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
Ticket Distributorによるaggregate transaction署名
$ node aggcosign.js
TransactionAnnounceResponse {
message: 'packet 501 was pushed to the network via /transaction/cosignature' }
ブロックに取り込まれた。cosignatures
フィールドがある。
$ nem2-cli transaction info -h 3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
AggregateTransaction:
Cosignatures:
Signer: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
InnerTransactions: [
TransferTransaction:
Recipient: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
Message: "send 100 nem:xem to distributor"
Mosaics:
d525ad41d95fcf29: 100000000
Signer: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Deadline: 2018-05-29
TransferTransaction:
Recipient: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Message: "send 1 foo:bar to alice"
Mosaics:
ec673e105521b12f: 1
Signer: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
Deadline: 2018-05-29
]
Signer: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
Deadline: 2018-05-29
Hash: 3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
$ curl http://localhost:3000/transaction/3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901
{
"meta": {
"height": [
36,
0
],
"hash": "3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901",
"merkleComponentHash": "72DF0D2A21983C6B1B6936EE03B8B8E011703224D08417E1BECDF02EEDADEB27",
"index": 0,
"id": "5B0D027549F94300011CA592"
},
"transaction": {
"signature": "5DAD80CA4BA4E23415ED2B4E33396D4C8E7AC960640F59F3DD0F39C2927CB740F1F46705019AE85BFA8616982783FF9BEAF1E119111E2DA2A9520B9C6D059206",
"signer": "3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57",
"version": 36866,
"type": 16961,
"fee": [
0,
0
],
"deadline": [
3692602609,
15
],
"cosignatures": [
{
"signer": "5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C",
"signature": "3D749715D7D6ED3FD159051ADCDCCB8A0B134C2D1EDAE209361522AEC4D9ECEB3DFF6416473591CE5E7FCF0E303AF8E57E35AC086D38DB8A8D3BBE07D61F9606"
}
],
"transactions": [
{
"meta": {
"height": [
36,
0
],
"aggregateHash": "3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901",
"aggregateId": "5B0D027549F94300011CA592",
"index": 0,
"id": "5B0D027549F94300011CA593"
},
"transaction": {
"signer": "3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57",
"version": 36867,
"type": 16724,
"recipient": "906C4A59CDF202715BA817088F57D264EA93093632CBDFD0CA",
"message": {
"type": 0,
"payload": "73656E6420313030206E656D3A78656D20746F206469737472696275746F72"
},
"mosaics": [
{
"id": [
3646934825,
3576016193
],
"amount": [
100000000,
0
]
}
]
}
},
{
"meta": {
"height": [
36,
0
],
"aggregateHash": "3C2E91238956096A1B992010D65E64EAF5654C8F18A6638F4E09A849A6290901",
"aggregateId": "5B0D027549F94300011CA592",
"index": 1,
"id": "5B0D027549F94300011CA594"
},
"transaction": {
"signer": "5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C",
"version": 36867,
"type": 16724,
"recipient": "90758EB47C28D6143BAA3DE6A8D9C319B503A1BFD8E789E9E2",
"message": {
"type": 0,
"payload": "73656E64203120666F6F3A62617220746F20616C696365"
},
"mosaics": [
{
"id": [
1428271407,
3966189072
],
"amount": [
1,
0
]
}
]
}
}
]
}
}
最終的なAliceとTicket Distributorの状態
Alice。めでたくfoo:bar
モザイクをもらった。XEM
は、ロックした分は返ってきて、トータル100減った。409090909
→409090899
→409090809
$ nem2-cli account info -a SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC
Account: SB2Y5N-D4FDLB-IO5KHX-TKRWOD-DG2QHI-N73DTY-T2PC
-------------------------------------------------------
Mosaics
nem:xem: 409090809
foo:bar: 1
Ticket Distributor。foo:bar
が1減って、XEM
が100増えた。409090759
→409090859
$ nem2-cli account info -a SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK
Account: SBWEUW-ON6IBH-CW5IC4-EI6V6S-MTVJGC-JWGLF5-7UGK
-------------------------------------------------------
Mosaics
foo:bar: 999999
nem:xem: 409090859
Ticket Distributorがcosignしなかったら
Aliceが作ったaggregate txに、チケット販売者が連署しなかった場合、Aliceの保証金10XEMは、永遠にロックされたままと思われる。
試しに、aggregate txの有効期限(Duration)を10ブロックで設定し、期限切れになるのを待ってみた。
期限が切れた後も、# nem2-cli account aggregatebonded -p <略>
でaggregate txは見れる。そして、連署のプログラムを流し、aggregate txをブロックに取り込める状態にした。
しばらく経って、nem2-cli transaction info -h <略>
でaggregate txを見に行っても見つからなかった。ブロックに取り込まれなかったものと思われる。
AliceのXEMは10減り、そのほかは変わらずだった。
lock funds 設定
config-network.properties
で、lock fundsの数量が決められるかもしれない。
[plugin:catapult.plugins.lock]
lockedFundsPerAggregate = 10'000'000
nemesisブロックの設定で、小数点の設定をいじると、また必要な量が変わってくるかも。
[mosaic>nem:xem]
divisibility = 6
Ticket Distributorが自動的に連署する
Signing announced aggregate bonded transactions automatically
validTransaction
関数により、指定のモザイクで、指定の数量以上の支払いかどうかをチェックしている。
コメント
- mosaicIdの表現がいっぱいある。
- mosaicってCase Sensitiveだっけ・・・
- jsonは縦に長くなる
- 説明が長くなる
- プログラム読むのが早い
- 下記について理解したい。
To fetch aggregate bonded transactions that should be signed by multisig cosignatories, refer to the multisig public key instead. See how to get multisig accounts where an account is cosignatory.