はじめに
前回の記事
前回は、トランザクションを作ることにのみ専念した。
トランザクションを作り、送信し、連署し、承認を確認した。
しかし、まだ検証していないものが2つある。
署名済みトランザクションを相手に渡す
ペイメントチャネルでは、署名済みトランザクションを送信せずに相手に渡す必要がある。
そこで今回は、シリアライズされたトランザクションを送信し、連署し、承認されることを確認する。
裏切りの代償
前回の記事では、ペイメントチャネルによる取引がスムーズにいった場合のみを対象とした。
そこで今回は、裏切りのケースを想定し、古いコミットメントトランザクションを送信し、もう一方が罰を発動できるかを確認する。
作ってみる
ペイロードを送信するサンプル。その後、連署しているのだが、これは通常の流れ。
function sendAndCosignAggregateTransaction(payloads, address1, account2, addressG) {
return new Promise((resolve, reject) => {
const calcurateTransactionHash = function(payload) {
const hashInputPayload =
payload.substr(4*2,32*2) +
payload.substr((4+64)*2,32*2) +
payload.substr((4+64+32)*2);
const hasher = sha3_256.create();
return hasher.update(Buffer.from(hashInputPayload, 'hex')).hex().toUpperCase();
}
const cosignAggregateBondedTransaction = function(transaction, account) {
const cosignatureTransaction = CosignatureTransaction.create(transaction);
const signedTransaction = account.signCosignatureTransaction(cosignatureTransaction);
return signedTransaction;
}
const payload = payloads[0];
const hash = calcurateTransactionHash(payload);
const lockFundsTxPayload = payloads[1];
const lockFundsTxHash = calcurateTransactionHash(lockFundsTxPayload);
const listener = new Listener(ENDPOINT);
const transactionHttp = new TransactionHttp(ENDPOINT);
listener.open().then(() => {
return new Promise((resolveReq, rejectReq) => {
console.log(lockFundsTxHash);
request({
url: `${ENDPOINT}/transaction`,
method: 'PUT',
headers: {
'Content-Type':'application/json'
},
json: {"payload": lockFundsTxPayload}
}, (error, response, body) => {
console.log(body);
error ? rejectReq(error) : resolveReq();
});
});
}).then(() => {
listener.confirmed(addressG).pipe(
filter((transaction) => transaction.transactionInfo !== undefined
&& transaction.transactionInfo.hash === lockFundsTxHash),
flatMap(ignored => rx.from(new Promise((resolveReq, rejectReq) => {
console.log(hash);
request({
url: `${ENDPOINT}/transaction/partial`,
method: 'PUT',
headers: {
'Content-Type':'application/json'
},
json: {payload}
}, (error, response, body) => {
error ? rejectReq(error) : resolveReq(body);
});
})))
).subscribe(
x => console.log(x),
err => console.error(err)
);
listener.aggregateBondedAdded(account2.address).pipe(
filter((_) => !_.signedByAccount(account2.publicAccount)),
map(transaction => cosignAggregateBondedTransaction(transaction, account2)),
flatMap(cosignatureSignedTransaction => transactionHttp.announceAggregateBondedCosignature(cosignatureSignedTransaction))
).subscribe(
x => console.log(x),
err => console.error(err)
);
listener.confirmed(address1).pipe(
filter((transaction) => transaction.transactionInfo !== undefined
&& transaction.transactionInfo.hash === hash)
).subscribe(
ignore => {
listener.close();
resolve();
},
err => console.error(err)
);
}).catch((error) => {
console.error(error);
});
});
}
裏切りのパターンは、普通のSecret Proofなので割愛する。
送ってみる
ソースはこちら
順番に以下の処理を行う。
- マルチシグ、Alice、Bobの秘密鍵を生成する
- AliceとBobにXEMを送る
- マルチシグ用アカウントをマルチシグ化する
- 残高を表示する
- Opening Transactionのペイロードを作成する
- Commitment Transaction 1のペイロードを作成する
- Opening Transactionを送信する
- 残高を表示する
- Commitment Transaction 2のペイロードを作成する
- BobがCommitment Transaction 1を送信する
- 残高を表示する
- Aliceが裏切りの代償を発動する
- 残高を表示する
$ node paymentch9c.js
=== send XEM first ===
EB4B38E0166CC7F14E0148B0B1CA9FEC0D83EBC89B2D8DB009A6465AE84F46D3
connection open
TransactionAnnounceResponse {
message: 'packet 9 was pushed to the network via /transaction' }
=== create multisig account ===
C97C074389684061FD9A5876CB01E5343F5E84FBF17DB7B9B3F0660D5229E520
connection open
TransactionAnnounceResponse {
message: 'packet 9 was pushed to the network via /transaction' }
=== show balance [Multisig, Alice, Bob] ===
[ 0, 5000000, 5000000 ]
=== create opning transaction payload ===
=== create commitment 1 transaction payload ===
=== send opening transaction ===
connection open
9254669288957604223ED99703D8A7759CAD4E73E1C54FBF2CBDDC481E7C186F
{ message: 'packet 9 was pushed to the network via /transaction' }
B0CAA690C5B19F371CDDB1FEF1E9E3DC3BB03C7E3018AB1AF0755857D333EC6A
{ message:
'packet 500 was pushed to the network via /transaction/partial' }
TransactionAnnounceResponse {
message:
'packet 501 was pushed to the network via /transaction/cosignature' }
=== show balance [Multisig, Alice, Bob] ===
[ 10000000, 0, 0 ]
=== create commitment 2 transaction payload ===
=== cheat (send old commitment transaction) ===
connection open
9D3CC19B769B5F0282F297CDE0545757929E4D75B1F28BAAE964F171FE7B8524
{ message: 'packet 9 was pushed to the network via /transaction' }
E1880F5B30D430F3A28B18CDCB88623783A2BBCD22AA3CF30BBC32208C18914A
{ message:
'packet 500 was pushed to the network via /transaction/partial' }
TransactionAnnounceResponse {
message:
'packet 501 was pushed to the network via /transaction/cosignature' }
=== show balance [Multisig, Alice, Bob] ===
[ 0, 4000000, 0 ]
=== penalty (collect all XEM) ===
D9DEDBB21B4883EB37F7688AC940EAC5FD4E201F0824E857E50AB4D5F07D5484
connection open
TransactionAnnounceResponse {
message: 'packet 9 was pushed to the network via /transaction' }
=== show balance [Multisig, Alice, Bob] ===
[ 0, 10000000, 0 ]
Opening Transaction
Commitment Transaction 1 (Cheat)
Secret Proof Transaction (Penalty)
まとめ
作ることができる
関連
NEM2カタパルトで双方向ペイメントチャネルするには その1 概要
https://qiita.com/planethouki/items/c22a21836d913418de82
NEM2カタパルトで双方向ペイメントチャネルするには その2 トランザクションは作れるのか
https://qiita.com/planethouki/items/53415a14b34bebd450fc
NEM2カタパルトで双方向ペイメントチャネルするには その3 ペイロードの送信と罰則
https://qiita.com/planethouki/items/945239fe04d2af1fb203