コンテンツにスキップ

サブネームスペースの登録⚓︎

中級

サブネームスペース (「子」ネームスペースとも呼ばれます)は、 ネームスペース の階層構造を拡張します。

このチュートリアルでは、既存の ルートネームスペース の配下にサブネームスペースを登録する方法を説明します。

登録後は、 次のステップ で説明するように、ネームスペースをモザイクやアカウントにリンクするための追加ステップが必要です。

前提条件⚓︎

開始する前に、以下を確認してください:

さらに、トランザクションがどのようにアナウンスされ承認されるかを理解するために、 転送トランザクション のチュートリアルを復習しておいてください。

完全なコード⚓︎

このチュートリアルの完全なコード一覧を以下に示します。 詳細な手順ごとの説明は次のセクションで行います。

import json
import os
import time
import urllib.request

from symbolchain.CryptoTypes import PrivateKey
from symbolchain.facade.SymbolFacade import SymbolFacade
from symbolchain.symbol.IdGenerator import generate_namespace_id
from symbolchain.symbol.Network import Address

NODE_URL = os.getenv('NODE_URL', 'https://reference.symboltest.net:3001')
print(f'Using node {NODE_URL}')


# Helper function to announce a transaction
def announce_transaction(payload, label):
    print(f'Announcing {label} to /transactions')
    request = urllib.request.Request(
        f'{NODE_URL}/transactions',
        data=payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='PUT'
    )
    with urllib.request.urlopen(request) as announce_response:
        print(f'  Response: {announce_response.read().decode()}')


# Helper function to wait for transaction confirmation
def wait_for_confirmation(tx_hash, label):
    print(f'Waiting for {label} confirmation...')
    for attempt in range(60):
        time.sleep(1)
        try:
            url = f'{NODE_URL}/transactionStatus/{tx_hash}'
            with urllib.request.urlopen(url) as confirm_response:
                status = json.loads(confirm_response.read().decode())
                print(f'  Transaction status: {status["group"]}')
                if status['group'] == 'confirmed':
                    print(f'{label} confirmed in {attempt} seconds')
                    return
                if status['group'] == 'failed':
                    raise RuntimeError(
                        f'{label} failed: {status["code"]}')
        except urllib.error.HTTPError:
            print('  Transaction status: unknown')
    raise TimeoutError(f'{label} not confirmed after 60 seconds')


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 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_multiplier = response_json['medianFeeMultiplier']
        minimum_multiplier = response_json['minFeeMultiplier']
        fee_multiplier = max(median_multiplier, minimum_multiplier)
        print(f'  Fee multiplier: {fee_multiplier}')

    # Build the subnamespace name
    root_namespace_name = os.getenv('ROOT_NAMESPACE', 'ns_root')
    subnamespace_name = os.getenv(
        'SUBNAMESPACE', f'sub_{int(time.time())}')
    full_namespace_name = (
        f'{root_namespace_name}.{subnamespace_name}')
    print(f'Creating subnamespace: {full_namespace_name}')

    # Generate the parent namespace ID from the root namespace name
    parent_id = generate_namespace_id(root_namespace_name)
    print(f'  Parent namespace ID: 0x{parent_id:016X}')

    # Build the transaction
    transaction = facade.create_transaction_from_descriptor(
        {
            'type': 'namespace_registration_transaction_v1',
            'registration_type': 'child',
            'parent_id': parent_id,
            'name': subnamespace_name
        },
        signer_key_pair.public_key,
        fee_multiplier,
        2 * 60 * 60)

    # 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
    announce_transaction(json_payload, 'namespace registration')

    # Wait for confirmation
    wait_for_confirmation(transaction_hash, 'namespace registration')

    # Retrieve the namespace
    namespace_id = generate_namespace_id(
        subnamespace_name, parent_id)
    print(f'Child namespace ID: {namespace_id} (0x{namespace_id:016X})')

    namespace_path = f'/namespaces/{namespace_id:016X}'
    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"  Parent ID: {namespace_info['parentId']}")
        print(f"  Depth: {namespace_info['depth']}")
        print(f"  Level 0: {namespace_info['level0']}")
        if int(namespace_info['depth']) >= 1:
            print(f"  Level 1: {namespace_info['level1']}")
        if int(namespace_info['depth']) >= 2:
            if 'level2' in namespace_info:
                print(f"  Level 2: {namespace_info['level2']}")
        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 {
    Address,
    SymbolFacade,
    descriptors,
    generateNamespaceId,
    models
} from 'symbol-sdk/symbol';

