This tutorial demonstrates how to restrict an account's outgoing transactions
so that it can only send transactions to a single authorized address.
If the restriction is already enabled, the tutorial instead demonstrates how to remove it.
After enabling or disabling the restriction, a test transfer transaction to an unauthorized address is announced,
showing how the network rejects it.
Difference with Mosaic Restrictions
Symbol also supports mosaic restrictions, which are defined at the mosaic level rather than
at the account level as shown in this tutorial.
These are distinct mechanisms.
They are configured using different transaction types and operate under different rules.
However, account restrictions can limit which mosaics an account may interact with, and
mosaic restrictions can limit which accounts may interact with a mosaic.
The conceptual overlap is therefore a common source of confusion.
importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacade,Addressfromsymbolchain.scimportAmount,AccountRestrictionFlagsfromsymbolchain.symbol.NetworkimportNetworkTimestampNODE_URL=os.environ.get('NODE_URL','https://reference.symboltest.net:3001')print(f'Using node {NODE_URL}')# Helper function to announce a transactiondefannounce_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')withurllib.request.urlopen(request)asresponse:print(f' Response: {response.read().decode()}')# Helper function to wait for transaction confirmationdefwait_for_confirmation(transaction_hash,label):print(f'Waiting for {label} confirmation...')forattemptinrange(60):time.sleep(1)try:url=f'{NODE_URL}/transactionStatus/{transaction_hash}'withurllib.request.urlopen(url)asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print(f'{label} confirmed in {attempt} seconds')returnifstatus['group']=='failed':raiseException(f'{label} failed: {status["code"]}')excepturllib.error.HTTPError:print(' Transaction status: unknown')raiseException(f'{label} not confirmed after 60 seconds')# Returns the list of restrictions currently applied to the accountdefget_account_restrictions(address):restrictions_path=f'/restrictions/account/{address}'print(f'Getting restrictions from {restrictions_path}')try:url=f'{NODE_URL}{restrictions_path}'withurllib.request.urlopen(url)asresponse:status=json.loads(response.read().decode())restrictions=status['accountRestrictions']['restrictions']print(f' Response: {restrictions}')returnrestrictionsexcepturllib.error.HTTPError:# The address has never been usedprint(' Response: No restrictions found')return[]# Returns a transaction that restricts an accountdefrestriction_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))returntransaction# Returns a transaction that removes a restriction from an accountdefrestriction_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)foraddrinrestriction['values']]})transaction.fee=Amount(fee_mult*transaction.size)print('Disabling the restriction with transaction:')print(json.dumps(transaction.to_json(),indent=2))returntransactionfacade=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 timetime_path='/node/time'print(f'Fetching current network time from {time_path}')withurllib.request.urlopen(f'{NODE_URL}{time_path}')asresponse: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 feesfee_path='/network/fees/transaction'print(f'Fetching recommended fees from {fee_path}')withurllib.request.urlopen(f'{NODE_URL}{fee_path}')asresponse: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 performrestrictions=get_account_restrictions(signer_address)iflen(restrictions)==0:# Enable the restrictionprint('\n--- Enabling restriction ---')transaction=restriction_enable_transaction()else:# Disable the restrictionprint('\n--- Disabling restriction ---')transaction=restriction_disable_transaction(restrictions[0])# Sign, announce and wait for confirmationjson_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 mosaicstransaction=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')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{KeyPair,SymbolTransactionFactory,models,Address,NetworkTimestamp,SymbolFacade}from'symbol-sdk/symbol';constNODE_URL=process.env.NODE_URL||'https://reference.symboltest.net:3001';console.log('Using node',NODE_URL);// Helper function to announce a transactionasyncfunctionannounceTransaction(payload,label){console.log(`Announcing ${label} to /transactions`);constresponse=awaitfetch(`${NODE_URL}/transactions`,{method:'PUT',headers:{'Content-Type':'application/json'},body:payload});console.log(' Response:',awaitresponse.text());}// Helper function to wait for transaction confirmationasyncfunctionwaitForConfirmation(transactionHash,label){console.log(`Waiting for ${label} confirmation...`);for(letattempt=0;attempt<60;attempt++){awaitnewPromise(resolve=>setTimeout(resolve,1000));try{constresponse=awaitfetch(`${NODE_URL}/transactionStatus/${transactionHash}`);conststatus=awaitresponse.json();console.log(' Transaction status:',status.group);if(status.group==='confirmed'){console.log(`${label} confirmed in`,attempt,'seconds');return;}if(status.group==='failed'){thrownewError(`${label} failed: ${status.code}`);}}catch(e){if(e.message.includes('failed'))throwe;console.log(' Transaction status: unknown');}}thrownewError(`${label} not confirmed after 60 seconds`);}// Returns the list of restrictions currently applied to the accountasyncfunctiongetAccountRestrictions(address){constrestrictionsPath=`/restrictions/account/${address}`;console.log(`Getting restrictions from ${restrictionsPath}`);try{constresponse=awaitfetch(`${NODE_URL}${restrictionsPath}`);constjson=awaitresponse.json();constrestrictions=json.accountRestrictions.restrictions;console.log(' Response:',restrictions);returnrestrictions;}catch{console.log(' Response: No restrictions found');return[];}}// Returns a transaction that restricts an accountfunctionrestrictionEnableTransaction(timestamp,feeMult){consttransaction=facade.transactionFactory.create({type:'account_address_restriction_transaction_v1',// This is the account that will be restrictedsignerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,// Allow only OUTGOING transactions to the authorized ADDRESSrestrictionFlags:models.AccountRestrictionFlags.ADDRESS.value|models.AccountRestrictionFlags.OUTGOING.value,// This is the only authorized outgoing addressrestrictionAdditions:[authAddress]});transaction.fee=newmodels.Amount(feeMult*transaction.size);console.log('Enabling the restriction with transaction:');console.dir(transaction.toJson(),{colors:true,depth:null});returntransaction;}// Returns a transaction that removes a restriction from an accountfunctionrestrictionDisableTransaction(timestamp,feeMult,restriction){consttransaction=facade.transactionFactory.create({type:'account_address_restriction_transaction_v1',// This is the account whose restriction will be liftedsignerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,// Reverse flagsrestrictionFlags:restriction.restrictionFlags,// Remove all addresses currently restrictedrestrictionDeletions:restriction.values.map(hex=>Address.fromDecodedAddressHexString(hex))});transaction.fee=newmodels.Amount(feeMult*transaction.size);console.log('Disabling the restriction with transaction:');console.dir(transaction.toJson(),{colors:true,depth:null});returntransaction;}constfacade=newSymbolFacade('testnet');constSIGNER_PRIVATE_KEY=process.env.SIGNER_PRIVATE_KEY||'0000000000000000000000000000000000000000000000000000000000000000';constsignerKeyPair=newKeyPair(newPrivateKey(SIGNER_PRIVATE_KEY));constsignerAddress=facade.network.publicKeyToAddress(signerKeyPair.publicKey);console.log(`Signer address: ${signerAddress}`);constauthAddress='TB6QOVCUOFRCF5QJSKPIQMLUVWGJS3KYFDETRPA';console.log(`Authorized address: ${authAddress}`);try{// Fetch current network timeconsttimePath='/node/time';console.log('Fetching current network time from',timePath);consttimeResponse=awaitfetch(`${NODE_URL}${timePath}`);consttimeJSON=awaittimeResponse.json();consttimestamp=newNetworkTimestamp(timeJSON.communicationTimestamps.receiveTimestamp);console.log(' Network time:',timestamp.timestamp,'ms since nemesis');// Fetch recommended feesconstfeePath='/network/fees/transaction';console.log('Fetching recommended fees from',feePath);constfeeResponse=awaitfetch(`${NODE_URL}${feePath}`);constfeeJSON=awaitfeeResponse.json();constmedianMult=feeJSON.medianFeeMultiplier;constminimumMult=feeJSON.minFeeMultiplier;constfeeMult=Math.max(medianMult,minimumMult);console.log(' Fee multiplier:',feeMult);// Get current state of the restriction and decide which// operation to performconstrestrictions=awaitgetAccountRestrictions(signerAddress);lettransaction;if(restrictions.length===0){// Enable the restrictionconsole.log('\n--- Enabling restriction ---');transaction=restrictionEnableTransaction(timestamp,feeMult);}else{// Disable the restrictionconsole.log('\n--- Disabling restriction ---');transaction=restrictionDisableTransaction(timestamp,feeMult,restrictions[0]);}// Sign, announce and wait for confirmationletpayload=SymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(signerKeyPair,transaction));lethash=facade.hashTransaction(transaction).toString();awaitannounceTransaction(payload,'restriction transaction');awaitwaitForConfirmation(hash,'restriction transaction');// Try a dummy transfer to a random address with no mosaicstransaction=facade.transactionFactory.create({type:'transfer_transaction_v1',signerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,recipientAddress:'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'});transaction.fee=newmodels.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 ---');awaitannounceTransaction(payload,'test transfer');awaitwaitForConfirmation(hash,'test transfer');}catch(e){console.error(e.message);}
The code begins by defining two helper functions.
For details on how transactions are announced and how their confirmation is tracked, refer to
Transfer transaction tutorial.
The remaining helper functions are described in the sections below.
An account can only configure restrictions on itself, so this tutorial requires a single private key.
The private key can be provided through the SIGNER_PRIVATE_KEY environment variable
(as a 64-character hexadecimal string).
If it is not provided, a default value is used.
The account must hold sufficient funds to announce transactions.
If the default key is used, the corresponding account may already be funded.
At this stage, the authorized address is also configured.
The restriction will later limit outgoing transactions to this address only.
# Fetch current network timetime_path='/node/time'print(f'Fetching current network time from {time_path}')withurllib.request.urlopen(f'{NODE_URL}{time_path}')asresponse: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 feesfee_path='/network/fees/transaction'print(f'Fetching recommended fees from {fee_path}')withurllib.request.urlopen(f'{NODE_URL}{fee_path}')asresponse: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}')
The following function retrieves the current account restrictions applied to a given address using the
/restrictions/account/{address}GET endpoint.
If no restrictions are configured, the function returns an empty list.
defget_account_restrictions(address):restrictions_path=f'/restrictions/account/{address}'print(f'Getting restrictions from {restrictions_path}')try:url=f'{NODE_URL}{restrictions_path}'withurllib.request.urlopen(url)asresponse:status=json.loads(response.read().decode())restrictions=status['accountRestrictions']['restrictions']print(f' Response: {restrictions}')returnrestrictionsexcepturllib.error.HTTPError:# The address has never been usedprint(' Response: No restrictions found')return[]
asyncfunctiongetAccountRestrictions(address){constrestrictionsPath=`/restrictions/account/${address}`;console.log(`Getting restrictions from ${restrictionsPath}`);try{constresponse=awaitfetch(`${NODE_URL}${restrictionsPath}`);constjson=awaitresponse.json();constrestrictions=json.accountRestrictions.restrictions;console.log(' Response:',restrictions);returnrestrictions;}catch{console.log(' Response: No restrictions found');return[];}}
The returned list is then evaluated to determine the tutorial's execution path.
Based on its contents, the appropriate configuration transaction is constructed,
either to enable or to remove the restriction.
constrestrictions=awaitgetAccountRestrictions(signerAddress);lettransaction;if(restrictions.length===0){// Enable the restrictionconsole.log('\n--- Enabling restriction ---');transaction=restrictionEnableTransaction(timestamp,feeMult);}else{// Disable the restrictionconsole.log('\n--- Disabling restriction ---');transaction=restrictionDisableTransaction(timestamp,feeMult,restrictions[0]);}
If multiple restrictions are configured on the account, only the first one returned by the endpoint is removed.
This situation should not occur in this tutorial.
defrestriction_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))returntransaction
functionrestrictionEnableTransaction(timestamp,feeMult){consttransaction=facade.transactionFactory.create({type:'account_address_restriction_transaction_v1',// This is the account that will be restrictedsignerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,// Allow only OUTGOING transactions to the authorized ADDRESSrestrictionFlags:models.AccountRestrictionFlags.ADDRESS.value|models.AccountRestrictionFlags.OUTGOING.value,// This is the only authorized outgoing addressrestrictionAdditions:[authAddress]});transaction.fee=newmodels.Amount(feeMult*transaction.size);console.log('Enabling the restriction with transaction:');console.dir(transaction.toJson(),{colors:true,depth:null});returntransaction;}
The transaction includes the following fields:
signer_public_key: public key of the account whose restriction configuration will be modified.
restriction_flags:
AccountRestrictionFlags.ADDRESS specifies that the restriction applies to addresses.
Other possible scopes are MOSAIC_ID and TRANSACTION_TYPE.
AccountRestrictionFlags.OUTGOING specifies that only outgoing transactions are affected.
Incoming transaction restrictions can be configured independently by omitting this flag.
By default, the listed values form an allowlist.
Only the specified addresses are allowed.
To configure the restriction in blocklist mode, where the listed addresses are forbidden,
include the AccountRestrictionFlags.BLOCK flag.
The network XOR's these flags with the current value, which at this point is 0 because the tutorial makes sure
no restriction is present before enabling it.
restriction_additions: list of addresses (or mosaic IDs, or transaction types) to be added to the restriction.
In this case, the list contains only the authorized address.
defrestriction_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)foraddrinrestriction['values']]})transaction.fee=Amount(fee_mult*transaction.size)print('Disabling the restriction with transaction:')print(json.dumps(transaction.to_json(),indent=2))returntransaction
functionrestrictionDisableTransaction(timestamp,feeMult,restriction){consttransaction=facade.transactionFactory.create({type:'account_address_restriction_transaction_v1',// This is the account whose restriction will be liftedsignerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,// Reverse flagsrestrictionFlags:restriction.restrictionFlags,// Remove all addresses currently restrictedrestrictionDeletions:restriction.values.map(hex=>Address.fromDecodedAddressHexString(hex))});transaction.fee=newmodels.Amount(feeMult*transaction.size);console.log('Disabling the restriction with transaction:');console.dir(transaction.toJson(),{colors:true,depth:null});returntransaction;}
The same restriction_flags values used when enabling the restriction are provided again.
Because the flags are XOR'ed by the network, supplying the same values toggles them off,
effectively clearing the restriction.
The addresses currently configured in the restriction are supplied in the restriction_deletions field
so they can be removed from the configuration.
The method converts the hexadecimal string format returned by the REST API
into the address representation expected when constructing a transaction.
# Sign, announce and wait for confirmationjson_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 confirmationletpayload=SymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(signerKeyPair,transaction));lethash=facade.hashTransaction(transaction).toString();awaitannounceTransaction(payload,'restriction transaction');awaitwaitForConfirmation(hash,'restriction transaction');
# Try a dummy transfer to a random address with no mosaicstransaction=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 mosaicstransaction=facade.transactionFactory.create({type:'transfer_transaction_v1',signerPublicKey:signerKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,recipientAddress:'TBBHGE77IHHOIYA46B3XSORRNR2L5MLW54YO75Y'});transaction.fee=newmodels.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 ---');awaitannounceTransaction(payload,'test transfer');awaitwaitForConfirmation(hash,'test transfer');
If the restriction has been enabled, the transfer fails with an Address_Interaction_Prohibited error.
If the restriction has been removed, the transfer is confirmed successfully.
The restriction configuration transaction and the test transfer are announced and confirmed independently.
Each requires its own confirmation, which may increase the total execution time.
The process could be optimized by embedding both transactions in a single aggregate transaction
and announcing them together.
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
Key points in the output:
Lines 2-3: Addresses of the involved accounts.
Line 9 (Response: No restrictions found): No restrictions are currently configured.
Line 21 ("restriction_flags": 16385): 0x4001 corresponds to the combination of ADDRESS and OUTGOING.
Line 22-24 ("restriction_additions"): List of allowed addresses, in decoded hexadecimal format.
The value corresponds to the address shown in line 3.
Line 41 (test transfer failed): The unauthorized recipient address results in an
Address_Interaction_Prohibited error, as expected.
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
Key points in the output:
Lines 2-3: Addresses of the involved accounts.
Line 9 (Response: [ ... ]): Existing restrictions are detected.
Line 21 (restriction_flags): Same flag value used when enabling the restriction.
Line 23-25 (restriction_deletions): The previously configured address is removed.
Line 44 (test transfer confirmed): The transfer is confirmed successfully because the restriction has been
lifted.
The transaction hashes shown in the output can be used to look up the transactions in the
Symbol Testnet Explorer.