中級
レシートからのネームスペース解決
ネームスペース のエイリアスは 時間の経過とともに変更される 可能性があるため、ネームスペースの現在の値が トランザクション 承認時の値と一致しない場合があります。
生のアドレスや モザイク ID の代わりにエイリアスを使用するたびに、ネットワークは承認時にエイリアスが指していた実際の値を記録した レシート を保存します。ネームスペースの解決を記録するレシートの種類は 解決ステートメント と呼ばれます。
このチュートリアルでは、解決ステートメントを照会して、承認済みトランザクションで使用されたネームスペースエイリアスの背後にある実際のアドレスとモザイクIDを見つける方法を説明します。
前提条件
開始する前に、 開発環境をセットアップ してください。
このチュートリアルではネットワークからのデータの読み取りのみを行います。トランザクションの送信は行わないため、 アカウント や XYM の残高は必要ありません。
完全なコード
このチュートリアルの完全なコード一覧を以下に示します。
詳細な手順ごとの説明は次のセクションで行います。
import json
import os
import urllib.request
from symbolchain.symbol.IdGenerator import is_mosaic_alias
from symbolchain.symbol.Network import Address
NODE_URL = os . getenv (
'NODE_URL' , 'https://reference.symboltest.net:3001' )
print ( f 'Using node { NODE_URL } ' )
# Hash of a confirmed tx that used a namespace alias
TX_HASH = os . getenv ( 'TRANSACTION_HASH' ,
'BA0C65DB752A3BF1B25285540642537ECE8C2CA716577EDF8BF0F8597A85ADC4' )
print ( f 'Transaction hash: { TX_HASH } ' )
try :
# Retrieve the confirmed transaction
tx_path = f '/transactions/confirmed/ { TX_HASH } '
print ( f 'Fetching transaction from { tx_path } ' )
with urllib . request . urlopen ( f ' { NODE_URL }{ tx_path } ' ) as response :
tx_data = json . loads ( response . read () . decode ())
block_height = tx_data [ 'meta' ][ 'height' ]
print ( f ' Block height: { block_height } ' )
# primaryId is 1-based, meta.index is 0-based
tx_index = int ( tx_data [ 'meta' ][ 'index' ])
tx_primary = tx_index + 1
print ( f ' Transaction index: { tx_index } (primaryId: { tx_primary } )' )
recipient_hex = tx_data [ 'transaction' ][ 'recipientAddress' ]
recipient_bytes = bytes . fromhex ( recipient_hex )
is_address_alias = ( recipient_bytes [ 0 ] & 0x01 ) == 1
print ( f ' Recipient: { recipient_hex } ' )
print ( f ' Is address alias: { is_address_alias } ' )
aliased_mosaics = set ()
mosaics = tx_data [ 'transaction' ][ 'mosaics' ]
for mosaic in mosaics :
mosaic_id = int ( mosaic [ 'id' ], 16 )
is_alias = is_mosaic_alias ( mosaic_id )
if is_alias :
aliased_mosaics . add ( mosaic [ 'id' ])
print ( f ' Mosaic: { mosaic [ "id" ] } ' )
print ( f ' Is mosaic alias: { is_alias } ' )
# Query address resolution statements
if is_address_alias :
address_path = ( '/statements/resolutions/address'
f '?height= { block_height } ' )
print ( f ' \n Fetching address resolutions from { address_path } ' )
with urllib . request . urlopen (
f ' { NODE_URL }{ address_path } ' ) as response :
address_data = json . loads ( response . read () . decode ())
address_statements = address_data [ 'data' ]
print ( f ' Found { len ( address_statements ) } '
+ ' resolution statement(s)' )
for item in address_statements :
statement = item [ 'statement' ]
if statement [ 'unresolved' ] != recipient_hex :
continue
resolved = None
for entry in statement [ 'resolutionEntries' ]:
source = entry [ 'source' ]
if source [ 'primaryId' ] <= tx_primary :
resolved = entry [ 'resolved' ]
if resolved :
address = Address . from_decoded_address_hex_string ( resolved )
print ( ' \n Address resolution:' )
print ( f ' Unresolved: { statement [ "unresolved" ] } ' )
print ( f ' Resolved: { address } ' )
# Query mosaic resolution statements
if len ( aliased_mosaics ):
mosaic_path = ( '/statements/resolutions/mosaic'
f '?height= { block_height } ' )
print ( f ' \n Fetching mosaic resolutions from { mosaic_path } ' )
with urllib . request . urlopen (
f ' { NODE_URL }{ mosaic_path } ' ) as response :
mosaic_data = json . loads ( response . read () . decode ())
mosaic_statements = mosaic_data [ 'data' ]
print ( f ' Found { len ( mosaic_statements ) } resolution statement(s)' )
for item in mosaic_statements :
statement = item [ 'statement' ]
if statement [ 'unresolved' ] not in aliased_mosaics :
continue
resolved = None
for entry in statement [ 'resolutionEntries' ]:
source = entry [ 'source' ]
if source [ 'primaryId' ] <= tx_primary :
resolved = entry [ 'resolved' ]
if resolved :
print ( ' \n Mosaic resolution:' )
print ( f ' Unresolved: { statement [ "unresolved" ] } ' )
print ( f ' Resolved: { resolved } ' )
except Exception as e :
print ( e )
Download source
import { Address , isMosaicAlias } from 'symbol-sdk/symbol' ;
const NODE_URL = process . env . NODE_URL ||
'https://reference.symboltest.net:3001' ;
console . log ( 'Using node' , NODE_URL );
// Hash of a confirmed tx that used a namespace alias
const TX_HASH = process . env . TRANSACTION_HASH ||
'BA0C65DB752A3BF1B25285540642537ECE8C2CA716577EDF8BF0F8597A85ADC4' ;
console . log ( 'Transaction hash:' , TX_HASH );
try {
// Retrieve the confirmed transaction
const txPath = `/transactions/confirmed/ ${ TX_HASH } ` ;
console . log ( 'Fetching transaction from' , txPath );
const txResponse = await fetch ( ` ${ NODE_URL }${ txPath } ` );
const txData = await txResponse . json ();
const blockHeight = txData . meta . height ;
console . log ( ' Block height:' , blockHeight );
// primaryId is 1-based, meta.index is 0-based
const txIndex = txData . meta . index ;
const txPrimary = txIndex + 1 ;
console . log (
` Transaction index: ${ txIndex } (primaryId: ${ txPrimary } )` );
const recipientHex = txData . transaction . recipientAddress ;
const recipientBytes = Buffer . from ( recipientHex , 'hex' );
const isAddressAlias = ( recipientBytes [ 0 ] & 0x01 ) === 1 ;
console . log ( ' Recipient:' , recipientHex );
console . log ( ' Is address alias:' , isAddressAlias );
const aliasedMosaics = new Set ();
const mosaics = txData . transaction . mosaics ;
for ( const mosaic of mosaics ) {
const mosaicId = BigInt ( `0x ${ mosaic . id } ` );
const isAlias = isMosaicAlias ( mosaicId );
if ( isAlias ) aliasedMosaics . add ( mosaic . id );
console . log ( ` Mosaic: ${ mosaic . id } ` );
console . log ( ` Is mosaic alias: ${ isAlias } ` );
}
// Query address resolution statements
if ( isAddressAlias ) {
const addressPath =
'/statements/resolutions/address'
+ `?height= ${ blockHeight } ` ;
console . log ( '\nFetching address resolutions from' , addressPath );
const addressResponse =
await fetch ( ` ${ NODE_URL }${ addressPath } ` );
const addressData = await addressResponse . json ();
const addressStatements = addressData . data ;
console . log ( ` Found ${ addressStatements . length } `
+ ' resolution statement(s)' );
for ( const item of addressStatements ) {
const statement = item . statement ;
if ( statement . unresolved !== recipientHex )
continue ;
let resolved = null ;
for ( const entry of
statement . resolutionEntries ) {
if ( entry . source . primaryId <= txPrimary )
resolved = entry . resolved ;
}
if ( resolved ) {
const address =
Address . fromDecodedAddressHexString ( resolved );
console . log ( '\nAddress resolution:' );
console . log ( ' Unresolved:' , statement . unresolved );
console . log ( ` Resolved: ${ address } ` );
}
}
}
// Query mosaic resolution statements
if ( aliasedMosaics . size ) {
const mosaicPath =
'/statements/resolutions/mosaic'
+ `?height= ${ blockHeight } ` ;
console . log ( '\nFetching mosaic resolutions from' , mosaicPath );
const mosaicResponse = await fetch ( ` ${ NODE_URL }${ mosaicPath } ` );
const mosaicData = await mosaicResponse . json ();
const mosaicStatements = mosaicData . data ;
console . log (
` Found ${ mosaicStatements . length } `
+ ' resolution statement(s)' );
for ( const item of mosaicStatements ) {
const statement = item . statement ;
if ( ! aliasedMosaics . has ( statement . unresolved ))
continue ;
let resolved = null ;
for ( const entry of
statement . resolutionEntries ) {
if ( entry . source . primaryId <= txPrimary )
resolved = entry . resolved ;
}
if ( resolved ) {
console . log ( '\nMosaic resolution:' );
console . log ( ' Unresolved:' , statement . unresolved );
console . log ( ` Resolved: ${ resolved } ` );
}
}
}
} catch ( e ) {
console . error ( e . message );
}
Download source
コード解説
トランザクションハッシュの定義
コードは、生のアドレスやモザイクIDの代わりにネームスペースエイリアスを使用した、承認済みトランザクションのハッシュを定義します。この ハッシュは TRANSACTION_HASH 環境変数から読み取られ、デフォルトでは Symbol テストネット 上の既知のトランザクションが使用されます。
承認済みトランザクションの取得
承認済みトランザクションは、そのハッシュを使用して /transactions/confirmed/{transactionId} GET エンドポイントから取得されます。
レスポンスには、トランザクションが承認された ブロック の高さ( height )を含む meta オブジェクトが含まれます。この高さは、その特定のブロックの解決ステートメントを照会するために必要です。
トランザクションのメタデータには index フィールドも含まれており、これはブロック内でのトランザクションの0から始まる位置を示します。コードは、後の解決エントリとの照合のために、これを1から始まる primaryId ( index + 1 )に変換します。
取得されたトランザクションには recipientAddress フィールドが含まれます。トランザクションが受信者としてネームスペースエイリアスを使用した場合、このフィールドには実際のアドレスではなく、未解決の値(エンコードされたネームスペースID)が保持されます。
コードは、最初のバイトの最下位ビットを調べることで、受信者がエイリアスであるかどうかを確認します:
ビットが 1 の場合、アドレスは ネームスペースのアドレスへのリンク チュートリアルで説明されているように、エンコードされたネームスペースエイリアスです。
ビットが 0 の場合、受信者は通常のアドレスであり、解決は不要です。
コードはトランザクションの mosaics 配列を反復処理し、SDK の IdGenerator.is_mosaic_alias isMosaicAlias 関数を使用して、各モザイクIDがネームスペースエイリアスかどうかを確認します。この関数は64ビット値のビット63(最上位ビット)を検査します:
ビットが 1 の場合、値は ネームスペースのモザイクへのリンク チュートリアルで説明されているように、モザイクエイリアスとして使用されるネームスペースIDです。
ビットが 0 の場合、値は通常のモザイクIDであり、解決は不要です。
アドレス解決ステートメントの照会
トランザクションメタデータのブロック高を使用して、コードは /statements/resolutions/address GET エンドポイントを照会し、そのブロックのアドレス解決ステートメントを取得します。
各解決ステートメントには以下が含まれます:
Height : 解決が発生したブロック。
Unresolved : トランザクションで使用された(アドレスとしてエンコードされた)ネームスペースエイリアス。
Resolution entries : 承認時点でのエイリアスと実際のアドレスをマッピングする配列。
エンドポイントはブロックのすべてのアドレス解決ステートメントを返します。異なるネームスペースエイリアスが使用された場合、単一のブロックに複数の解決ステートメントが含まれる可能性があるため、コードは unresolved フィールドがトランザクションの recipientAddress と一致しないステートメントをスキップします。
同じブロック内のトランザクション間でエイリアスが別の値に再リンクされた場合、各ステートメントは複数のエントリを持つことができます。連続するトランザクションが同じ値に解決される場合、保存されるエントリは1つだけです。
各解決エントリには以下が含まれます:
Source : primaryId (ブロック内のトランザクションの1から始まるインデックス)および secondaryId ( アグリゲートトランザクション 内の1から始まるインデックス、またはスタンドアロントランザクションの場合は 0 )を使用して、解決された値がどのトランザクションから適用されるかを示します。
Resolved : 指定されたソース以降、エイリアスが指していた実際のアドレス。
分析対象のトランザクションの解決された値を特定するために、コードは primaryId がトランザクションの primaryId 以下である最後のエントリを見つけます。これが、エイリアスが現在何を指しているかに関係なく、また同じブロック内でエイリアスが複数回定義されたとしても、ネットワークがその特定のトランザクションに使用した値です。
モザイク解決ステートメントの照会
同じトランザクションでは、生のモザイクIDの代わりに symbol.xym をモザイクエイリアスとしても使用しています。コードは同じブロック高で /statements/resolutions/mosaic GET エンドポイントを照会し、モザイク解決ステートメントを取得します。
アドレス解決と同様に、 unresolved フィールドがトランザクションのエイリアス化されたモザイクIDと一致しないステートメントはスキップされます。
レスポンスはアドレス解決と同じ構造に従いますが、 resolved フィールドにはアドレスではなくモザイクIDが含まれます。同じ照合ロジックが適用され、 primaryId がトランザクションの primaryId 以下である最後のエントリが解決されたモザイクIDを与えます。
出力
以下に示す出力は、プログラムの典型的な実行結果に対応しています。
Using node https://reference.symboltest.net:3001
Transaction hash: BA0C65DB752A3BF1B25285540642537ECE8C2CA716577EDF8BF0F8597A85ADC4
Fetching transaction from /transactions/confirmed/BA0C65DB752A3BF1B25285540642537ECE8C2CA716577EDF8BF0F8597A85ADC4
Block height: 3125903
Transaction index: 0 (primaryId: 1)
Recipient: 99EC0888813BE133E6000000000000000000000000000000
Is address alias: True
Mosaic: E74B99BA41F4AFEE
Is mosaic alias: True
Fetching address resolutions from /statements/resolutions/address?height=3125903
Found 1 resolution statement(s)
Address resolution:
Unresolved: 99EC0888813BE133E6000000000000000000000000000000
Resolved: TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI
Fetching mosaic resolutions from /statements/resolutions/mosaic?height=3125903
Found 1 resolution statement(s)
Mosaic resolution:
Unresolved: E74B99BA41F4AFEE
Resolved: 72C0212E67A08BCE
出力の主なポイント:
結論
このチュートリアルでは、以下の方法を説明しました: