中級
ルートネームスペースの登録
ネームスペース は、アカウント や モザイク に対して人間が読み取り可能なエイリアス(別名)を提供します。これにより、長いアドレスや16進数のモザイクIDの代わりに使用することができます。
このチュートリアルでは、ルートネームスペース を登録し、そのレンタル 有効期間 を設定する方法を説明します。
一度登録した後は、次のステップ で説明するように、ネームスペースをモザイクやアカウントにリンクするための追加ステップが必要です。
前提条件
開始する前に、以下を確認してください。
さらに、トランザクションがどのようにアナウンスされ承認されるかを理解するために、転送トランザクション のチュートリアルを復習しておいてください。
完全なコード
このチュートリアルの完全なコード一覧を以下に示します。
詳細な手順ごとの説明は次のセクションで行います。
import json
import os
import time
import urllib.request
from symbolchain.CryptoTypes import PrivateKey
from symbolchain.facade.SymbolFacade import SymbolFacade
from symbolchain.symbol.Network import NetworkTimestamp
from symbolchain.symbol.IdGenerator import generate_namespace_id
from symbolchain.sc import Amount
from symbolchain.symbol.Network import Address
NODE_URL = os . getenv (
'NODE_URL' , 'https://reference.symboltest.net:3001' )
print ( f 'Using node { NODE_URL } ' )
SIGNER_PRIVATE_KEY = os . getenv (
'SIGNER_PRIVATE_KEY' ,
'0000000000000000000000000000000000000000000000000000000000000000' )
signer_key_pair = SymbolFacade . KeyPair ( PrivateKey ( SIGNER_PRIVATE_KEY ))
facade = SymbolFacade ( 'testnet' )
signer_address = facade . network . public_key_to_address (
signer_key_pair . public_key )
print ( f 'Signer address: { signer_address } ' )
try :
# Fetch current network time
time_path = '/node/time'
print ( f 'Fetching current network time from { time_path } ' )
with urllib . request . urlopen ( f ' { NODE_URL }{ time_path } ' ) as response :
response_json = json . loads ( response . read () . decode ())
receive_timestamp = (
response_json [ 'communicationTimestamps' ][ 'receiveTimestamp' ])
timestamp = NetworkTimestamp ( int ( receive_timestamp ))
print ( f ' Network time: { timestamp . timestamp } ms since nemesis' )
# Fetch recommended fees
fee_path = '/network/fees/transaction'
print ( f 'Fetching recommended fees from { fee_path } ' )
with urllib . request . urlopen ( f ' { NODE_URL }{ fee_path } ' ) as response :
response_json = json . loads ( response . read () . decode ())
median_mult = response_json [ 'medianFeeMultiplier' ]
minimum_mult = response_json [ 'minFeeMultiplier' ]
fee_mult = max ( median_mult , minimum_mult )
print ( f ' Fee multiplier: { fee_mult } ' )
# Build the transaction
namespace_name = f 'ns_ { int ( time . time ()) } '
print ( f 'Creating root namespace: { namespace_name } ' )
transaction = facade . transaction_factory . create ({
'type' : 'namespace_registration_transaction_v1' ,
'signer_public_key' : signer_key_pair . public_key ,
'deadline' : timestamp . add_hours ( 2 ) . timestamp ,
'registration_type' : 'root' ,
'duration' : 86400 , # approximately 30 days
'name' : namespace_name
})
transaction . fee = Amount ( fee_mult * transaction . size )
# Sign transaction and generate final payload
signature = facade . sign_transaction ( signer_key_pair , transaction )
json_payload = facade . transaction_factory . attach_signature (
transaction , signature )
print ( 'Built transaction:' )
print ( json . dumps ( transaction . to_json (), indent = 2 ))
transaction_hash = facade . hash_transaction ( transaction )
print ( f 'Transaction hash: { transaction_hash } ' )
# Announce transaction
print ( 'Announcing namespace registration to /transactions' )
request = urllib . request . Request (
f ' { NODE_URL } /transactions' ,
data = json_payload . encode (),
headers = { 'Content-Type' : 'application/json' },
method = 'PUT'
)
with urllib . request . urlopen ( request ) as response :
print ( f ' Response: { response . read () . decode () } ' )
# Wait for confirmation
print ( 'Waiting for namespace registration confirmation...' )
for attempt in range ( 60 ):
time . sleep ( 1 )
try :
status_url = (
f ' { NODE_URL } /transactionStatus/ { transaction_hash } ' )
with urllib . request . urlopen ( status_url ) as response :
status = json . loads ( response . read () . decode ())
print ( f ' Transaction status: { status [ "group" ] } ' )
if status [ 'group' ] == 'confirmed' :
print ( 'Namespace registration confirmed in' ,
attempt , 'seconds' )
break
if status [ 'group' ] == 'failed' :
raise Exception ( 'Namespace registration failed:' ,
status [ 'code' ])
except urllib . error . HTTPError :
print ( ' Transaction status: unknown' )
# Retrieve the namespace
namespace_id = generate_namespace_id ( namespace_name )
print ( f 'Namespace ID: { namespace_id } ( { hex ( namespace_id ) } )' )
namespace_path = f '/namespaces/ { namespace_id : x } '
print ( f 'Fetching namespace information from { namespace_path } ' )
with urllib . request . urlopen (
f ' { NODE_URL }{ namespace_path } ' ) as response :
response_json = json . loads ( response . read () . decode ())
namespace_info = response_json [ 'namespace' ]
print ( 'Namespace information:' )
reg_type = namespace_info [ "registrationType" ]
print ( f ' Registration type: { reg_type } ' )
owner_address = Address . from_decoded_address_hex_string (
namespace_info [ "ownerAddress" ])
print ( f ' Owner address: { owner_address } ' )
print ( f ' Start height: { namespace_info [ "startHeight" ] } ' )
print ( f ' End height: { namespace_info [ "endHeight" ] } ' )
except Exception as e :
print ( e )
Download source
import { PrivateKey } from 'symbol-sdk' ;
import {
SymbolFacade ,
NetworkTimestamp ,
Address ,
models ,
generateNamespaceId
} from 'symbol-sdk/symbol' ;
const NODE_URL = process . env . NODE_URL ||
'https://reference.symboltest.net:3001' ;
console . log ( 'Using node' , NODE_URL );
const SIGNER_PRIVATE_KEY = process . env . SIGNER_PRIVATE_KEY ||
'0000000000000000000000000000000000000000000000000000000000000000' ;
const signerKeyPair = new SymbolFacade . KeyPair (
new PrivateKey ( SIGNER_PRIVATE_KEY ));
const facade = new SymbolFacade ( 'testnet' );
const signerAddress = facade . network . publicKeyToAddress (
signerKeyPair . publicKey );
console . log ( 'Signer address:' , signerAddress . toString ());
try {
// Fetch current network time
const timePath = '/node/time' ;
console . log ( 'Fetching current network time from' , timePath );
const timeResponse = await fetch ( ` ${ NODE_URL }${ timePath } ` );
const timeJSON = await timeResponse . json ();
const receiveTimestamp =
timeJSON . communicationTimestamps . receiveTimestamp ;
const timestamp = new NetworkTimestamp ( receiveTimestamp );
console . log ( ' Network time:' , timestamp . timestamp ,
'ms since nemesis' );
// Fetch recommended fees
const feePath = '/network/fees/transaction' ;
console . log ( 'Fetching recommended fees from' , feePath );
const feeResponse = await fetch ( ` ${ NODE_URL }${ feePath } ` );
const feeJSON = await feeResponse . json ();
const medianMult = feeJSON . medianFeeMultiplier ;
const minimumMult = feeJSON . minFeeMultiplier ;
const feeMult = Math . max ( medianMult , minimumMult );
console . log ( ' Fee multiplier:' , feeMult );
// Build the transaction
const namespaceName = `ns_ ${ Date . now () } ` ;
console . log ( 'Creating root namespace:' , namespaceName );
const transaction = facade . transactionFactory . create ({
type : 'namespace_registration_transaction_v1' ,
signerPublicKey : signerKeyPair . publicKey . toString (),
deadline : timestamp . addHours ( 2 ). timestamp ,
registrationType : 'root' ,
duration : 86400n , // approximately 30 days
name : namespaceName
});
transaction . fee = new models . Amount ( feeMult * transaction . size );
// Sign transaction and generate final payload
const signature = facade . signTransaction (
signerKeyPair , transaction );
const jsonPayload =
facade . transactionFactory . static . attachSignature (
transaction , signature );
console . log ( 'Built transaction:' );
console . dir ( transaction . toJson (), { colors : true });
const transactionHash =
facade . hashTransaction ( transaction ). toString ();
console . log ( 'Transaction hash:' , transactionHash );
// Announce transaction
console . log ( 'Announcing namespace registration to /transactions' );
const announceResponse = await fetch ( ` ${ NODE_URL } /transactions` , {
method : 'PUT' ,
headers : { 'Content-Type' : 'application/json' },
body : jsonPayload
});
console . log ( ' Response:' , await announceResponse . text ());
// Wait for confirmation
console . log ( 'Waiting for namespace registration confirmation...' );
for ( let attempt = 0 ; attempt < 60 ; attempt ++ ) {
await new Promise ( resolve => setTimeout ( resolve , 1000 ));
try {
const statusUrl =
` ${ NODE_URL } /transactionStatus/ ${ transactionHash } ` ;
const statusResponse = await fetch ( statusUrl );
if ( ! statusResponse . ok ) {
console . log ( ' Transaction status: unknown' );
continue ;
}
const status = await statusResponse . json ();
console . log ( ' Transaction status:' , status . group );
if ( status . group === 'confirmed' ) {
console . log ( 'Namespace registration confirmed in' ,
attempt , 'seconds' );
break ;
}
if ( status . group === 'failed' ) {
throw new Error (
`Namespace registration failed: ${ status . code } ` );
}
} catch ( error ) {
if ( error . message . includes ( 'failed:' )) {
throw error ;
}
console . log ( ' Transaction status: unknown' );
}
}
// Retrieve the namespace
const namespaceId = generateNamespaceId ( namespaceName );
console . log (
'Namespace ID:' ,
` ${ namespaceId } (0x ${ namespaceId . toString ( 16 ) } )` );
const namespacePath =
`/namespaces/ ${ namespaceId . toString ( 16 ) } ` ;
console . log (
'Fetching namespace information from' , namespacePath );
const namespaceResponse =
await fetch ( ` ${ NODE_URL }${ namespacePath } ` );
const namespaceJSON = await namespaceResponse . json ();
const namespaceInfo = namespaceJSON . namespace ;
console . log ( 'Namespace information:' );
console . log (
' Registration type:' , namespaceInfo . registrationType );
const ownerAddress = Address . fromDecodedAddressHexString (
namespaceInfo . ownerAddress );
console . log ( ' Owner address:' , ownerAddress . toString ());
console . log ( ' Start height:' , namespaceInfo . startHeight );
console . log ( ' End height:' , namespaceInfo . endHeight );
} catch ( e ) {
console . error ( e . message );
}
Download source
コード解説
アカウントの設定
このスニペットは、署名者の 秘密鍵 を SIGNER_PRIVATE_KEY 環境変数から読み取ります。設定されていない場合は、デフォルトのテストキーが使用されます。
署名者の アドレス は 公開鍵 から派生します。
このアカウントが、登録されたネームスペースを所有することになります。
ネットワーク時間と手数料の取得
転送トランザクション チュートリアルで説明されているプロセスに従い、ネットワーク時間と推奨手数料をそれぞれ /node/time GET および /network/fees/transaction GET から取得します。
トランザクションの構築
# Build the transaction
namespace_name = f 'ns_ { int ( time . time ()) } '
print ( f 'Creating root namespace: { namespace_name } ' )
transaction = facade . transaction_factory . create ({
'type' : 'namespace_registration_transaction_v1' ,
'signer_public_key' : signer_key_pair . public_key ,
'deadline' : timestamp . add_hours ( 2 ) . timestamp ,
'registration_type' : 'root' ,
'duration' : 86400 , # approximately 30 days
'name' : namespace_name
})
transaction . fee = Amount ( fee_mult * transaction . size )
// Build the transaction
const namespaceName = `ns_ ${ Date . now () } ` ;
console . log ( 'Creating root namespace:' , namespaceName );
const transaction = facade . transactionFactory . create ({
type : 'namespace_registration_transaction_v1' ,
signerPublicKey : signerKeyPair . publicKey . toString (),
deadline : timestamp . addHours ( 2 ). timestamp ,
registrationType : 'root' ,
duration : 86400n , // approximately 30 days
name : namespaceName
});
ネームスペース登録トランザクションでは以下を指定します。
Type: ネームスペース登録トランザクションにはタイプ namespace_registration_transaction_v1 を使用します。
登録タイプ: root という値は、ルートネームスペースが作成されることを示します。
代わりに サブネームスペースを登録 するには child を使用してください。
有効期間: ネームスペースがレンタルされるブロック数。
最小期間は 86,400 ブロック(約30日)、最大期間は 5,256,000 ブロック(約5年)です。
名前: ルートネームスペースの名前。
名前には、英小文字、数字、ハイフン、アンダースコアのみを使用でき、数字または文字で始まる必要があり、最大64文字までです。
チュートリアルを複数回実行してもネームスペース名が重複しないように、名前にタイムスタンプが付加されています。実用的なプログラムでは、ネームスペースに固定の名前を使用します。
ネームスペースレンタル手数料
標準の トランザクション手数料 に加えて、ネームスペースの登録には、要求された期間に比例した レンタル手数料 が必要です。
トランザクション手数料とは異なり、レンタル手数料はトランザクションリクエストには 含まれません 。
登録トランザクションが承認されると、ネットワークによって トランザクション署名者のアカウント から自動的に計算され、差し引かれます。
レンタル手数料の額は、 /network/fees/rental GET エンドポイントを使用して事前に計算できます。
トランザクションの送信
# Sign transaction and generate final payload
signature = facade . sign_transaction ( signer_key_pair , transaction )
json_payload = facade . transaction_factory . attach_signature (
transaction , signature )
print ( 'Built transaction:' )
print ( json . dumps ( transaction . to_json (), indent = 2 ))
transaction_hash = facade . hash_transaction ( transaction )
print ( f 'Transaction hash: { transaction_hash } ' )
# Announce transaction
print ( 'Announcing namespace registration to /transactions' )
request = urllib . request . Request (
f ' { NODE_URL } /transactions' ,
data = json_payload . encode (),
headers = { 'Content-Type' : 'application/json' },
method = 'PUT'
)
with urllib . request . urlopen ( request ) as response :
print ( f ' Response: { response . read () . decode () } ' )
// Sign transaction and generate final payload
const signature = facade . signTransaction (
signerKeyPair , transaction );
const jsonPayload =
facade . transactionFactory . static . attachSignature (
transaction , signature );
console . log ( 'Built transaction:' );
console . dir ( transaction . toJson (), { colors : true });
const transactionHash =
facade . hashTransaction ( transaction ). toString ();
console . log ( 'Transaction hash:' , transactionHash );
// Announce transaction
console . log ( 'Announcing namespace registration to /transactions' );
const announceResponse = await fetch ( ` ${ NODE_URL } /transactions` , {
method : 'PUT' ,
headers : { 'Content-Type' : 'application/json' },
body : jsonPayload
});
トランザクションは、転送トランザクションの作成 と同じプロセスに従って署名され、アナウンスされます。
コードはその後、ステータスが confirmed に変わるまで /transactionStatus/{hash} GET エンドポイントをポーリングして、トランザクションが承認されるのを待ちます。
ネームスペースの取得
ネームスペースが登録されたことを確認するために、コードは /namespaces/{namespaceId} GET エンドポイントを使用してネットワークからネームスペースを取得し、そのプロパティを表示します。
ネームスペース ID は IdGenerator.generate_namespace_id generateNamespaceId を使用して計算されます。
この関数はネームスペース名に決定論的なハッシュアルゴリズムを適用し、ネームスペース情報を照会するために必要な ID を生成します。
レスポンスが成功すれば、ネームスペースが登録され、ネットワーク上でアクティブであることが確認されます。
ネームスペースは登録されましたが、まだリンクされていません
ネームスペースが モザイク や アカウント のエイリアスとして機能するとき、初めて有用なものとなります。
次のステップ のガイドを使用して、ネームスペースを識別子にリンクしてください。
出力
以下に示す出力は、プログラムの典型的な実行結果に対応しています。
Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Fetching current network time from /node/time
Network time: 99282614227 ms since nemesis
Fetching recommended fees from /network/fees/transaction
Fee multiplier: 100
Creating root namespace: ns_1766533079
Built transaction:
{
"signature": "64F716B9F4CB265D7130FDBD6BABB93A3AE2CC31896D0D3C85DE7CAFDE3CC5053730FC56FCF64B3FCFD24213A9BF3432AC08D2C03766EAF847C86421D88EA603",
"signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
"version": 1,
"network": 152,
"type": 16718,
"fee": "15900",
"deadline": "99289814227",
"duration": "86400",
"id": "10704093651797406551",
"registration_type": 0,
"name": "6e735f31373636353333303739"
}
Transaction hash: 2E3ACFD140AEAA21E7839376C4290695E9E58761DC37B1E60D3207F76370CC22
Announcing namespace registration to /transactions
Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for namespace registration confirmation...
Transaction status: unconfirmed
Transaction status: unconfirmed
Transaction status: confirmed
Namespace registration confirmed in 11 seconds
Namespace ID: 10704093651797406551 (0x948c9476ade5f357)
Fetching namespace information from /namespaces/948c9476ade5f357
Namespace information:
Registration type: 0
Owner address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Start height: 2984442
End height: 3073722
出力の主なポイント:
ネームスペース名 (7行目): 選択された名前 ns_1766533079 には、一意性を確保するためにタイムスタンプが含まれています。
ネームスペースの詳細を表示するには、Symbol Testnet Explorer でこの名前を検索してください。
手数料 (15行目): 0.0159 XYM のトランザクション手数料は、トランザクションサイズに手数料倍率を乗じて計算されます。 レンタル手数料 は、トランザクションが承認された際にネットワークによって別途差し引かれます。
ID と名前 (18、20行目): id フィールドにはネームスペース ID が10進数で表示され、 name には16進数文字列としてエンコードされたネームスペース名が含まれます。例えば、 6e735f... は ns_1... にデコードされます。
ネームスペース ID (30行目): 18行目の id フィールドと一致するように、10進数と16進数の両方の表現が表示されます。
登録タイプ (33行目): 値 0 はルートネームスペースであることを示します(サブネームスペースの場合は 1 )。
所有者アドレス (34行目): ネームスペースを登録し、所有しているアカウント。
開始高と終了高 (35-36行目): ネームスペースはブロック 2984442 から 3073722 まで有効です。
終了高には、要求された期間を超えて 猶予期間 (テストネットでは1日、メインネットでは30日)が含まれており、所有者がネームスペースを他の人に利用可能になる前に更新する時間を与えます。
出力に印刷されたトランザクション ハッシュ を使用して、 Symbol Testnet Explorer でトランザクションを検索することもできます。
結論
このチュートリアルでは、以下の方法を説明しました。
次のステップ
ルートネームスペースを作成したので、以下のことができます。