const NODE_URL = process.env.NODE_URL ||
    'https://reference.symboltest.net:3001';
console.log('Using node', NODE_URL);

// Helper function to announce a transaction
async function announceTransaction(payload, label) {
    console.log(`Announcing ${label} to /transactions`);
    const response = await fetch(`${NODE_URL}/transactions`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: payload
    });
    console.log('  Response:', await response.text());
}

// Helper function to wait for transaction confirmation
async function waitForConfirmation(transactionHash, label) {
    console.log(`Waiting for ${label} confirmation...`);
    for (let attempt = 0; 60 > attempt; attempt++) {
        await new Promise(resolve => { setTimeout(resolve, 1000); });
        const response = await fetch(
            `${NODE_URL}/transactionStatus/${transactionHash}`);
        if (!response.ok) {
            if (404 === response.status) {
                console.log('  Transaction status: unknown');
                continue;
            }
            throw new Error(`HTTP ${response.status}`);
        }
        const status = await response.json();
        console.log('  Transaction status:', status.group);
        if ('confirmed' === status.group) {
            console.log(`${label} confirmed in`, attempt, 'seconds');
            return;
        }
        if ('failed' === status.group)
            throw new Error(`${label} failed: ${status.code}`);
    }
    throw new Error(`${label} not confirmed after 60 seconds`);
}

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 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 medianMultiplier = feeJSON.medianFeeMultiplier;
    const minimumMultiplier = feeJSON.minFeeMultiplier;
    const feeMultiplier = Math.max(medianMultiplier, minimumMultiplier);
    console.log('  Fee multiplier:', feeMultiplier);

    // Build the subnamespace name
    const rootNamespaceName = process.env.ROOT_NAMESPACE || 'ns_root';
    const subnamespaceName =
        process.env.SUBNAMESPACE || `sub_${Date.now()}`;
    const fullNamespaceName =
        `${rootNamespaceName}.${subnamespaceName}`;
    console.log('Creating subnamespace:', fullNamespaceName);

    // Generate the parent namespace ID from the root name
    const parentId = generateNamespaceId(rootNamespaceName);
    const parentIdHex = parentId.toString(16)
        .toUpperCase().padStart(16, '0');
    console.log('Parent namespace ID:', `0x${parentIdHex}`);

    // Build the transaction
    const transaction = facade.createTransactionFromTypedDescriptor(
        new descriptors.NamespaceRegistrationTransactionV1Descriptor(
            new models.NamespaceId(0n),
            models.NamespaceRegistrationType.CHILD,
            undefined,
            new models.NamespaceId(parentId),
            subnamespaceName),
        signerKeyPair.publicKey,
        feeMultiplier,
        2 * 60 * 60);

    // 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
    await announceTransaction(jsonPayload, 'namespace registration');

    // Wait for confirmation
    await waitForConfirmation(transactionHash, 'namespace registration');

    // Retrieve the namespace
    const namespaceId = generateNamespaceId(
        subnamespaceName, parentId);
    const namespaceIdHex = namespaceId.toString(16)
        .toUpperCase().padStart(16, '0');
    console.log(
        'Child namespace ID:',
        `${namespaceId} (0x${namespaceIdHex})`);

    const namespacePath = `/namespaces/${namespaceIdHex}`;
    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('  Parent ID:', namespaceInfo.parentId);
    console.log('  Depth:', namespaceInfo.depth);
    console.log('  Level 0:', namespaceInfo.level0);
    if (1 <= namespaceInfo.depth)
        console.log('  Level 1:', namespaceInfo.level1);
    if (2 <= namespaceInfo.depth && namespaceInfo.level2)
        console.log('  Level 2:', namespaceInfo.level2);
    console.log('  Start height:', namespaceInfo.startHeight);
    console.log('  End height:', namespaceInfo.endHeight);
} catch (e) {
    console.error(e.message);
}

Download source

