コンテンツにスキップ

ネームスペース情報を取得する⚓︎

初級

このチュートリアルでは、ネームスペース のプロパティ、その サブネームスペース、およびその下に定義された モザイク を取得する方法を説明します。

前提条件⚓︎

このチュートリアルではネットワークからデータを読み取るだけです。アカウントは必要ありません。

始める前に、開発環境をセットアップ してください。

完全なコード⚓︎

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

import json
import os
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'http://libertalia.nemtest.net:7890')
print(f'Using node {NODE_URL}')

NAMESPACE_NAME = os.getenv('NAMESPACE_NAME', 'company')
print(f'Namespace name: {NAMESPACE_NAME}')

try:
    # Fetch namespace information
    namespace_path = f'/namespace?namespace={NAMESPACE_NAME}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(f'{NODE_URL}{namespace_path}') as response:
        namespace_info = json.loads(response.read().decode())
        print('Namespace information:')
        print(f'  Name: {namespace_info["fqn"]}')
        print(f'  Owner: {namespace_info["owner"]}')
        lease_height = namespace_info['height']
        print(f'  Height: {lease_height}')

    # Compute the lease expiration
    LEASE_DURATION = 525600  # approximately one year of blocks
    with urllib.request.urlopen(f'{NODE_URL}/chain/height') as response:
        current_height = json.loads(response.read().decode())['height']
    expiration_height = lease_height + LEASE_DURATION
    print(f'\nCurrent chain height: {current_height}')
    print(f'Lease expiration height: {expiration_height}')
    print(f'Blocks until expiration: {expiration_height - current_height}')

    # List the subnamespaces
    owner = namespace_info['owner']
    subnamespaces_path = (
        f'/account/namespace/page'
        f'?address={owner}&parent={NAMESPACE_NAME}')
    print(f'\nFetching subnamespaces from {subnamespaces_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{subnamespaces_path}'
    ) as response:
        subnamespaces = json.loads(response.read().decode())['data']
        print(f'Subnamespaces of {NAMESPACE_NAME}: {len(subnamespaces)}')
        for subnamespace in subnamespaces:
            print(f'  {subnamespace["fqn"]}')

    # List the mosaics defined under the namespace
    mosaics_path = (
        f'/namespace/mosaic/definition/page?namespace={NAMESPACE_NAME}')
    print(f'\nFetching mosaic definitions from {mosaics_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaics_path}') as response:
        mosaics = json.loads(response.read().decode())['data']
        print(f'Mosaics defined under {NAMESPACE_NAME}: {len(mosaics)}')
        for entry in mosaics:
            mosaic_id = entry['mosaic']['id']
            print(f'  {mosaic_id["namespaceId"]}:{mosaic_id["name"]}')

except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://libertalia.nemtest.net:7890';
console.log('Using node', NODE_URL);

const NAMESPACE_NAME = process.env.NAMESPACE_NAME || 'company';
console.log('Namespace name:', NAMESPACE_NAME);

try {
    // Fetch namespace information
    const namespacePath = `/namespace?namespace=${NAMESPACE_NAME}`;
    console.log('Fetching namespace information from', namespacePath);
    const namespaceResponse = await fetch(`${NODE_URL}${namespacePath}`);
    if (!namespaceResponse.ok)
        throw new Error(`HTTP error! status: ${namespaceResponse.status}`);

    const namespaceInfo = await namespaceResponse.json();
    console.log('Namespace information:');
    console.log('  Name:', namespaceInfo.fqn);
    console.log('  Owner:', namespaceInfo.owner);
    const leaseHeight = namespaceInfo.height;
    console.log('  Height:', leaseHeight);

    // Compute the lease expiration
    const LEASE_DURATION = 525600; // approximately one year of blocks
    const chainResponse = await fetch(`${NODE_URL}/chain/height`);
    const currentHeight = (await chainResponse.json()).height;
    const expirationHeight = leaseHeight + LEASE_DURATION;
    console.log('\nCurrent chain height:', currentHeight);
    console.log('Lease expiration height:', expirationHeight);
    console.log('Blocks until expiration:',
        expirationHeight - currentHeight);

    // List the subnamespaces
    const owner = namespaceInfo.owner;
    const subnamespacesPath = '/account/namespace/page' +
        `?address=${owner}&parent=${NAMESPACE_NAME}`;
    console.log('\nFetching subnamespaces from', subnamespacesPath);
    const subnamespacesResponse =
        await fetch(`${NODE_URL}${subnamespacesPath}`);
    const subnamespaces = (await subnamespacesResponse.json()).data;
    console.log(`Subnamespaces of ${NAMESPACE_NAME}:`,
        subnamespaces.length);
    for (const subnamespace of subnamespaces)
        console.log(`  ${subnamespace.fqn}`);

    // List the mosaics defined under the namespace
    const mosaicsPath =
        `/namespace/mosaic/definition/page?namespace=${NAMESPACE_NAME}`;
    console.log('\nFetching mosaic definitions from', mosaicsPath);
    const mosaicsResponse = await fetch(`${NODE_URL}${mosaicsPath}`);
    const mosaics = (await mosaicsResponse.json()).data;
    console.log(`Mosaics defined under ${NAMESPACE_NAME}:`,
        mosaics.length);
    for (const entry of mosaics) {
        const mosaicId = entry.mosaic.id;
        console.log(`  ${mosaicId.namespaceId}:${mosaicId.name}`);
    }

} catch (e) {
    console.error(e.message);
}

