LoginSignup
3
2

More than 1 year has passed since last update.

[Symbol] Pythonで複数人に同時にxymを送金する

Last updated at Posted at 2021-07-27

概要

  • 秘密鍵を有するウォレットから任意の複数アドレスへ同時にモザイク(xym)を送金プログラムです
    • 注意 : 秘密鍵は絶対に公開しないでください
  • 全コードはGithubにて公開中
    • 本記事は「05_aggregateTransaction1」
    • バグや次回の要望あればコメントください
  • 投げXYMは「NDLS6GYOIPHATATNAVVOUNJXBD6X4BXU6IRBHIY」まで
  • ノード運営しています。あなたの委任お待ちしております。
    • symbol-node.takagi-tech.com
    • ハーベスト報酬の99%を還元しています。
    • 詳細はこちら

前提知識

前回記事のトランザクションの生成からノードへのアナウンスが理解できている方向けの記事になります。
まだご覧になっていない方は先にこちらをご覧ください。

アグリゲートトランザクションとは

複数のトランザクションを内包するトランザクションをアグリゲートトランザクションと呼びます。
今回触れないことも多くありますが、こちらがとても参考になります。

各種設定の確認

送信先一覧の設定

今回は下記の3人に送金します。
xymの数量、メッセージを個別に設定できるようにしています。
1人目には1xym、2人目には2xym、3人目には3xymの計6xymを送金します。

# 送信先アドレス、送金量、メッセージ(送金量は可分性が6なので1000000倍した数値を指定)
TO_LIST = [
    ["TBMAGI3FR6ZFDXB5CHYTCE5EBKBSQP2CZPS7CGA", 1 * 1000000, "Hi Bob."],
    ["TAJEWDXCCMQLWTKVWDAX2EW7KS3L2NT6BOOIDSQ", 2 * 1000000, "Hi Alice."],
    ["TBQFD5L6OPJHHNJWCANKH6GG4GF3RRZOXNQ4KUA", 3 * 1000000, "こんにちは、太郎さん"]
]

その他設定

宛先以外は前回記事と同じ内容になります。

# 送信元
FROM_PRIVATEKEY = "270A5053FEDDAD47B05216D270A2F9E6DDCE74A8A16E8E3410022BDE011A3***"

# 最大手数料 (この例だと0.1枚)
MAX_FEE = 1 * 100000

# Symbol誕生のUTC秒 (メインネットは1615853185)
BIRTHTIME = 1616694977

# トランザクションの有効期限(単位はhour。この例だと2時間)
EXP_TIME = 2

# XYMのモザイクID (メインネットは0x6BED913FA20223F8)
MOSAIC_ID = 0x091F837E059AE13C

# トランザクションの送信先ノード
NODEURL = "http://sym-test-01.opening-line.jp:3000"

トランザクションの作成・署名とノードへアナウンス

  1. 3人に対するそれぞれの個別トランザクションの生成
  2. 3つのトランザクションを内包するアグリゲートトランザクションの作成
  3. アグリゲートトランザクションへ署名
  4. ノードへアナウンス

を行います。

import datetime
import urllib.request
import json
from binascii import unhexlify, hexlify
from symbolchain.core.CryptoTypes import PrivateKey, Hash256
from symbolchain.core.sym.KeyPair import KeyPair
from symbolchain.core.facade.SymFacade import SymFacade
from symbolchain.core.sym.MerkleHashBuilder import MerkleHashBuilder
import sha3

facade = SymFacade('public_test')
keypair = KeyPair(PrivateKey(unhexlify(FROM_PRIVATEKEY)))

# トランザクションのリスト作成
tx_lst = []
for item in TO_LIST:
    tx = facade.transaction_factory.create_embedded({
        'type': 'transfer',
        'signer_public_key': keypair.public_key,
        'recipient_address': SymFacade.Address(item[0]),
        'mosaics': [(MOSAIC_ID, item[1])],
        'message': item[2]
    })
    tx_lst.append(tx)

# マークルハッシュの作成
hash_builder = MerkleHashBuilder()
for tx in tx_lst:
    hash_builder.update(Hash256(sha3.sha3_256(tx.serialize()).digest()))
merkle_hash = hash_builder.final()

# アグリゲートトランザクションの有効期限
deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=EXP_TIME)).timestamp()) - BIRTHTIME) * 1000

# アグリゲートコンプリートの作成
aggregate = facade.transaction_factory.create({
    'type': 'aggregateComplete',
    'signer_public_key': keypair.public_key,
    'fee': MAX_FEE,
    'deadline': deadline,
    'transactions_hash': merkle_hash,
    'transactions': tx_lst
})

# 自己署名
signature = facade.sign_transaction(keypair, aggregate)
aggregate.signature = signature.bytes