//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.1

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.symbol.sdk.CryptoTypes;
import org.symbol.sdk.facade.SymbolFacade;
import org.symbol.sdk.symbol.Address;
import org.symbol.sdk.symbol.IdGenerator;
import org.symbol.sdk.symbol.KeyPair;
import org.symbol.sdk.symbol.SymbolTransactionFactory;
import org.symbol.sdk.symbol.descriptors.*;
import org.symbol.sdk.symbol.models.*;

public final class RegisterSubnamespace {
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    private static final HttpClient HTTP_CLIENT =
        HttpClient.newHttpClient();

    private final String nodeUrl = System.getenv().getOrDefault(
        "NODE_URL", "https://reference.symboltest.net:3001");

    private final SymbolFacade facade = new SymbolFacade("testnet");

    private void announceTransaction(
        final String payload,
        final String label
    ) throws IOException, InterruptedException {
        System.out.printf("Announcing %s to /transactions%n", label);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(nodeUrl + "/transactions"))
            .header("Content-Type", "application/json")
            .PUT(HttpRequest.BodyPublishers.ofString(payload))
            .build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        System.out.printf("  Response: %s%n", response.body());
    }

    private void waitForConfirmation(
        final String transactionHash,
        final String label
    ) throws IOException, InterruptedException {
        System.out.printf("Waiting for %s confirmation...%n", label);
        for (int attempt = 0; 60 > attempt; ++attempt) {
            Thread.sleep(1000);
            final String statusPath =
                "/transactionStatus/" + transactionHash;
            final HttpRequest statusRequest = HttpRequest.newBuilder(
                URI.create(nodeUrl + statusPath)).GET().build();
            final HttpResponse<String> statusResponse = HTTP_CLIENT
                .send(statusRequest, BodyHandlers.ofString());
            if (404 == statusResponse.statusCode()) {
                System.out.println("  Transaction status: unknown");
                continue;
            }
            if (2 != statusResponse.statusCode() / 100)
                throw new IOException(
                    "HTTP " + statusResponse.statusCode());

            final JsonNode status =
                JSON_MAPPER.readTree(statusResponse.body());
            final String group = status.get("group").asText();
            System.out.printf("  Transaction status: %s%n", group);
            if ("confirmed".equals(group)) {
                System.out.printf("%s confirmed in %d seconds%n",
                    label, attempt);
                return;
            }
            if ("failed".equals(group))
                throw new IOException(String.format("%s failed: %s",
                    label, status.get("code").asText()));
        }
        throw new IOException(String.format(
            "%s not confirmed after 60 seconds", label));
    }

    public static void main(final String[] args) {
        try {
            new RegisterSubnamespace().run();
        } catch (final Exception ex) {
            System.out.println(null == ex.getMessage()
                ? ex.toString()
                : ex.getMessage());
        }
    }

    private void run() throws IOException, InterruptedException {
        System.out.printf("Using node %s%n", nodeUrl);

        final String signerPrivateKey = System.getenv().getOrDefault(
            "SIGNER_PRIVATE_KEY", "0".repeat(64));
        final KeyPair signerKeyPair = new KeyPair(
            new CryptoTypes.PrivateKey(signerPrivateKey));

        final Address signerAddress = facade.network.publicKeyToAddress(
            signerKeyPair.getPublicKey());
        System.out.printf("Signer address: %s%n", signerAddress);

        // Fetch recommended fees
        final String feePath = "/network/fees/transaction";
        System.out.printf("Fetching recommended fees from %s%n", feePath);
        final JsonNode feeJson = getJson(feePath);
        final long feeMultiplier = Math.max(
            feeJson.get("medianFeeMultiplier").asLong(),
            feeJson.get("minFeeMultiplier").asLong());
        System.out.printf("  Fee multiplier: %d%n", feeMultiplier);

        // Build the subnamespace name
        final String rootNamespaceName = System.getenv().getOrDefault(
            "ROOT_NAMESPACE", "ns_root");
        final String subnamespaceName = System.getenv().getOrDefault(
            "SUBNAMESPACE", "sub_" + System.currentTimeMillis());
        final String fullNamespaceName =
            rootNamespaceName + "." + subnamespaceName;
        System.out.printf("Creating subnamespace: %s%n",
            fullNamespaceName);

        // Generate the parent namespace ID from the root name
        final long parentId = IdGenerator.generateNamespaceId(
            rootNamespaceName);
        System.out.printf("Parent namespace ID: 0x%016X%n", parentId);

        // Build the transaction
        final Transaction transaction =
            facade.createTransactionFromTypedDescriptor(
                new NamespaceRegistrationTransactionV1Descriptor(
                    new NamespaceId(0),
                    NamespaceRegistrationType.CHILD,
                    null,
                    new NamespaceId(parentId),
                    subnamespaceName.getBytes(StandardCharsets.UTF_8)),
                signerKeyPair.getPublicKey(),
                feeMultiplier,
                2 * 60 * 60);

        // Sign transaction and generate final payload
        final CryptoTypes.Signature signature = facade.signTransaction(
            signerKeyPair, transaction);
        final String jsonPayload = SymbolTransactionFactory
            .attachSignature(transaction, signature);
        System.out.println("Built transaction:");
        System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter()
            .writeValueAsString(transaction.toJson()));

        final String transactionHash =
            facade.hashTransaction(transaction).toString();
        System.out.printf("Transaction hash: %s%n", transactionHash);

        // Announce transaction
        announceTransaction(jsonPayload, "namespace registration");

        // Wait for confirmation
        waitForConfirmation(transactionHash, "namespace registration");

        // Retrieve the namespace
        final long namespaceId = IdGenerator.generateNamespaceId(
            subnamespaceName, parentId);
        System.out.printf("Child namespace ID: %s (0x%016X)%n",
            Long.toUnsignedString(namespaceId), namespaceId);

        final String namespacePath = "/namespaces/%016X".formatted(
            namespaceId);
        System.out.printf("Fetching namespace information from %s%n",
            namespacePath);
        final JsonNode namespaceInfo = getJson(namespacePath)
            .get("namespace");
        System.out.println("Namespace information:");
        System.out.printf("  Registration type: %s%n",
            namespaceInfo.get("registrationType").asText());
        final Address ownerAddress = Address
            .fromDecodedAddressHexString(
                namespaceInfo.get("ownerAddress").asText());
        System.out.printf("  Owner address: %s%n", ownerAddress);
        System.out.printf("  Parent ID: %s%n",
            namespaceInfo.get("parentId").asText());
        System.out.printf("  Depth: %s%n",
            namespaceInfo.get("depth").asText());
        System.out.printf("  Level 0: %s%n",
            namespaceInfo.get("level0").asText());
        if (1 <= namespaceInfo.get("depth").asInt())
            System.out.printf("  Level 1: %s%n",
                namespaceInfo.get("level1").asText());
        if (2 <= namespaceInfo.get("depth").asInt()
            && namespaceInfo.has("level2"))
            System.out.printf("  Level 2: %s%n",
                namespaceInfo.get("level2").asText());
        System.out.printf("  Start height: %s%n",
            namespaceInfo.get("startHeight").asText());
        System.out.printf("  End height: %s%n",
            namespaceInfo.get("endHeight").asText());
    }

    private JsonNode getJson(final String path)
        throws IOException, InterruptedException {
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(nodeUrl + path)).GET().build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        return JSON_MAPPER.readTree(response.body());
    }

}