Download source

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

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 com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

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

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

    private static final String NODE_URL = System.getenv().getOrDefault(
        "NODE_URL", "http://libertalia.nemtest.net:7890");

    public static void main(final String[] args) {
        try {
            new GetNamespaceInfo().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", NODE_URL);

        final String namespaceName = System.getenv().getOrDefault(
            "NAMESPACE_NAME", "company");
        System.out.printf("Namespace name: %s%n", namespaceName);

        // Fetch namespace information
        final String namespacePath = String.format(
            "/namespace?namespace=%s", namespaceName);
        System.out.printf("Fetching namespace information from %s%n",
            namespacePath);
        final HttpRequest namespaceRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + namespacePath)).GET().build();
        final HttpResponse<String> namespaceResponse = HTTP_CLIENT.send(
            namespaceRequest, BodyHandlers.ofString());
        if (2 != namespaceResponse.statusCode() / 100)
            throw new IOException("HTTP error! status: "
                + namespaceResponse.statusCode());
        final JsonNode namespaceInfo = JSON_MAPPER.readTree(
            namespaceResponse.body());
        System.out.println("Namespace information:");
        System.out.printf("  Name: %s%n",
            namespaceInfo.get("fqn").asText());
        System.out.printf("  Owner: %s%n",
            namespaceInfo.get("owner").asText());
        final long leaseHeight = namespaceInfo.get("height").asLong();
        System.out.printf("  Height: %d%n", leaseHeight);

        // Compute the lease expiration
        final long leaseDuration = 525_600;
        final HttpRequest heightRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + "/chain/height")).GET().build();
        final HttpResponse<String> heightResponse = HTTP_CLIENT.send(
            heightRequest, BodyHandlers.ofString());
        final long currentHeight = JSON_MAPPER.readTree(
            heightResponse.body()).get("height").asLong();
        final long expirationHeight = leaseHeight + leaseDuration;
        System.out.printf("%nCurrent chain height: %d%n", currentHeight);
        System.out.printf("Lease expiration height: %d%n",
            expirationHeight);
        System.out.printf("Blocks until expiration: %d%n",
            expirationHeight - currentHeight);

        // List the subnamespaces
        final String owner = namespaceInfo.get("owner").asText();
        final String subnamespacesPath = String.format(
            "/account/namespace/page?address=%s&parent=%s",
            owner, namespaceName);
        System.out.printf("%nFetching subnamespaces from %s%n",
            subnamespacesPath);
        final HttpRequest subnamespacesRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + subnamespacesPath)).GET().build();
        final HttpResponse<String> subnamespacesResponse =
            HTTP_CLIENT.send(
                subnamespacesRequest, BodyHandlers.ofString());
        final JsonNode subnamespaces = JSON_MAPPER.readTree(
            subnamespacesResponse.body()).get("data");
        System.out.printf("Subnamespaces of %s: %d%n",
            namespaceName, subnamespaces.size());
        for (final JsonNode subnamespace : subnamespaces)
            System.out.printf("  %s%n",
                subnamespace.get("fqn").asText());

        // List the mosaics defined under the namespace
        final String mosaicsPath = String.format(
            "/namespace/mosaic/definition/page?namespace=%s",
            namespaceName);
        System.out.printf("%nFetching mosaic definitions from %s%n",
            mosaicsPath);
        final HttpRequest mosaicsRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + mosaicsPath)).GET().build();
        final HttpResponse<String> mosaicsResponse = HTTP_CLIENT.send(
            mosaicsRequest, BodyHandlers.ofString());
        final JsonNode mosaics = JSON_MAPPER.readTree(
            mosaicsResponse.body()).get("data");
        System.out.printf("Mosaics defined under %s: %d%n",
            namespaceName, mosaics.size());
        for (final JsonNode entry : mosaics) {
            final JsonNode id = entry.get("mosaic").get("id");
            System.out.printf("  %s:%s%n",
                id.get("namespaceId").asText(),
                id.get("name").asText());
        }

    }
}

