LoginSignup
3
5

More than 1 year has passed since last update.

Symbolブロックチェーンのメタデータを登録するトランザクションをPythonで作成する

Last updated at Posted at 2021-05-29

Symbolが提供するアグリゲートトランザクションは、送金だけではなく、ブロックチェーンの状態を変更させるトランザクションを混ぜて作成させることが可能です。

つまり、入金と同時にマルチシグの構成を変更して所有権を譲渡したり、
入金と同時に、メタデータに卒業証明を記入したり
アポスティーユと同時に、数量が1つだけの代替不可トークンを発行したりすることができます。

今回はメタデータの作成方法について説明してみます。

事前準備

import sha3
import json
import http.client
import datetime
from binascii import hexlify
from binascii import unhexlify
from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.sym.KeyPair import KeyPair
from symbolchain.core.facade.SymFacade import SymFacade
from symbolchain.core.CryptoTypes import PublicKey
from symbolchain.core.sym.IdGenerator import generate_namespace_id
from symbolchain.core.sym.MerkleHashBuilder import MerkleHashBuilder
from symbolchain.core.CryptoTypes import Hash256

前回までに使用したパッケージを事前にimportしておきます。

メタデータの作成

hasher = sha3.sha3_256()
hasher.update('certificate'.encode('utf8'))
digest = hasher.digest()
metadataKey = int.from_bytes(digest[0:8], 'little')
metadataValue = 'aaa'

今回はkey:certificate value:aaa として登録します。
key値の作成方法はいろいろあるかとは思いますが、NodeJS版SDKも上記方法で統一されているようです。

メタデータの登録

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)
print(str(facade.network.public_key_to_address(bobKeypair.public_key)))
print(str(bobKeypair.public_key))
strBobPubkey = str(bobKeypair.public_key)
strBobAddress = str(facade.network.public_key_to_address(bobKeypair.public_key))

bobPubkey = PublicKey(unhexlify(strBobPubkey))
bobAddress = SymFacade.Address(strBobAddress)

aliceTx = facade.transaction_factory.create_embedded({
    'type': 'accountMetadata',
    'signer_public_key': alicePubkey,
    'target_address': bobAddress,
    'scoped_metadata_key': metadataKey,
    'value_size_delta': len(metadataValue),
    'value': metadataValue
})

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

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

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

signature = facade.sign_transaction(aliceKeypair, aggregate)
aggregate.signature = signature.bytes
hash = facade.hash_transaction(aggregate).bytes
hexlifiedHash = hexlify(hash)
print(hexlifiedHash)

#bob側で署名して
hexlifiedSignedHash = str(bobKeypair.sign(unhexlify(hexlifiedHash)))
cosignature = (0, bobPubkey.bytes, unhexlify(hexlifiedSignedHash))
aggregate.cosignatures.append(cosignature)

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

strJson = json.dumps(payload)
headers = {'Content-type': 'application/json'}

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

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

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

AliceからBobにメタデータを記録します。
アグリゲートコンプリートによるオフライン署名の方法で実施しました。

トランザクションの組み立て以外は、送金トランザクションの時と全く同じですね。

Bobのメタデータに記録するので、Aliceの署名だけではなく、Bobのオプトイン署名も必要になります。Symbolではトークンを送りつける以外のあらゆる他アカウントへの状態操作について、受け取るアカウントのオプトイン署名が必要になります。オプトインを要せず相手に譲渡したい場合はNFTなどトークンの形に置き換えて送信する方法が考えられます。

メタデータ更新

次に登録したメタデータを修正してみます。

更新内容の定義
newMetadataValue = 'bbb'
xorValue = "".join([chr(ord(data) ^ ord(code))
    for (data, code) in zip(metadataValue, newMetadataValue)]).encode().hex()

value値 aaaをbbbに変更します。
ただ、トランザクションにはbbbではなく、XORの差分値を登録します。
ここがトレース可能なブロックチェーンならではの部分ですね。

aliceTx = facade.transaction_factory.create_embedded({
    'type': 'accountMetadata',
    'signer_public_key': alicePubkey,
    'target_address': bobAddress,
    'scoped_metadata_key': metadataKey,
    'value_size_delta': 0,
    'value': unhexlify(xorValue)
})

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

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

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

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

hash = facade.hash_transaction(aggregate).bytes
hexlifiedHash = hexlify(hash)
print(hexlifiedHash)

#bob側で署名して
hexlifiedSignedHash = str(bobKeypair.sign(unhexlify(hexlifiedHash)))
cosignature = (0, bobPubkey.bytes, unhexlify(hexlifiedSignedHash))
aggregate.cosignatures.append(cosignature)

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

strJson = json.dumps(payload)

headers = {'Content-type': 'application/json'}

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

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

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

value_size_delta は現在バグのため負値を指定することができません。
ぜひお試しください。

3
5
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
5