Symbolブロックチェーンで複数人が同時に送金するトランザクションをPythonで作成する(2)

本記事はPythonでSymbolブロックチェーン連載の4回目です。

[PythonでSymbolブロックチェーンの送金プログラムを書いてみる。](https://qiita.com/nem_takanobu/items/f3c02caa17ad385b6155)
[Symbolブロックチェーンで一人が複数人に送金するトランザクションをPythonで作成する](https://qiita.com/nem_takanobu/items/d148dee444a74737769b)
[Symbolブロックチェーンで複数人が同時に送金するトランザクションをPythonで作成する](https://qiita.com/nem_takanobu/items/99557ff2299354aabcc4)
[Symbolブロックチェーンで複数人が同時に送金するトランザクションをPythonで作成する(2)](https://qiita.com/nem_takanobu/items/7e7bed7e612b613cf296)

今回はSymbolのアグリゲートトランザクションを利用して、複数人が同時に送金するトランザクションを作成の後編です。
後半は署名が未完成のトランザクションを保証された状態で留め置きしておくボンデッドトランザクションの方法を紹介します。

前回のこの記事を先にお読みください。

https://qiita.com/nem_takanobu/items/99557ff2299354aabcc4


##### 起案者アカウント準備

```py
from binascii import unhexlify
from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.sym.KeyPair import KeyPair
from symbolchain.core.facade.SymFacade import SymFacade

facade = SymFacade('public_test')

b = unhexlify("896E43895B908AF5847ECCB2645543751D94BD87E71058B003417FED5123****")

alicePrikey = PrivateKey(b)
aliceKeypair = KeyPair(alicePrikey)
alicePubkey = aliceKeypair.public_key
aliceAddress = facade.network.public_key_to_address(alicePubkey)
str(aliceAddress)
```


##### 連署アカウントの準備

```py
from symbolchain.core.CryptoTypes import PublicKey
bobPubkey = PublicKey(unhexlify(""))
bobAddress = SymFacade.Address("")
```

今回は留め置きされたトランザクションをウォレットで連署するので、すでに作成済みの連署アカウントを使用します。

##### アグリゲートトランザクションの作成と署名

```py
msg = 'test'
bobTx = facade.transaction_factory.create_embedded({
    'type': 'transfer',
    'signer_public_key': bobPubkey,
    'recipient_address': aliceAddress,
    'message': bytes(1) + msg.encode('utf8')
})

aliceTx = facade.transaction_factory.create_embedded({
    'type': 'transfer',
    'signer_public_key': alicePubkey,
    'recipient_address': bobAddress,
    'message': bytes(1) + msg.encode('utf8')
})

from symbolchain.core.sym.MerkleHashBuilder import MerkleHashBuilder
from symbolchain.core.CryptoTypes import Hash256
import sha3

hash_builder = MerkleHashBuilder()
hash_builder.update(Hash256(sha3.sha3_256(aliceTx.serialize()).digest()))
hash_builder.update(Hash256(sha3.sha3_256(bobTx.serialize()).digest()))
merkle_hash = hash_builder.final()

import datetime
deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=2)).timestamp()) - 1637848847) * 1000

aggregate = facade.transaction_factory.create({
    'type': 'aggregateBonded',
    'signer_public_key': alicePubkey,
    'fee': 1000000,
    'deadline': deadline,
    'transactions_hash': merkle_hash,
    'transactions': [aliceTx,bobTx]
})

signature = facade.sign_transaction(aliceKeypair, aggregate)
aggregate.signature = signature.bytes

```

前回までの記事を参考に、アグリゲートトランザクションを一気に作成します。
typeには`aggregateBonded`を指定します。

##### ハッシュロックトランザクションの作成とアナウンス

```py

from binascii import hexlify
hash = facade.hash_transaction(aggregate).bytes

hashLockTx = facade.transaction_factory.create({
    'type': 'hashLock',
    'signer_public_key': alicePubkey,
    'fee': 1000000,
    'deadline': deadline,
    'hash': hash,
    'mosaic': (0x091F837E059AE13C, 10000000),
    'duration': 480
})

hashLockSignature = facade.sign_transaction(aliceKeypair, hashLockTx)
hashLockTx.signature = hashLockSignature.bytes

import json
import http.client
headers = {'Content-type': 'application/json'}
conn = http.client.HTTPConnection("sym-test-01.opening-line.jp",3000)


hashLockPayload = {"payload": hexlify(hashLockTx.serialize()).decode('utf8').upper()}
conn.request("PUT", "/transactions", json.dumps(hashLockPayload),headers)
response = conn.getresponse()
print(response.status, response.reason)


print('http://sym-test-01.opening-line.jp:3000/transactionStatus/' + str(facade.hash_transaction(hashLockTx)))
```

アグリゲートトランザクションのアナウンス前に「このハッシュ値のトランザクションが来たら留め置きお願いします」という感じでハッシュロックトランザクションを投げておきます。スパム防止のため、留め置きの手数料として10XYM必要です。トランザクションが完成すれば、10XYMは返却されます。

##### アグリゲートトランザクションのアナウンス

```py

payload = {"payload": hexlify(aggregate.serialize()).decode('utf8').upper()}
conn = http.client.HTTPConnection("sym-test-01.opening-line.jp",3000)
conn.request("PUT", "/transactions/partial", json.dumps(payload),headers)

response = conn.getresponse()
print(response.status, response.reason)

hash = facade.hash_transaction(aggregate)
print('http://sym-test-01.opening-line.jp:3000/transactionStatus/' + str(hash))

```

ハッシュロックトランザクションが成功すれば、次にアグリゲートトランザクション本体を投げます。
エンドポイントが`/transactions/partial`になっているので注意してください。

このトランザクションが成功すると、 partialという状態になります。
署名が必要なアカウントに対して、ウォレット等で署名を促されるので、トランザクション内容を確認し、署名します。

##### Pythonで連署する場合

```py

hexlifiedSignedHash = str(bobKeypair.sign(unhexlify(str(hash))))
payload ={    
   "parentHash":str(hash),
   "signature":hexlifiedSignedHash,
   "signerPublicKey":str(bobPubkey),
   "version":"0"
 }

conn = http.client.HTTPConnection("sym-test-01.opening-line.jp",3000)
conn.request("PUT", "/transactions/cosignature", json.dumps(payload),headers)

response = conn.getresponse()
print(response.status, response.reason)
statusHash = facade.hash_transaction(aggregate)
print('http://sym-test-01.opening-line.jp:3000/transactionStatus/' + str(statusHash))

```

pythonで連署を行う場合は、上記のようにしてREST APIに投げるとトランザクションの状態がconfirmedになってトランザクションが承認されます。