Download source

コード解説⚓︎

コードは ルートネームスペースの登録 チュートリアルと同じパターンに従います。 このセクションでは、主な相違点のみに焦点を当てます。

共通のステップ(アカウントの設定、推奨手数料の取得、およびアナウンス)と、ルートネームスペースと共通のトランザクション記述子フィールドの詳細な説明については、 ルートネームスペースの登録 を参照してください。

サブネームスペース名の選択⚓︎

    # Build the subnamespace name
    root_namespace_name = os.getenv('ROOT_NAMESPACE', 'ns_root')
    subnamespace_name = os.getenv(
        'SUBNAMESPACE', f'sub_{int(time.time())}')
    full_namespace_name = (
        f'{root_namespace_name}.{subnamespace_name}')
    print(f'Creating subnamespace: {full_namespace_name}')

    # Generate the parent namespace ID from the root namespace name
    parent_id = generate_namespace_id(root_namespace_name)
    print(f'  Parent namespace ID: 0x{parent_id:016X}')
    // Build the subnamespace name
    const rootNamespaceName = process.env.ROOT_NAMESPACE || 'ns_root';
    const subnamespaceName =
        process.env.SUBNAMESPACE || `sub_${Date.now()}`;
    const fullNamespaceName =
        `${rootNamespaceName}.${subnamespaceName}`;
    console.log('Creating subnamespace:', fullNamespaceName);

    // Generate the parent namespace ID from the root name
    const parentId = generateNamespaceId(rootNamespaceName);
    const parentIdHex = parentId.toString(16)
        .toUpperCase().padStart(16, '0');
    console.log('Parent namespace ID:', `0x${parentIdHex}`);
        // Build the subnamespace name
        final String rootNamespaceName = System.getenv().getOrDefault(
            "ROOT_NAMESPACE", "ns_root");
        final String subnamespaceName = System.getenv().getOrDefault(
            "SUBNAMESPACE", "sub_" + System.currentTimeMillis());
        final String fullNamespaceName =
            rootNamespaceName + "." + subnamespaceName;
        System.out.printf("Creating subnamespace: %s%n",
            fullNamespaceName);

        // Generate the parent namespace ID from the root name
        final long parentId = IdGenerator.generateNamespaceId(
            rootNamespaceName);
        System.out.printf("Parent namespace ID: 0x%016X%n", parentId);