# アグリゲートトランザクションのハッシュ値
tx_hash = facade.hash_transaction(aggregate)

# ノードへアナウンス
payload = {"payload": hexlify(aggregate.serialize()).decode('utf8').upper()}
req = urllib.request.Request(NODEURL + "/transactions",
                             json.dumps(payload).encode(),
                             {'Content-type': 'application/json'},
                             method='PUT')
with urllib.request.urlopen(req) as res:
    print("tx hash:" + str(tx_hash))
    print("status code:" + str(res.getcode()))
    print('http://explorer.testnet.symboldev.network/transactions/' + str(tx_hash))

問題なく実行できれば、下記のように 202 がAPIから返ってきます。

tx hash:CD321C0840D9DFCEE419A2D3C2281BFC818963F3C836F51CB47C0237E8E85DC0
status code:202
http://explorer.testnet.symboldev.network/transactions/CD321C0840D9DFCEE419A2D3C2281BFC818963F3C836F51CB47C0237E8E85DC0

エクスプローラーからも確認できますね。
これで3人に同時に送金が完了しました。
image.png

トランザクション内容の確認

次に、受理されたトランザクション内容を確認したいと思います。
自分宛のトランザクションが含まれるアグリゲートトランザクションの場合、

  1. 自分宛のトランザクション一覧からアグリゲートトランザクションIDを取得
  2. そのアグリゲートトランザクションの詳細を取得

のように2段階で問い合わせが必要になります。
上記のサンプルの受信者の1人 TBMAGI3FR6ZFDXB5CHYTCE5EBKBSQP2CZPS7CGA で確認してみます。

アグリゲートトランザクションIDの取得

自分宛のトランザクションが含まれるアグリゲートトランザクション一覧を確認していきます。
問い合わせ時のパラメーターに 'type': tx_type, を含めることでアグリゲートトランザクションのみ取得します。
print(d['id']) でアグリゲートトランザクションIDを表示します。

# メッセージ確認対象のアドレス
TO_ADDRESS = "TBMAGI3FR6ZFDXB5CHYTCE5EBKBSQP2CZPS7CGA"
# トランザクションのタイプ(0x4141 (16705 decimal) - AggregateCompleteTransaction.)
tx_type = 0x4141

req = urllib.request.Request(NODEURL + '/accounts/' + TO_ADDRESS)
with urllib.request.urlopen(req) as res:
    accountInfo = json.load(res)
ADDRESS48 = accountInfo['account']['address']

url = NODEURL + '/transactions/confirmed'
params = {
    'address': TO_ADDRESS,
    'type': tx_type,
    'order': 'desc',
}

req = urllib.request.Request('{}?{}'.format(url, urllib.parse.urlencode(params)))
with urllib.request.urlopen(req) as res:
    data = json.load(res)

for d in data['data']:
    print(d['id'])

結果は

60FFF136A36C2904DF689E49
60FFF089A36C2904DF689E35

2件ありました。

アグリゲートトランザクション詳細の取得

先ほどの結果のうち、 60FFF136A36C2904DF689E49 の詳細を確認します。
自分宛ではないトランザクションや、送金以外のトランザクションも含まれるので、下記のように絞り込みをかけます。

# トランザクションのタイプ(0x4154 (16724 decimal) - TransferTransaction.)
tx_type = 0x4154
# XYMのモザイクID(メインネットは6BED913FA20223F8)
XYM_ID = '091F837E059AE13C'

# 詳細を取得するアグリゲートトランザクションのid
tx_ids = ['60FFF136A36C2904DF689E49']

url = NODEURL + '/transactions/confirmed'
params = {
    'transactionIds': tx_ids
}
req = urllib.request.Request(url,
                             json.dumps(params).encode(),
                             {'Content-type': 'application/json'},
                             method='POST')
with urllib.request.urlopen(req) as res:
    data = json.load(res)

for d in data:
    for tx in d['transaction']['transactions']:
        # 自分宛の送金トランザクションのみ抽出
        if tx['transaction']['type'] == tx_type and tx['transaction']['recipientAddress'] == ADDRESS48:
            message = ""
            if "message" in tx['transaction']:
                message = tx['transaction']['message']
            # XYMの受信のみ抽出
            for mosaic in tx['transaction']['mosaics']:
                if mosaic['id'] == XYM_ID:
                    print("height:{}, {}, {}xym, [{}]".format(tx['meta']['height'], '受信' , int(mosaic['amount'])/1000000, unhexlify(message).decode('utf-8')))

結果はこのようになり、先ほど実行したサンプル通り、1xym + メッセージが確認できました。

height:275528, 受信, 1.0xym, [Hi Bob.]

全コードはこちら

参考情報

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2