【Jaguar's Blog 7】Symbol Mosaic Levy?
この記事は12月28日にNEM/Symgolのコア開発者Jaguar氏によって投稿された記事「Symbol Mosaic Levy?」をChatGPTを用いて翻訳したものです。
Symbolに完全に移行されていなかったNEMの機能の一つはモザイク・レビー(Mosaic Levy)です。しかし、Symbolの機能を使用してモザイク・レビーを近似することができることを知っていましたか?続けて読んで、その方法を知ってください!
NEM
テストネット上でNEMモザイクを作成し、その挙動を観察してみましょう!
まず、Symbol SDKを使用してNEMトランザクションを送信する方法についてのこの記事を確認してください。
ネームスペースの作成
NEMでは、すべてのモザイクはネームスペースに関連付けられていますので、まず最初にネームスペースを作成しましょう:
async def create_namespace(facade, signer_key_pair, namespace_name):
await prepare_and_send_transaction(facade, signer_key_pair, {
'type': 'namespace_registration_transaction_v1',
'rental_fee_sink': 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35',
'rental_fee': xem(100),
'name': namespace_name,
'fee': xem(10)
})
まず、namespace_registration_transaction_v1というタイプのトランザクションを作成します。これはネームスペースを登録または更新するために使用されます。ネームスペースはグローバルなリソースです。乱用を防ぐために、ネームスペースが作成または更新されるたびに、トランザクション手数料に加えてネットワークに固定の手数料が支払われる必要があります。この手数料はレンタル料金と呼ばれ、すべてのネームスペース登録は一時的であり、年ごとに更新する必要があります。レンタル料金は100 XEMで固定されており、TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35と呼ばれるレンタル料金シンクアカウントに支払われなければなりません。さらに、10 XEMのトランザクション手数料を支払う必要があります。最後に、希望するネームスペースの名前を設定します。
モザイクの作成
今作成したネームスペース内に、レビーを備えたモザイクを作成しましょう:
async def create_mosaic(facade, signer_key_pair, namespace_name, mosaic_name):
await prepare_and_send_transaction(facade, signer_key_pair, {
'type': 'mosaic_definition_transaction_v1',
'rental_fee_sink': 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC',
'rental_fee': xem(50),
'mosaic_definition': {
'owner_public_key': signer_key_pair.public_key,
'id': {
'namespace_id': {'name': namespace_name.encode('utf8')},
'name': mosaic_name.encode('utf8')
},
'description': 'example mosaic with levy'.encode('utf8'),
'properties': [
{'property_': {'name': 'divisibility'.encode('utf8'), 'value': '0'.encode('utf8')}},
{'property_': {'name': 'initialSupply'.encode('utf8'), 'value': '100'.encode('utf8')}},
{'property_': {'name': 'supplyMutable'.encode('utf8'), 'value': 'false'.encode('utf8')}},
{'property_': {'name': 'transferable'.encode('utf8'), 'value': 'true'.encode('utf8')}}
],
'levy': {
'transfer_fee_type': 'absolute',
'recipient_address': facade.network.public_key_to_address(signer_key_pair.public_key),
'mosaic_id': {
'namespace_id': {'name': 'nem'.encode('utf8')},
'name': 'xem'.encode('utf8')
},
'fee': xem(1)
}
},
'fee': xem(10)
})
まず、mosaic_definition_transaction_v1というタイプのトランザクションを作成します。これはモザイクを作成するために使用されます。ネームスペースと同様に、モザイクもグローバルなリソースであり、レンタル料金の支払いが必要です。ただし、モザイクは自動的に所有するネームスペースの寿命を共有します。モザイクのレンタル料金は50 XEMで固定されており、TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLCと呼ばれるレンタル料金シンクアカウントに支払われなければなりません。さらに、10 XEMのトランザクション手数料を支払う必要があります。
何よりも重要なのは、新しいモザイクを定義するためにモザイク定義を指定することです。。owner_public_keyはトランザクションの署名者と一致し、冗長です。モザイクIDは、モザイクを所有するネームスペースのnamespace_idとモザイクの名前の2つの部分から構成されます。モザイクの説明は、ブロックチェーンにモザイクと一緒に保存される友好的な説明を可能にします。モザイクのプロパティは、モザイクの動作を構成します。
divisibility - モザイクが持つことができる小数点以下の桁数;ここではゼロに設定します
initialSupply - モザイクの初期ユニット数;ここでは100に設定します
supplyMutable - モザイクの供給が変更可能である場合にtrueに設定するブール値;ここではfalseに設定します
transferable - モザイクを任意のアカウントに送信できる場合にtrueに設定するブール値;ここではtrueに設定します
最後に、レビーを構成します。transfer_fee_typeは、レビーの金額が一定であることを示すabsolute、または転送される金額に比例することを示すpercentileのいずれかになります。recipient_addressは、レビーが支払われるアカウントです。これをモザイクの所有者のアカウントに設定しましたが、任意のアカウントにすることもできます。mosaic_idは、レビーの支払いに使用するモザイクです。ここではXEMを選択し、完全にnem.xemと指定できます。1 XEMの手数料を設定しており、したがってこのモザイクのどんな金額でも転送されるたびに1 XEMのレビーが支払われます。
モザイクの送信
任意のモザイクを送信するためのヘルパー関数を作成しましょう:
async def send_mosaic(facade, signer_key_pair, namespace_name, mosaic_name, recipient_address):
await prepare_and_send_transaction(facade, signer_key_pair, {
'type': 'transfer_transaction_v2',
'recipient_address': recipient_address,
'amount': xem(1),
'mosaics': [
{
'mosaic': {
'mosaic_id': {
'namespace_id': {'name': namespace_name.encode('utf8')},
'name': mosaic_name.encode('utf8')
},
'amount': amount
}
}
],
'fee': xem(1)
})
まず、mosaicsを送信するために使用されるtransfer_transaction_v2のタイプのトランザクションを作成します。受信者(recipient_address)を指定し、1 XEMの転送手数料を支払います。amountを1 XEMに設定していますが、これはモザイクバッグを送信する場合に常に推奨されています。実際には金額ではなく、モザイク内のすべての金額に乗算される乗数です。最後に、mosaics配列で送信したいモザイク(またはモザイク)を指定できます。今作成したモザイクを送信したいだけなので、単一のモザイクアイテムしか必要ありません。mosaic_idをmosaic_definitionで送信したIDに設定し、amountを希望する金額に設定します。
モザイク作成者から他人への送信
ネームスペースとモザイクを作成し、モザイクを送信できるようなコードを書いてみましょう:
await create_namespace(facade, signer_key_pair, '<YOUR_NAMESPACE_NAME>')
await create_mosaic(facade, signer_key_pair, '<YOUR_NAMESPACE_NAME>', '<YOUR_MOSAIC_NAME>')
新しいモザイクの所有者であるモザイク所有者のアカウント(Alice)から別のアカウント(Bob)に10ユニットの新しいモザイクを送信してみましょう:
await send_mosaic(facade, signer_key_pair, '<YOUR_NAMESPACE_NAME>', '<YOUR_MOSAIC_NAME>', recipient_address, 10)
トランザクション後、次の状態変更が発生するはずです:
Alice - 1 XEM - トランザクション手数料の支払い
Alice - 10 MOSAIC - モザイクの転送
Bob + 10 MOSAIC - モザイクの転送
他人から他人への送信
さて、BobからAliceに5ユニット返送しましょう:
await send_mosaic(facade, recipient_key_pair, '<YOUR_NAMESPACE_NAME>', '<YOUR_MOSAIC_NAME>', signer_address, 5)
トランザクション後、次の状態変更が発生するはずです:
Alice + 1 XEM - モザイクのレビー支払い
Alice + 5 MOSAIC - モザイクの転送
Bob - 1 XEM - トランザクション手数料の支払い
Bob - 1 XEM - モザイクのレビー支払い
Bob - 5 MOSAIC - モザイクの転送
モザイク・レビーが成功裏に適用されましたね!
制限事項
前の例を通じて、NEMのレビーがどのように動作するかについて理解が深まったことを期待していますね!レビーは特定の状況で有用であるかもしれませんが、NEMのレビーには理解しておくべきいくつかの問題があります。
まず、処理の複雑さから、レビーは再帰的に処理されません。これが何を意味するかを理解するために、例を考えてみましょう。モザイク「Apple」が100 XEMの絶対的なレビーを持っていると仮定します。モザイク「Lemon」が1つの「Apple」の絶対的なレビーを持っていると仮定します。Lemonが転送されると、そのレビーの一部として1つの「Apple」が移動しますが、「Apple」の100 XEMのレビーは回避されます。これは過去にスパムモザイクを高いレビーで除去するために肯定的に使用されてきましたが、最終的にはレビーを回避するバグです。
次に、レビーは非常に原始的で、absoluteとpercentileという2つのタイプの計算しかサポートしていません。他のレビー計算(例えば、現在のYENレートに結び付いたもの)は、異なる設定でより有用かもしれません。残念ながら、追加のロジックを導入するには、NEMネットワーク全体のハードフォークが必要です。
Symbol
レビーを最も基本的な形に分解すれば、レビーは転送税であると理解できます。言い換えれば、任意のレビーシステムには、事前に決められた税金が支払われない限り、転送をブロックする方法が必要です。それをSymbolでエミュレートできる方法はあるでしょうか?考えてみましょう 🤔 ...
確かに、アグリゲートトランザクションを使用してモザイクの転送と一緒に税金を支払うことはできます。それをすべての場合で強制することはできるでしょうか?モザイクの転送に税金の支払いがない場合はどうでしょうか?それも防ぐことができます。非転送可能なモザイクを作成することで、モザイクの所有者はすべての転送に関与する必要があり、適切な税金の支払いがない場合には転送リクエストを拒否できるようになります。
ますますSymbolのトランザクションを送信するには、このSymbol SDKの記事を確認してください。この記事の例を簡略化するために、prepare_and_send_transaction関数を使用します。
モザイクの作成
NEMとは異なり、Symbolのモザイクはネームスペースとは独立しています。ネームスペースを作成する必要がないので、Symbolで単にモザイクを作成しましょう:
async def create_mosaic(facade, signer_key_pair):
embedded_transactions = [
facade.transaction_factory.create_embedded({
'type': 'mosaic_definition_transaction_v1',
'signer_public_key': signer_key_pair.public_key,
'nonce': 3,
'divisibility': 0
}),
facade.transaction_factory.create_embedded({
'type': 'mosaic_supply_change_transaction_v1',
'signer_public_key': signer_key_pair.public_key,
'mosaic_id': generate_mosaic_id(facade.network.public_key_to_address(signer_key_pair.public_key), 3),
'delta': 1000,
'action': 'increase'
})
]
merkle_hash = facade.hash_embedded_transactions(embedded_transactions)
await prepare_and_send_transaction(facade, signer_key_pair, {
'type': 'aggregate_complete_transaction_v2',
'signer_public_key': signer_key_pair.public_key,
'transactions_hash': merkle_hash,
'transactions': embedded_transactions
})
原子性のために、モザイクを作成するためには、2つのトランザクションで構成されるアグリゲートを使用します。モザイクの所有者である1つのアカウントが、アグリゲートとそのすべての埋め込まれたトランザクションの署名者として使用されます。
最初の埋め込みトランザクションは、mosaic_definition_transaction_v1のタイプのモザイク定義トランザクションです。nonceはモザイクの所有者にスコープが限定され、グローバルに一意なモザイクIDを導出するために使用されます。divisibilityは、小数点以下の単位がサポートされていないことを示すためにゼロに設定されています。フラグは設定されていないため、デフォルトの値が使用されます:不変の供給、転送不可、制限不可、取消不可。この例の目的では、唯一の重要なフラグは転送可能フラグであり、これは設定されていてはなりません。
2つ目の埋め込みトランザクションは、mosaic_supply_change_transaction_v1のタイプのモザイク供給変更トランザクションです。mosaic_idは、モザイクの所有者のアドレスと、前のトランザクションで指定されたnonceを使用して指定していることに注意してください。deltaとactionは、モザイクの単位を1,000増やすことを示しています。NEMでは、初期のモザイク供給はモザイクの定義の一部として設定できますが、Symbolでは供給の変更は常にモザイク供給変更トランザクションを使用して行う必要があります。
モザイクの送信
任意のモザイクを送信するためのヘルパー関数を作成しましょう:
async def send_mosaic(facade, signer_key_pair, mosaic_id, recipient_address, amount):
await prepare_and_send_transaction(facade, signer_key_pair, {
'type': 'transfer_transaction_v1',
'recipient_address': recipient_address,
'mosaics': [
{'mosaic_id': mosaic_id, 'amount': amount}
]
})
最初に、モザイクを送信するために使用されるtransfer_transaction_v1のタイプのトランザクションを作成します。1つのモザイクのみを送信しているため、mosaicsには1つのアイテムしかありません。渡されたrecipient_address、mosaic_id、およびamountは、転送トランザクション内で適切に設定されます。
モザイク作成者から他人への送信
モザイクを作成してモザイクを送信できるようにするためのコードを書いてみましょう:
await create_mosaic(facade, signer_key_pair)
新しいモザイクの所有者のアカウント(Alice)から、別のアカウント(Bob)に10ユニットのモザイクを送信しましょう:
mosaic_id = generate_mosaic_id(facade.network.public_key_to_address(signer_key_pair.public_key), 3)
await send_mosaic(facade, signer_key_pair, mosaic_id, recipient_address, 10)
トランザクションの後、以下の状態変化が起こるはずです:
Alice -X XEM - トランザクション手数料の支払い
Alice -10 MOSAIC - モザイクの転送
Bob +10 MOSAIC - モザイクの転送
他人から他人への送信
BobからAliceにモザイクの5ユニットを送信しましょう:
await send_mosaic(facade, recipient_key_pair, mosaic_id, signer_address, 5)
トランザクションの後、以下の状態変化が起こるはずです:
Alice +5 MOSAIC - モザイクの転送
Bob -X XEM - トランザクション手数料の支払い
Bob -5 MOSAIC - モザイクの転送
他人から他人への送信 (直接)
Bobから別のアカウント(Charlie)にモザイクの3ユニットを送信しようとしてみましょう:
await send_mosaic(facade, recipient_key_pair, mosaic_id, other_address, 3)
この操作は、モザイクが転送不可であるため、エラー「Failure_Mosaic_Non_Transferable」で失敗します!
他人から他人への送信 (間接)
お気に入りのホスティングプラットフォーム上にクラウドサービス(またはLambda関数)があると仮定しましょう。Bobは、モザイクの3ユニットをCharlieに転送するために必要なすべての情報をサービスに提供します。サービスは、3つの埋め込みトランザクションで構成された部分的に署名されたアグリゲートを返します:
BobからAliceへのモザイクユニット3の転送
AliceからCharlieへのモザイクユニット3の転送
BobからAliceへのレビー/税金の支払い
Bobが手数料に同意する場合、彼はトランザクションに署名してネットワークに送信できます。モザイクの所有者であるAliceが中間業者として機能しているため、非転送可能の違反はありません。Bobが同意しない場合、彼は部分的に署名されたトランザクションを破棄し、転送は実行されません。実際、この外部サービスは他のブロックチェーンのオラクルと非常に似た役割を果たしています。
具体的には、アグリゲートは次のようになります:
async def send_mosaic_with_levy(facade, alice_key_pair, bob_key_pair, charlie_address, mosaic_id, amount):
currency_mosaic_id = await get_currency_mosaic_id()
alice_address = facade.network.public_key_to_address(alice_key_pair.public_key)
embedded_transactions = [
facade.transaction_factory.create_embedded({
'type': 'transfer_transaction_v1',
'signer_public_key': bob_key_pair.public_key,
'recipient_address': alice_address,
'mosaics': [
{'mosaic_id': mosaic_id, 'amount': amount}
]
}),
facade.transaction_factory.create_embedded({
'type': 'transfer_transaction_v1',
'signer_public_key': alice_key_pair.public_key,
'recipient_address': charlie_address,
'mosaics': [
{'mosaic_id': mosaic_id, 'amount': amount}
]
}),
facade.transaction_factory.create_embedded({
'type': 'transfer_transaction_v1',
'signer_public_key': bob_key_pair.public_key,
'recipient_address': alice_address,
'mosaics': [
{'mosaic_id': currency_mosaic_id, 'amount': 1_000000}
]
})
]
merkle_hash = facade.hash_embedded_transactions(embedded_transactions)
await prepare_and_send_transaction(facade, alice_key_pair, {
'type': 'aggregate_complete_transaction_v2',
'signer_public_key': alice_key_pair.public_key,
'transactions_hash': merkle_hash,
'transactions': embedded_transactions
}, cosignatory_key_pairs=[bob_key_pair])
前述の通り、このアグリゲートは3つの転送トランザクションで構成されています。AliceとBobの両方によって署名されています。
リフレクション
モザイクの転送を促進するための中央集権的なオラクルを持つことは、十分に分散化されていないと主張することができます。確かに、これは分散化と柔軟性の間の典型的なトレードオフです。転送可能フラグが設定されていないモザイクを使用することで、レビー/税金の支払いの回避策がないことを確認できます。中央化されたサービスの使用は、YENの価格に関連する動的な手数料戦略の使用を可能にします。これらの両方は、NEMのレビー実装に比べて改善されています!
分散化の低減に関係なく、こうしたサービスの価値が見えてくるといいですね。これらのサービスは、より伝統的なクラウド開発のスキルを使用し、Symbolを拡張するための別の手段を提供します。多くの可能性があります。実際、Symbolの部分トランザクションロジックをシミュレートするサービスを構築することもできます。ただし、アグリゲートボンデッドトランザクションを作成するのではなく、アグリゲートコンプリートトランザクションを作成し、ハッシュロックトランザクションの要件を回避することができます!