サブネームスペースは、親ネームスペース名と子の名前をドットで結合したフルネームによって識別されます(例: company.product )。 命名規則については、テキストブックの 名前 を参照してください。

チュートリアルを複数回実行しても名前が重複しないように、子の名前にタイムスタンプが付加されています。 ただし、実用的なプログラムでは、サブネームスペースに固定の名前を使用します。 ROOT_NAMESPACE および SUBNAMESPACE 環境変数を使って、チュートリアルに固定の名前を強制的に使用させることができます。

親ネームスペース ID は、親名から を使用して導出されます。 親ネームスペースは、名前ではなくこの ID によって参照されます。

署名者が所有する親ネームスペースを使用してください

デフォルトでは、コードは SIGNER_PRIVATE_KEY で参照されるテストアカウントと、 ns_root という名前の親ネームスペースを使用します。

ルートネームスペースの登録 チュートリアルから来た場合は、 SIGNER_PRIVATE_KEY および ROOT_NAMESPACE 環境変数を、そこで作成したアカウントとネームスペース、または署名者が所有する他の任意のネームスペースに一致するように設定してください。

トランザクションの構築⚓︎

    # Build the transaction
    transaction = facade.create_transaction_from_descriptor(
        {
            'type': 'namespace_registration_transaction_v1',
            'registration_type': 'child',
            'parent_id': parent_id,
            'name': subnamespace_name
        },
        signer_key_pair.public_key,
        fee_multiplier,
        2 * 60 * 60)
    // Build the transaction
    const transaction = facade.createTransactionFromTypedDescriptor(
        new descriptors.NamespaceRegistrationTransactionV1Descriptor(
            new models.NamespaceId(0n),
            models.NamespaceRegistrationType.CHILD,
            undefined,
            new models.NamespaceId(parentId),
            subnamespaceName),
        signerKeyPair.publicKey,
        feeMultiplier,
        2 * 60 * 60);
        // Build the transaction
        final Transaction transaction =
            facade.createTransactionFromTypedDescriptor(
                new NamespaceRegistrationTransactionV1Descriptor(
                    new NamespaceId(0),
                    NamespaceRegistrationType.CHILD,
                    null,
                    new NamespaceId(parentId),
                    subnamespaceName.getBytes(StandardCharsets.UTF_8)),
                signerKeyPair.getPublicKey(),
                feeMultiplier,
                2 * 60 * 60);

サブネームスペースを登録する際の主な違いは、トランザクション記述子にあります:

  • : child という値は、サブネームスペースが作成されることを示します。 代わりに ルートネームスペースを登録 するには root を使用してください。

  • : 有効期間を指定する代わりに、前のステップで導出した親ネームスペースの ID を提供します。 親は、ルートネームスペースまたは別のサブネームスペースのいずれかです。

  • : サブネームスペースの名前。前のステップで選択したものです。

    これはフルパスではなく、サブネームスペース自体の名前であることに注意してください。 例えば、 company がルートである場合に company.product を作成するには、 name: 'product' および と設定します。

