コンテンツにスキップ
上級

アカウントへの制限の追加⚓︎

アカウントは、以下の項目に対して制限を課すことができます。

  • インタラクション可能な他の アカウント
  • 取引可能な モザイク
  • 実行可能な操作(トランザクションタイプ)の種類

これらの制限はアカウント制限を使用して設定されます。

このチュートリアルでは、アカウントの 送信トランザクション を制限し、許可された単一のアドレスにのみトランザクションを送信できるようにする方法を実演します。

もし制限がすでに有効である場合は、代わりにその制限を解除する方法を実演します。

制限の有効化または無効化を行った後、未承認のアドレスに対してテスト用の転送トランザクションをアナウンスし、ネットワークがそれをどのように拒否するかを確認します。

モザイク制限との違い

Symbolは、このチュートリアルで説明するアカウントレベルの制限とは別に、モザイクレベルで定義される モザイク制限 もサポートしています。

これらは異なる仕組みです。異なるトランザクションタイプを使用して設定され、異なるルールに基づいて動作します。

アカウント制限はアカウントがインタラクションできるモザイクを制限でき、モザイク制限はモザイクとインタラクションできるアカウントを制限できるため、概念的な重複が混乱の元となることがよくあります。

前提条件⚓︎

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

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

完全なコード⚓︎

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

import json
import os
import time
import urllib.request

from symbolchain.CryptoTypes import PrivateKey
from symbolchain.facade.SymbolFacade import SymbolFacade, Address
from symbolchain.sc import Amount, AccountRestrictionFlags
from symbolchain.symbol.Network import NetworkTimestamp

