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);}
# 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}')
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[];}}
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;}
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;}
# 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');
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): 現在制限は設定されていません。
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