サブネームスペースのレンタル手数料

標準のトランザクション手数料に加えて、サブネームスペースの登録には レンタル手数料 が必要です。

トランザクション手数料とは異なり、レンタル手数料はトランザクションリクエストには 含まれません。

サブネームスペースの場合、この手数料は期間に関係なく固定です。 登録トランザクションが承認されると、ネットワークによってレンタル手数料が自動的に差し引かれるため、トランザクション内で指定する必要はありません。

その後、トランザクションは ルートネームスペースの登録 と同じプロセスに従って署名、アナウンス、承認されます。

サブネームスペースの取得⚓︎

    # Retrieve the namespace
    namespace_id = generate_namespace_id(
        subnamespace_name, parent_id)
    print(f'Child namespace ID: {namespace_id} (0x{namespace_id:016X})')

    namespace_path = f'/namespaces/{namespace_id:016X}'
    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"  Parent ID: {namespace_info['parentId']}")
        print(f"  Depth: {namespace_info['depth']}")
        print(f"  Level 0: {namespace_info['level0']}")
        if int(namespace_info['depth']) >= 1:
            print(f"  Level 1: {namespace_info['level1']}")
        if int(namespace_info['depth']) >= 2:
            if 'level2' in namespace_info:
                print(f"  Level 2: {namespace_info['level2']}")
        print(f"  Start height: {namespace_info['startHeight']}")
        print(f"  End height: {namespace_info['endHeight']}")
    // Retrieve the namespace
    const namespaceId = generateNamespaceId(
        subnamespaceName, parentId);
    const namespaceIdHex = namespaceId.toString(16)
        .toUpperCase().padStart(16, '0');
    console.log(
        'Child namespace ID:',
        `${namespaceId} (0x${namespaceIdHex})`);

    const namespacePath = `/namespaces/${namespaceIdHex}`;
    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('  Parent ID:', namespaceInfo.parentId);
    console.log('  Depth:', namespaceInfo.depth);
    console.log('  Level 0:', namespaceInfo.level0);
    if (1 <= namespaceInfo.depth)
        console.log('  Level 1:', namespaceInfo.level1);
    if (2 <= namespaceInfo.depth && namespaceInfo.level2)
        console.log('  Level 2:', namespaceInfo.level2);
    console.log('  Start height:', namespaceInfo.startHeight);
    console.log('  End height:', namespaceInfo.endHeight);
        // Retrieve the namespace
        final long namespaceId = IdGenerator.generateNamespaceId(
            subnamespaceName, parentId);
        System.out.printf("Child namespace ID: %s (0x%016X)%n",
            Long.toUnsignedString(namespaceId), namespaceId);

        final String namespacePath = "/namespaces/%016X".formatted(
            namespaceId);
        System.out.printf("Fetching namespace information from %s%n",
            namespacePath);
        final JsonNode namespaceInfo = getJson(namespacePath)
            .get("namespace");
        System.out.println("Namespace information:");
        System.out.printf("  Registration type: %s%n",
            namespaceInfo.get("registrationType").asText());
        final Address ownerAddress = Address
            .fromDecodedAddressHexString(
                namespaceInfo.get("ownerAddress").asText());
        System.out.printf("  Owner address: %s%n", ownerAddress);
        System.out.printf("  Parent ID: %s%n",
            namespaceInfo.get("parentId").asText());
        System.out.printf("  Depth: %s%n",
            namespaceInfo.get("depth").asText());
        System.out.printf("  Level 0: %s%n",
            namespaceInfo.get("level0").asText());
        if (1 <= namespaceInfo.get("depth").asInt())
            System.out.printf("  Level 1: %s%n",
                namespaceInfo.get("level1").asText());
        if (2 <= namespaceInfo.get("depth").asInt()
            && namespaceInfo.has("level2"))
            System.out.printf("  Level 2: %s%n",
                namespaceInfo.get("level2").asText());
        System.out.printf("  Start height: %s%n",
            namespaceInfo.get("startHeight").asText());
        System.out.printf("  End height: %s%n",
            namespaceInfo.get("endHeight").asText());

サブネームスペースが登録されたことを確認するために、コードは /namespaces/{namespaceId} GET エンドポイントを使用してネットワークから情報を取得し、そのプロパティを表示します。

