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

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

[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/d148dee444a74737769b

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

```py:alice
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)
```

トランザクションの起案者となるAliceアカウントを秘密鍵から作成します。

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

```py:bob
bobPrikey = PrivateKey.random()
bobKeypair = SymFacade.KeyPair(bobPrikey)
strBobPubkey = str(bobKeypair.public_key)
strBobAddress = str(facade.network.public_key_to_address(bobKeypair.public_key))
```
トランザクションを連署するアカウントBobを新規に作成します。AliceがBobの秘密鍵を知ることはできません。周知された公開鍵とアドレス(strBobPubkey,strBobAddress)を使って、Aliceはトランザクションを組み立てます。

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

```py:alice
from symbolchain.core.CryptoTypes import PublicKey
bobPubkey = PublicKey(unhexlify(strBobPubkey))
bobAddress = SymFacade.Address(strBobAddress)

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': 'aggregateComplete',
    '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

from binascii import hexlify
hash = facade.hash_transaction(aggregate).bytes
hexlifiedHash = hexlify(hash)
print(hexlifiedHash)
```
起案者のAliceがトランザクションを作成します。ネットワーク手数料はAliceが全て支払います。
Aliceの署名が必要なaliceTxとBobの署名が必要なbobTxの2種類を集約させ、署名します。その後、トランザクションをハッシュ値(hexlifiedHash)をBobに通知し署名をオフラインで促します。

##### 連署者署名

```py:bob
hexlifiedSignedHash = str(bobKeypair.sign(unhexlify(hexlifiedHash)))
print(hexlifiedSignedHash)
```

連署者Bobはトランザクションではなく、トランザクションハッシュに対して署名を行います。
署名結果(hexlifiedSignedHash)をAliceにオフライン（ネットワーク外）で返送します。

##### 署名結合とネットワークへのアナウンス

```py:alice

cosignature = (0, bobPubkey.bytes, unhexlify(hexlifiedSignedHash))
aggregate.cosignatures.append(cosignature)

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

Bobから署名を受け取ったAliceはトランザクションに連署を組み込み、ネットワークへアナウンスします。

##### 確認

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

以上です。

今回の記事が成功しましたら、次は「署名が未完成のトランザクションを保証された状態で留め置きしておくボンデッドトランザクション」に挑戦してみましょう。

https://qiita.com/nem_takanobu/items/7e7bed7e612b613cf296