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

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

[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)


今回からPythonでアグリートトランザクションを実行してみます。
前回のこの記事を先にお読みください。

https://qiita.com/nem_takanobu/items/f3c02caa17ad385b6155


今回は一人が複数人に送金する場合を想定します。


追記）みーちさんがnemlogにmainnetでの送金記事をアップしていただいてます。

https://nemlog.nem.social/blog/62080


##### インポート

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

facade = SymFacade('public_test')

```
##### 送金者の生成

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

alicePrikey = PrivateKey(b)
aliceKeypair = KeyPair(alicePrikey)
alicePubkey = aliceKeypair.public_key
aliceAddress = facade.network.public_key_to_address(alicePubkey)
str(aliceAddress)
```
送信者としてAliceアカウントを秘密鍵から生成します。

##### 受信者の生成

```py
bobPrikey = PrivateKey.random()
bobKeypair = SymFacade.KeyPair(bobPrikey)
bobPubkey = bobKeypair.public_key
bobAddress = facade.network.public_key_to_address(bobPubkey)

carolPrikey = PrivateKey.random()
carolKeypair = SymFacade.KeyPair(carolPrikey)
carolPubkey = carolKeypair.public_key
carolAddress = facade.network.public_key_to_address(carolPubkey)
```

受信者としてBob,Carolアカウントを新規に作成します。

##### トランザクション作成

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

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

2種類のトランザクションを作成します。受信者はそれぞれBob,Carolですが、送信者はAlice一人です。

##### マークルハッシュの作成

```py
from symbolchain.core.symbol.MerkleHashBuilder import MerkleHashBuilder
from symbolchain.core.CryptoTypes import Hash256
import sha3

hash_builder = MerkleHashBuilder()
hash_builder.update(Hash256(sha3.sha3_256(bobTx.serialize()).digest()))
hash_builder.update(Hash256(sha3.sha3_256(carolTx.serialize()).digest()))
merkle_hash = hash_builder.final()
```
複数のトランザクションで構成されるため、それぞれのトランザクションハッシュからアグリゲートトランザクションのハッシュを導出しておきます。


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

```py

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

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

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

先ほど作成したトランザクションを配列に詰めてアグリゲートトランザクションを作成して署名します。

##### ネットワークへアナウンス

```py
from binascii import hexlify
payload = {"payload": hexlify(aggregate.serialize()).decode('utf8').upper()}

import json
json = json.dumps(payload)

headers = {'Content-type': 'application/json'}
import http.client

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

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

署名したトランザクションをJSON化してネットワークに通知します。

##### 確認

```py
hash = facade.hash_transaction(aggregate)
print('https://sym-test-01.opening-line.jp:3001/transactionStatus/' + str(hash))
```


##### 全ソースコード

```py
from binascii import unhexlify
from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.symbol.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)


bobPrikey = PrivateKey.random()
bobKeypair = SymFacade.KeyPair(bobPrikey)
bobPubkey = bobKeypair.public_key
bobAddress = facade.network.public_key_to_address(bobPubkey)

carolPrikey = PrivateKey.random()
carolKeypair = SymFacade.KeyPair(carolPrikey)
carolPubkey = carolKeypair.public_key
carolAddress = facade.network.public_key_to_address(carolPubkey)


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

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

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

hash_builder = MerkleHashBuilder()
hash_builder.update(Hash256(sha3.sha3_256(bobTx.serialize()).digest()))
hash_builder.update(Hash256(sha3.sha3_256(carolTx.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': 'aggregateComplete',
    'signer_public_key': alicePubkey,
    'fee': 1000000,
    'deadline': deadline,
    'transactions_hash': merkle_hash,
    'transactions': [bobTx,carolTx]
})

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


from binascii import hexlify
payload = {"payload": hexlify(aggregate.serialize()).decode('utf8').upper()}

import json
jsonPayload = json.dumps(payload)

headers = {'Content-type': 'application/json'}
import http.client

conn = http.client.HTTPConnection("sym-test-01.opening-line.jp",3000)
conn.request("PUT", "/transactions", jsonPayload ,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))


```