NODE_URL = os.environ.get(
    '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 response:
        print(f'  Response: {response.read().decode()}')

# Helper function to wait for transaction confirmation
def wait_for_confirmation(transaction_hash, label):
    print(f'Waiting for {label} confirmation...')
    for attempt in range(60):
        time.sleep(1)
        try:
            url = f'{NODE_URL}/transactionStatus/{transaction_hash}'
            with urllib.request.urlopen(url) as response:
                status = json.loads(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 Exception(f'{label} failed: {status["code"]}')
        except urllib.error.HTTPError:
            print('  Transaction status: unknown')
    raise Exception(f'{label} not confirmed after 60 seconds')

# Returns the list of restrictions currently applied to the account
def get_account_restrictions(address):
    restrictions_path = f'/restrictions/account/{address}'
    print(f'Getting restrictions from {restrictions_path}')
    try:
        url = f'{NODE_URL}{restrictions_path}'
        with urllib.request.urlopen(url) as response:
            status = json.loads(response.read().decode())
            restrictions = status['accountRestrictions']['restrictions']
            print(f'  Response: {restrictions}')
            return restrictions
    except urllib.error.HTTPError:
        # The address has never been used
        print('  Response: No restrictions found')
    return []

# Returns a transaction that restricts an account
def restriction_enable_transaction():
    transaction = facade.transaction_factory.create({
        'type': 'account_address_restriction_transaction_v1',
        # This is the account that will be restricted
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        # Allow only OUTGOING transactions to the authorized ADDRESS
        'restriction_flags':
            AccountRestrictionFlags.ADDRESS |
            AccountRestrictionFlags.OUTGOING,
        # This is the only authorized outgoing address
        'restriction_additions': [auth_address]
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    print('Enabling the restriction with transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    return transaction

# Returns a transaction that removes a restriction from an account
def restriction_disable_transaction(restriction):
    transaction = facade.transaction_factory.create({
        'type': 'account_address_restriction_transaction_v1',
        # This is the account whose restriction will be lifted
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        # Reverse flags
        'restriction_flags': restriction['restrictionFlags'],
        # Remove all addresses currently restricted
        'restriction_deletions': [
            Address.from_decoded_address_hex_string(addr) for addr in
                restriction['values']
        ]
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    print('Disabling the restriction with transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    return transaction

facade = SymbolFacade('testnet')

SIGNER_PRIVATE_KEY = os.getenv('SIGNER_PRIVATE_KEY',
    '0000000000000000000000000000000000000000000000000000000000000000')
signer_key_pair = SymbolFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))
signer_address = facade.network.public_key_to_address(
    signer_key_pair.public_key)
print(f'Signer address: {signer_address}')

auth_address = 'TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA'
print(f'Authorized address: {auth_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}')

    # Get current state of the restriction and decide which
    # operation to perform
    restrictions = get_account_restrictions(signer_address)
    if len(restrictions) == 0:
        # Enable the restriction
        print('\n--- Enabling restriction ---')
        transaction = restriction_enable_transaction()
    else:
        # Disable the restriction
        print('\n--- Disabling restriction ---')
        transaction = restriction_disable_transaction(restrictions[0])

    # Sign, announce and wait for confirmation
    json_payload = facade.transaction_factory.attach_signature(
        transaction,
        facade.sign_transaction(signer_key_pair, transaction))
    transaction_hash = facade.hash_transaction(transaction)
    announce_transaction(json_payload, 'restriction transaction')
    wait_for_confirmation(transaction_hash, 'restriction transaction')

    # Try a dummy transfer to a random address with no mosaics
    transaction = facade.transaction_factory.create({
        'type': 'transfer_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'recipient_address': 'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    json_payload = facade.transaction_factory.attach_signature(
        transaction,
        facade.sign_transaction(signer_key_pair, transaction))
    transaction_hash = facade.hash_transaction(transaction)
    print('\n--- Attempting transfer to unauthorized address ---')
    announce_transaction(json_payload, 'test transfer')
    wait_for_confirmation(transaction_hash, 'test transfer')

except Exception as e:
    print(e)

Download source

import { PrivateKey } from 'symbol-sdk';
import {
    KeyPair,
    SymbolTransactionFactory,
    models,
    Address,
    NetworkTimestamp,
    SymbolFacade
} 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; attempt < 60; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        try {
            const response = await fetch(
                `${NODE_URL}/transactionStatus/${transactionHash}`);
            const status = await response.json();
            console.log('  Transaction status:', status.group);
            if (status.group === 'confirmed') {
                console.log(`${label} confirmed in`, attempt, 'seconds');
                return;
            }
            if (status.group === 'failed') {
                throw new Error(`${label} failed: ${status.code}`);
            }
        } catch (e) {
            if (e.message.includes('failed'))
                throw e;
            console.log('  Transaction status: unknown');
        }
    }
    throw new Error(`${label} not confirmed after 60 seconds`);
}

// Returns the list of restrictions currently applied to the account
async function getAccountRestrictions(address) {
    const restrictionsPath = `/restrictions/account/${address}`;
    console.log(`Getting restrictions from ${restrictionsPath}`);
    try {
        const response = await fetch(`${NODE_URL}${restrictionsPath}`);
        const json = await response.json();
        const restrictions = json.accountRestrictions.restrictions;
        console.log('  Response:', restrictions);
        return restrictions;
    } catch {
        console.log('  Response: No restrictions found');
        return [];
    }
}

// Returns a transaction that restricts an account
function restrictionEnableTransaction(timestamp, feeMult) {
    const transaction = facade.transactionFactory.create({
        type: 'account_address_restriction_transaction_v1',
        // This is the account that will be restricted
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        // Allow only OUTGOING transactions to the authorized ADDRESS
        restrictionFlags:
            models.AccountRestrictionFlags.ADDRESS.value |
            models.AccountRestrictionFlags.OUTGOING.value,
        // This is the only authorized outgoing address
        restrictionAdditions: [authAddress]
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    console.log('Enabling the restriction with transaction:');
    console.dir(transaction.toJson(), { colors: true, depth: null });

    return transaction;
}

// Returns a transaction that removes a restriction from an account
function restrictionDisableTransaction(timestamp, feeMult, restriction) {
    const transaction = facade.transactionFactory.create({
        type: 'account_address_restriction_transaction_v1',
        // This is the account whose restriction will be lifted
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        // Reverse flags
        restrictionFlags: restriction.restrictionFlags,
        // Remove all addresses currently restricted
        restrictionDeletions: restriction.values.map(hex =>
            Address.fromDecodedAddressHexString(hex))
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    console.log('Disabling the restriction with transaction:');
    console.dir(transaction.toJson(), { colors: true, depth: null });

    return transaction;
}

const facade = new SymbolFacade('testnet');

const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY ||
    '0000000000000000000000000000000000000000000000000000000000000000';
const signerKeyPair = new KeyPair(new PrivateKey(SIGNER_PRIVATE_KEY));
const signerAddress = facade.network.publicKeyToAddress(
    signerKeyPair.publicKey);
console.log(`Signer address: ${signerAddress}`);

const authAddress = 'TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA';
console.log(`Authorized address: ${authAddress}`);

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 timestamp = new NetworkTimestamp(
        timeJSON.communicationTimestamps.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);

    // Get current state of the restriction and decide which
    // operation to perform
    const restrictions = await getAccountRestrictions(signerAddress);
    let transaction;
    if (restrictions.length === 0) {
        // Enable the restriction
        console.log('\n--- Enabling restriction ---');
        transaction = restrictionEnableTransaction(timestamp, feeMult);
    } else {
        // Disable the restriction
        console.log('\n--- Disabling restriction ---');
        transaction = restrictionDisableTransaction(timestamp, feeMult,
            restrictions[0]);
    }

    // Sign, announce and wait for confirmation
    let payload = SymbolTransactionFactory.attachSignature(
        transaction,
        facade.signTransaction(signerKeyPair, transaction));
    let hash =
        facade.hashTransaction(transaction).toString();
    await announceTransaction(payload, 'restriction transaction');
    await waitForConfirmation(hash, 'restriction transaction');

    // Try a dummy transfer to a random address with no mosaics
    transaction = facade.transactionFactory.create({
        type: 'transfer_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        recipientAddress: 'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    payload = SymbolTransactionFactory.attachSignature(
        transaction,
        facade.signTransaction(signerKeyPair, transaction));
    hash = facade.hashTransaction(transaction).toString();
    console.log('\n--- Attempting transfer to unauthorized address ---');
    await announceTransaction(payload, 'test transfer');
    await waitForConfirmation(hash, 'test transfer');

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

Download source

コード解説⚓︎

コードは、2つのヘルパー関数の定義から始まります。 トランザクションのアナウンス方法や承認の追跡方法の詳細については、転送トランザクション のチュートリアルを参照してください。その他のヘルパー関数については、以下のセクションで説明します。

その後、チュートリアルは以下の手順で進みます。

アカウントがすでに制限されているかどうかに応じて、以下のいずれかのトランザクションが作成されます。

その後、トランザクションは アナウンスおよび承認 され、最後に テスト転送 が送信されます。

アカウントの設定⚓︎

SIGNER_PRIVATE_KEY = os.getenv('SIGNER_PRIVATE_KEY',
    '0000000000000000000000000000000000000000000000000000000000000000')
signer_key_pair = SymbolFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))
signer_address = facade.network.public_key_to_address(
    signer_key_pair.public_key)
print(f'Signer address: {signer_address}')

auth_address = 'TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA'
print(f'Authorized address: {auth_address}')
const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY ||
    '0000000000000000000000000000000000000000000000000000000000000000';
const signerKeyPair = new KeyPair(new PrivateKey(SIGNER_PRIVATE_KEY));
const signerAddress = facade.network.publicKeyToAddress(
    signerKeyPair.publicKey);
console.log(`Signer address: ${signerAddress}`);

const authAddress = 'TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA';
console.log(`Authorized address: ${authAddress}`);

アカウントは自分自身に対してのみ制限を設定できるため、このチュートリアルでは単一の 秘密鍵 が必要です。 秘密鍵は SIGNER_PRIVATE_KEY 環境変数(64文字の16進数文字列)を通じて提供できます。提供されない場合は、デフォルト値が使用されます。

アカウントはトランザクションをアナウンスするのに十分な資金を保有している必要があります。デフォルトの鍵を使用する場合、対応するアカウントにはすでに資金が供給されている可能性があります。

この段階で、許可されたアドレスも設定されます。制限によって、後に送信トランザクションはこのアドレスのみに限定されます。

ネットワーク時間と手数料の取得⚓︎

    # 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}')
    // 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 timestamp = new NetworkTimestamp(
        timeJSON.communicationTimestamps.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);

転送トランザクション チュートリアルで説明されているプロセスに従い、ネットワーク時間と推奨手数料をそれぞれ /node/time GET および /network/fees/transaction GET から取得します。

制限状態の検出⚓︎

以下の関数は、/restrictions/account/{address} GET エンドポイントを使用して、指定されたアドレスに適用されている現在のアカウント制限を取得します。制限が設定されていない場合、関数は空のリストを返します。

def get_account_restrictions(address):
    restrictions_path = f'/restrictions/account/{address}'
    print(f'Getting restrictions from {restrictions_path}')
    try:
        url = f'{NODE_URL}{restrictions_path}'
        with urllib.request.urlopen(url) as response:
            status = json.loads(response.read().decode())
            restrictions = status['accountRestrictions']['restrictions']
            print(f'  Response: {restrictions}')
            return restrictions
    except urllib.error.HTTPError:
        # The address has never been used
        print('  Response: No restrictions found')
    return []
async function getAccountRestrictions(address) {
    const restrictionsPath = `/restrictions/account/${address}`;
    console.log(`Getting restrictions from ${restrictionsPath}`);
    try {
        const response = await fetch(`${NODE_URL}${restrictionsPath}`);
        const json = await response.json();
        const restrictions = json.accountRestrictions.restrictions;
        console.log('  Response:', restrictions);
        return restrictions;
    } catch {
        console.log('  Response: No restrictions found');
        return [];
    }
}

返されたリストを評価して、チュートリアルの実行パスを決定します。その内容に基づいて、制限を有効化するか解除するか、適切な設定トランザクションが構築されます。

    restrictions = get_account_restrictions(signer_address)
    if len(restrictions) == 0:
        # Enable the restriction
        print('\n--- Enabling restriction ---')
        transaction = restriction_enable_transaction()
    else:
        # Disable the restriction
        print('\n--- Disabling restriction ---')
        transaction = restriction_disable_transaction(restrictions[0])
    const restrictions = await getAccountRestrictions(signerAddress);
    let transaction;
    if (restrictions.length === 0) {
        // Enable the restriction
        console.log('\n--- Enabling restriction ---');
        transaction = restrictionEnableTransaction(timestamp, feeMult);
    } else {
        // Disable the restriction
        console.log('\n--- Disabling restriction ---');
        transaction = restrictionDisableTransaction(timestamp, feeMult,
            restrictions[0]);
    }

アカウントに複数の制限が設定されている場合、エンドポイントから返された最初の制限のみが削除されます。このチュートリアルの範囲内では、そのような状況は発生しないはずです。

制限の有効化⚓︎

アカウントがインタラクションできるアドレスのリストを制限するには、AccountAddressRestrictionTransactionV1 を使用します。

このチュートリアルでは扱いませんが、他の2つのアカウント制限タイプは以下の通りです。

def restriction_enable_transaction():
    transaction = facade.transaction_factory.create({
        'type': 'account_address_restriction_transaction_v1',
        # This is the account that will be restricted
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        # Allow only OUTGOING transactions to the authorized ADDRESS
        'restriction_flags':
            AccountRestrictionFlags.ADDRESS |
            AccountRestrictionFlags.OUTGOING,
        # This is the only authorized outgoing address
        'restriction_additions': [auth_address]
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    print('Enabling the restriction with transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    return transaction
function restrictionEnableTransaction(timestamp, feeMult) {
    const transaction = facade.transactionFactory.create({
        type: 'account_address_restriction_transaction_v1',
        // This is the account that will be restricted
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        // Allow only OUTGOING transactions to the authorized ADDRESS
        restrictionFlags:
            models.AccountRestrictionFlags.ADDRESS.value |
            models.AccountRestrictionFlags.OUTGOING.value,
        // This is the only authorized outgoing address
        restrictionAdditions: [authAddress]
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    console.log('Enabling the restriction with transaction:');
    console.dir(transaction.toJson(), { colors: true, depth: null });

    return transaction;
}

トランザクションには以下のフィールドが含まれます。

  • signer_public_key: 制限設定を変更するアカウントの 公開鍵

  • restriction_flags: これらは AccountRestrictionFlags です。

    • ADDRESS は、制限がアドレスに適用されることを指定します。他の可能なスコープは MOSAIC_IDTRANSACTION_TYPE です。
    • OUTGOING は、送信トランザクションのみが影響を受けることを指定します。受信トランザクションの制限は、このフラグを除外することで独立して設定できます。

    デフォルトでは、リストされた値は「許可リスト(allowlist)」を形成します。指定されたアドレスのみが許可されます。

    リストされたアドレスを禁止する「拒否リスト(blocklist)」モードで制限を設定するには、BLOCK フラグを含めます。

    ネットワークはこれらのフラグを現在の値と XOR(排他的論理和)演算します。このチュートリアルでは、有効化する前に制限が存在しないことを確認しているため、この時点での現在の値は 0 です。

  • restriction_additions: 制限に追加するアドレス(またはモザイクID、トランザクションタイプ)のリスト。

    この例では、リストには許可されたアドレスのみが含まれます。

制限の解除⚓︎

制限を無効にするには、設定されているフラグとリストされたアドレスの両方をクリアする必要があります。

def restriction_disable_transaction(restriction):
    transaction = facade.transaction_factory.create({
        'type': 'account_address_restriction_transaction_v1',
        # This is the account whose restriction will be lifted
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        # Reverse flags
        'restriction_flags': restriction['restrictionFlags'],
        # Remove all addresses currently restricted
        'restriction_deletions': [
            Address.from_decoded_address_hex_string(addr) for addr in
                restriction['values']
        ]
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    print('Disabling the restriction with transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    return transaction
function restrictionDisableTransaction(timestamp, feeMult, restriction) {
    const transaction = facade.transactionFactory.create({
        type: 'account_address_restriction_transaction_v1',
        // This is the account whose restriction will be lifted
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        // Reverse flags
        restrictionFlags: restriction.restrictionFlags,
        // Remove all addresses currently restricted
        restrictionDeletions: restriction.values.map(hex =>
            Address.fromDecodedAddressHexString(hex))
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    console.log('Disabling the restriction with transaction:');
    console.dir(transaction.toJson(), { colors: true, depth: null });

    return transaction;
}

制限を有効にした時と同じ restriction_flags の値が再度提供されます。フラグはネットワークによって XOR されるため、同じ値を提供するとそれらがオフに切り替わり、実質的に制限がクリアされます。

現在制限に設定されているアドレスは restriction_deletions フィールドに指定され、設定から削除されます。

メソッドは、REST API から返される16進文字列形式を、トランザクション構築時に期待されるアドレス表現に変換します。

トランザクションの送信⚓︎

構築されたトランザクションは、転送トランザクション チュートリアルで説明されている通り、署名、アナウンス、承認されます。

    # Sign, announce and wait for confirmation
    json_payload = facade.transaction_factory.attach_signature(
        transaction,
        facade.sign_transaction(signer_key_pair, transaction))
    transaction_hash = facade.hash_transaction(transaction)
    announce_transaction(json_payload, 'restriction transaction')
    wait_for_confirmation(transaction_hash, 'restriction transaction')
    // Sign, announce and wait for confirmation
    let payload = SymbolTransactionFactory.attachSignature(
        transaction,
        facade.signTransaction(signerKeyPair, transaction));
    let hash =
        facade.hashTransaction(transaction).toString();
    await announceTransaction(payload, 'restriction transaction');
    await waitForConfirmation(hash, 'restriction transaction');

テスト転送の送信⚓︎

その後、未承認のアドレスに対してテスト用の転送が試行されます。

    # Try a dummy transfer to a random address with no mosaics
    transaction = facade.transaction_factory.create({
        'type': 'transfer_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'recipient_address': 'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    json_payload = facade.transaction_factory.attach_signature(
        transaction,
        facade.sign_transaction(signer_key_pair, transaction))
    transaction_hash = facade.hash_transaction(transaction)
    print('\n--- Attempting transfer to unauthorized address ---')
    announce_transaction(json_payload, 'test transfer')
    wait_for_confirmation(transaction_hash, 'test transfer')
    // Try a dummy transfer to a random address with no mosaics
    transaction = facade.transactionFactory.create({
        type: 'transfer_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey,
        deadline: timestamp.addHours(2).timestamp,
        recipientAddress: 'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);
    payload = SymbolTransactionFactory.attachSignature(
        transaction,
        facade.signTransaction(signerKeyPair, transaction));
    hash = facade.hashTransaction(transaction).toString();
    console.log('\n--- Attempting transfer to unauthorized address ---');
    await announceTransaction(payload, 'test transfer');
    await waitForConfirmation(hash, 'test transfer');

制限が有効になっている場合、転送は Address_Interaction_Prohibited エラーで失敗します。制限が解除されている場合、転送は正常に承認されます。

制限設定トランザクションとテスト転送は独立してアナウンスされ、承認されます。それぞれに個別の承認が必要なため、全体の実行時間が長くなる可能性があります。

このプロセスは、両方のトランザクションを単一の アグリゲートトランザクション に組み込んで一緒にアナウンスすることで最適化できます。

出力⚓︎

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

Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Authorized address: TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA
Fetching current network time from /node/time
  Network time: 104098647293 ms since nemesis
Fetching recommended fees from /network/fees/transaction
  Fee multiplier: 100
Getting restrictions from /restrictions/account/TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
  Response: No restrictions found

--- Enabling restriction ---
Enabling the restriction with transaction:
{
  "signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
  "version": 1,
  "network": 152,
  "type": 16720,
  "fee": "16000",
  "deadline": "104105847293",
  "restriction_flags": 16385,
  "restriction_additions": [
    "987D075454716222F609929E883174AD8C996D5828C938BC"
  ],
  "restriction_deletions": []
}
Announcing restriction transaction to /transactions
  Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for restriction transaction confirmation...
  Transaction status: unconfirmed
  Transaction status: unconfirmed
  ...
  Transaction status: confirmed
restriction transaction confirmed in 6 seconds

--- Attempting transfer to unauthorized address ---
Announcing test transfer to /transactions
  Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for test transfer confirmation...
  Transaction status: failed
test transfer failed: Failure_RestrictionAccount_Address_Interaction_Prohibited

出力の主なポイント:

  • 2-3行目: 関与するアカウントのアドレス。
  • 9行目 (Response: No restrictions found): 現在制限は設定されていません。
  • 21行目 ("restriction_flags": 16385): 0x4001ADDRESSOUTGOING の組み合わせに対応します。
  • 22-24行目 ("restriction_additions"): デコードされた16進数形式の、許可されたアドレスのリスト。この値は3行目に示されているアドレスに対応します。
  • 41行目 (test transfer failed): 期待通り、未承認の受信者アドレスにより Address_Interaction_Prohibited エラーが発生しています。
Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Authorized address: TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA
Fetching current network time from /node/time
  Network time: 104098759668 ms since nemesis
Fetching recommended fees from /network/fees/transaction
  Fee multiplier: 100
Getting restrictions from /restrictions/account/TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
  Response: [{'restrictionFlags': 16385, 'values': ['987D075454716222F609929E883174AD8C996D5828C938BC']}]

--- Disabling restriction ---
Disabling the restriction with transaction:
{
  "signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
  "version": 1,
  "network": 152,
  "type": 16720,
  "fee": "16000",
  "deadline": "104105959668",
  "restriction_flags": 16385,
  "restriction_additions": [],
  "restriction_deletions": [
    "987D075454716222F609929E883174AD8C996D5828C938BC"
  ]
}
Announcing restriction transaction to /transactions
  Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for restriction transaction confirmation...
  Transaction status: unconfirmed
  Transaction status: unconfirmed
  ...
  Transaction status: confirmed
restriction transaction confirmed in 2 seconds

--- Attempting transfer to unauthorized address ---
Announcing test transfer to /transactions
  Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for test transfer confirmation...
  Transaction status: unconfirmed
  Transaction status: unconfirmed
  ...
  Transaction status: confirmed
test transfer confirmed in 23 seconds

出力の主なポイント:

  • 2-3行目: 関与するアカウントのアドレス。
  • 9行目 (Response: [ ... ]): 既存の制限が検出されました。
  • 21行目 (restriction_flags): 制限を有効にした時と同じフラグ値。
  • 23-25行目 (restriction_deletions): 以前に設定されていたアドレスが削除されます。
  • 44行目 (test transfer confirmed): 制限が解除されたため、転送が正常に承認されました。

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

結論⚓︎

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

ステップ 関連ドキュメント
現在の制限設定の取得 /restrictions/account/{address} GET
制限の有効化 AccountAddressRestrictionTransactionV1
制限の解除 AccountAddressRestrictionTransactionV1