Download source

スニペットでは、NODE_URL 環境変数を使って NEM API ノードを指定します。 値が指定されていない場合は、デフォルトの テストネット ノードを使用します。

NAMESPACE_NAME 環境変数では、foo や foo.bar のような、ドットで区切られた完全な 名前 で照会するネームスペースを指定します。 設定されていない場合は、テストネットに登録された ルートネームスペース である company を使用します。

コードの説明⚓︎

ネームスペース情報を取得する⚓︎

    # Fetch namespace information
    namespace_path = f'/namespace?namespace={NAMESPACE_NAME}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(f'{NODE_URL}{namespace_path}') as response:
        namespace_info = json.loads(response.read().decode())
        print('Namespace information:')
        print(f'  Name: {namespace_info["fqn"]}')
        print(f'  Owner: {namespace_info["owner"]}')
        lease_height = namespace_info['height']
        print(f'  Height: {lease_height}')
    // Fetch namespace information
    const namespacePath = `/namespace?namespace=${NAMESPACE_NAME}`;
    console.log('Fetching namespace information from', namespacePath);
    const namespaceResponse = await fetch(`${NODE_URL}${namespacePath}`);
    if (!namespaceResponse.ok)
        throw new Error(`HTTP error! status: ${namespaceResponse.status}`);

    const namespaceInfo = await namespaceResponse.json();
    console.log('Namespace information:');
    console.log('  Name:', namespaceInfo.fqn);
    console.log('  Owner:', namespaceInfo.owner);
    const leaseHeight = namespaceInfo.height;
    console.log('  Height:', leaseHeight);
        // Fetch namespace information
        final String namespacePath = String.format(
            "/namespace?namespace=%s", namespaceName);
        System.out.printf("Fetching namespace information from %s%n",
            namespacePath);
        final HttpRequest namespaceRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + namespacePath)).GET().build();
        final HttpResponse<String> namespaceResponse = HTTP_CLIENT.send(
            namespaceRequest, BodyHandlers.ofString());
        if (2 != namespaceResponse.statusCode() / 100)
            throw new IOException("HTTP error! status: "
                + namespaceResponse.statusCode());
        final JsonNode namespaceInfo = JSON_MAPPER.readTree(
            namespaceResponse.body());
        System.out.println("Namespace information:");
        System.out.printf("  Name: %s%n",
            namespaceInfo.get("fqn").asText());
        System.out.printf("  Owner: %s%n",
            namespaceInfo.get("owner").asText());
        final long leaseHeight = namespaceInfo.get("height").asLong();
        System.out.printf("  Height: %d%n", leaseHeight);

/namespace GET エンドポイントは、次の内容を含むネームスペースの現在のプロパティを取得します。

リースの有効期限を計算する⚓︎

    # Compute the lease expiration
    LEASE_DURATION = 525600  # approximately one year of blocks
    with urllib.request.urlopen(f'{NODE_URL}/chain/height') as response:
        current_height = json.loads(response.read().decode())['height']
    expiration_height = lease_height + LEASE_DURATION
    print(f'\nCurrent chain height: {current_height}')
    print(f'Lease expiration height: {expiration_height}')
    print(f'Blocks until expiration: {expiration_height - current_height}')
    // Compute the lease expiration
    const LEASE_DURATION = 525600; // approximately one year of blocks
    const chainResponse = await fetch(`${NODE_URL}/chain/height`);
    const currentHeight = (await chainResponse.json()).height;
    const expirationHeight = leaseHeight + LEASE_DURATION;
    console.log('\nCurrent chain height:', currentHeight);
    console.log('Lease expiration height:', expirationHeight);
    console.log('Blocks until expiration:',
        expirationHeight - currentHeight);
        // Compute the lease expiration
        final long leaseDuration = 525_600;
        final HttpRequest heightRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + "/chain/height")).GET().build();
        final HttpResponse<String> heightResponse = HTTP_CLIENT.send(
            heightRequest, BodyHandlers.ofString());
        final long currentHeight = JSON_MAPPER.readTree(
            heightResponse.body()).get("height").asLong();
        final long expirationHeight = leaseHeight + leaseDuration;
        System.out.printf("%nCurrent chain height: %d%n", currentHeight);
        System.out.printf("Lease expiration height: %d%n",
            expirationHeight);
        System.out.printf("Blocks until expiration: %d%n",
            expirationHeight - currentHeight);

ネームスペースが永久に所有されることはありません。 ルートネームスペースは 525600 ブロック(およそ 1 年)リース を受け、期限切れになる前に更新する必要があります。 サブネームスペースは個別にリースされず、ルートネームスペースと同時に期限切れになります。

有効期限の高さは API レスポンスに含まれませんが、ネームスペースの高さにリース期間を加えて導出できます。 これを /chain/height GET が返す現在のチェーン高と比較すると、ネームスペースの期限切れまでに残っているブロック数がわかります。

サブネームスペースを一覧表示する⚓︎

    # List the subnamespaces
    owner = namespace_info['owner']
    subnamespaces_path = (
        f'/account/namespace/page'
        f'?address={owner}&parent={NAMESPACE_NAME}')
    print(f'\nFetching subnamespaces from {subnamespaces_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{subnamespaces_path}'
    ) as response:
        subnamespaces = json.loads(response.read().decode())['data']
        print(f'Subnamespaces of {NAMESPACE_NAME}: {len(subnamespaces)}')
        for subnamespace in subnamespaces:
            print(f'  {subnamespace["fqn"]}')
    // List the subnamespaces
    const owner = namespaceInfo.owner;
    const subnamespacesPath = '/account/namespace/page' +
        `?address=${owner}&parent=${NAMESPACE_NAME}`;
    console.log('\nFetching subnamespaces from', subnamespacesPath);
    const subnamespacesResponse =
        await fetch(`${NODE_URL}${subnamespacesPath}`);
    const subnamespaces = (await subnamespacesResponse.json()).data;
    console.log(`Subnamespaces of ${NAMESPACE_NAME}:`,
        subnamespaces.length);
    for (const subnamespace of subnamespaces)
        console.log(`  ${subnamespace.fqn}`);
        // List the subnamespaces
        final String owner = namespaceInfo.get("owner").asText();
        final String subnamespacesPath = String.format(
            "/account/namespace/page?address=%s&parent=%s",
            owner, namespaceName);
        System.out.printf("%nFetching subnamespaces from %s%n",
            subnamespacesPath);
        final HttpRequest subnamespacesRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + subnamespacesPath)).GET().build();
        final HttpResponse<String> subnamespacesResponse =
            HTTP_CLIENT.send(
                subnamespacesRequest, BodyHandlers.ofString());
        final JsonNode subnamespaces = JSON_MAPPER.readTree(
            subnamespacesResponse.body()).get("data");
        System.out.printf("Subnamespaces of %s: %d%n",
            namespaceName, subnamespaces.size());
        for (final JsonNode subnamespace : subnamespaces)
            System.out.printf("  %s%n",
                subnamespace.get("fqn").asText());

ネームスペースの子を直接返すエンドポイントはありません。 ただし、サブネームスペースは常にルートネームスペースの所有者を共有する ため、そのアカウントが所有するネームスペースを照会すれば見つけられます。

/account/namespace/page GET エンドポイントは、アカウントが所有するネームスペースを返します。 オプションの parent パラメーターを使うと、指定したネームスペースのサブネームスペースだけに結果を制限できます。 前の手順で取得したネームスペース所有者と照会したネームスペースを parent の値として使うと、そのサブネームスペースが返されます。

ネームスペースのモザイクを一覧表示する⚓︎

    # List the mosaics defined under the namespace
    mosaics_path = (
        f'/namespace/mosaic/definition/page?namespace={NAMESPACE_NAME}')
    print(f'\nFetching mosaic definitions from {mosaics_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaics_path}') as response:
        mosaics = json.loads(response.read().decode())['data']
        print(f'Mosaics defined under {NAMESPACE_NAME}: {len(mosaics)}')
        for entry in mosaics:
            mosaic_id = entry['mosaic']['id']
            print(f'  {mosaic_id["namespaceId"]}:{mosaic_id["name"]}')
    // List the mosaics defined under the namespace
    const mosaicsPath =
        `/namespace/mosaic/definition/page?namespace=${NAMESPACE_NAME}`;
    console.log('\nFetching mosaic definitions from', mosaicsPath);
    const mosaicsResponse = await fetch(`${NODE_URL}${mosaicsPath}`);
    const mosaics = (await mosaicsResponse.json()).data;
    console.log(`Mosaics defined under ${NAMESPACE_NAME}:`,
        mosaics.length);
    for (const entry of mosaics) {
        const mosaicId = entry.mosaic.id;
        console.log(`  ${mosaicId.namespaceId}:${mosaicId.name}`);
    }
        // List the mosaics defined under the namespace
        final String mosaicsPath = String.format(
            "/namespace/mosaic/definition/page?namespace=%s",
            namespaceName);
        System.out.printf("%nFetching mosaic definitions from %s%n",
            mosaicsPath);
        final HttpRequest mosaicsRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + mosaicsPath)).GET().build();
        final HttpResponse<String> mosaicsResponse = HTTP_CLIENT.send(
            mosaicsRequest, BodyHandlers.ofString());
        final JsonNode mosaics = JSON_MAPPER.readTree(
            mosaicsResponse.body()).get("data");
        System.out.printf("Mosaics defined under %s: %d%n",
            namespaceName, mosaics.size());
        for (final JsonNode entry : mosaics) {
            final JsonNode id = entry.get("mosaic").get("id");
            System.out.printf("  %s:%s%n",
                id.get("namespaceId").asText(),
                id.get("name").asText());
        }

モザイクは常に ネームスペースの下に定義 され、関連するモザイクをまとめるプレフィックスとして機能します。

/namespace/mosaic/definition/page GET エンドポイントは、照会した名前とネームスペースが完全に一致するモザイクごとに 1 つの定義を返します。 より深いサブネームスペース(foo を照会したときの foo.bar:baz など)で定義されたモザイクは含まれません。 それらも一覧に含めるには、前の手順で見つかった各サブネームスペースについて、この照会を繰り返してください。

出力⚓︎

以下の出力は、テストネットの company ネームスペースを照会した場合の実行例です。

Using node http://libertalia.nemtest.net:7890
Namespace name: company
Fetching namespace information from /namespace?namespace=company
Namespace information:
  Name: company
  Owner: TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
  Height: 625711

Current chain height: 655420
Lease expiration height: 1151311
Blocks until expiration: 495891

Fetching subnamespaces from /account/namespace/page?address=TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP&parent=company
Subnamespaces of company: 1
  company.division

Fetching mosaic definitions from /namespace/mosaic/definition/page?namespace=company
Mosaics defined under company: 1
  company:token

出力の要点は次のとおりです。

  • ネームスペース名(5 行目): 照会したネームスペース company。 ドットを含まないため、ルートネームスペースです。

  • 所有者(6 行目): 現在ネームスペースを所有するアカウント。

  • 高さ(7 行目): 現在の所有権期間が開始したブロックの高さ。

  • リースの有効期限(9~11 行目): 有効期限の高さは、所有権の高さにリース期間 525600 ブロックを加えた値です。 現在のチェーン高を引くと、有効期限までに残っているブロック数がわかります。

  • サブネームスペース(14~15 行目): company の下に company.division というサブネームスペースが 1 つ存在します。

  • モザイク(18~19 行目): ネームスペースの直下に company:token というモザイクが 1 つ定義されています。

まとめ⚓︎

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

手順 関連ドキュメント
ネームスペースのプロパティを取得する /namespace GET
リースの有効期限を計算する /chain/height GET
サブネームスペースを一覧表示する /account/namespace/page GET
ネームスペースのモザイクを一覧表示する /namespace/mosaic/definition/page GET