サブネームスペース ID は を使用して計算されます。 この関数はサブネームスペース名と親 ID の両方を受け取り、決定論的なハッシュアルゴリズムを適用してサブネームスペース ID を生成します。

レスポンスが成功すれば、サブネームスペースが登録され、ネットワーク上でアクティブであることが確認されます。

サブネームスペースは登録されましたが、まだリンクされていません

サブネームスペースは、 モザイク や アカウント のエイリアスとして機能するときに有用なものとなります。 次のステップ のガイドを使用して、サブネームスペースを識別子にリンクしてください。

出力⚓︎

以下に示す出力は、プログラムの典型的な実行結果に対応しています。

Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Fetching recommended fees from /network/fees/transaction
  Fee multiplier: 100
Creating subnamespace: ns_root.sub_1766533103
Parent namespace ID: 0xB0786316D5C1D9DD
Built transaction:
{
  "signature": "9E91F08855D1911BE7208BB3441ACF8AE18EBEB529867D6DAC8552A837E17AA2F05BEBAE510950D5194E0837C4A6E780138CCF29A065F55C05A76CEF6AFA610E",
  "signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
  "version": 1,
  "network": 152,
  "type": 16718,
  "fee": "16000",
  "deadline": "99289837399",
  "parent_id": "12716022497607277021",
  "id": "11305240440531381773",
  "registration_type": 1,
  "name": "7375625f31373636353333313033"
}
Transaction hash: 43962A040342198E485F5E91364CA0270F737C56E58909FE03FE3F02AD97CA56
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: confirmed
namespace registration confirmed in 19 seconds
Child namespace ID: 11305240440531381773 (0x9CE448486E40EE0D)
Fetching namespace information from /namespaces/9CE448486E40EE0D
Namespace information:
  Registration type: 1
  Owner address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
  Parent ID: B0786316D5C1D9DD
  Depth: 2
  Level 0: B0786316D5C1D9DD
  Level 1: 9CE448486E40EE0D
  Start height: 2956766
  End height: 3046046

出力の主なポイント:

  • ネームスペースのフルパス (5行目): サブネームスペース ns_root.sub_1766533103 は、完全な名前を示しています。

  • 親ネームスペース ID (6, 33行目): 親 ID 0xB0786316D5C1D9DD は、このサブネームスペースをそのルートにリンクします。

  • 手数料 (14行目): 0.016 XYM のトランザクション手数料は、トランザクションサイズに手数料倍率を乗じて計算されます。 レンタル手数料 は、トランザクションが承認された際にネットワークによって別途差し引かれます。

  • ID と名前 (17, 19行目): id フィールドにはサブネームスペース ID が10進数で表示され、 name には16進数としてエンコードされた子要素の部分のみが含まれます(例えば、 7375625f... は sub_1... にデコードされます)。

  • サブネームスペース ID (28行目): 17行目の id フィールドと一致するように、10進数と16進数の両方の表現が表示されます。

  • 登録タイプ (31行目): 値 1 はサブネームスペースであることを示します(ルートネームスペースの場合は 0 )。

  • 所有者アドレス (32行目): ネームスペースを登録したアカウントであり、ルートネームスペースの所有者と同じである必要があります。

  • 深さ (Depth) (34行目): 深さ 2 は、ネームスペース階層に2つのレベルがあることを示します。レベル 0 はルートネームスペース、レベル 1 はこのサブネームスペースです。

  • レベル (Levels) (35-36行目): 完全な階層パスです。 level0 にはルートネームスペース ID が、 level1 には子 ID が含まれます。深さが 3 であれば、 level2 に孫 ID が含まれます。

  • 開始高と終了高 (37-38行目): これらの値はルートネームスペースから継承され、個別に設定されることはありません。

出力されたトランザクション ハッシュ を使用して、 Symbol Testnet Explorer でトランザクションを検索することもできます。

結論⚓︎

このチュートリアルでは、以下の方法を説明しました:

ステップ 関連ドキュメント
ネームスペース ID を生成する
サブネームスペース登録トランザクションを構築する , NamespaceRegistrationTransactionV1
サブネームスペースを取得する /namespaces/{namespaceId} GET

次のステップ⚓︎

サブネームスペースを作成したので、以下のことができます: