importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.symbol.IdGeneratorimportgenerate_mosaic_alias_idNODE_URL=os.getenv('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)asannounce_response:print(f' Response: {announce_response.read().decode()}')# Helper function to wait for transaction confirmationdefwait_for_confirmation(tx_hash,label):print(f'Waiting for {label} confirmation...')forattemptinrange(60):time.sleep(1)try:url=f'{NODE_URL}/transactionStatus/{tx_hash}'withurllib.request.urlopen(url)asconfirm_response:status=json.loads(confirm_response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print(f'{label} confirmed in {attempt} seconds')returnifstatus['group']=='failed':raiseRuntimeError(f'{label} failed: {status["code"]}')excepturllib.error.HTTPError:print(' Transaction status: unknown')raiseTimeoutError(f'{label} not confirmed after 60 seconds')# Account A (initiates the aggregate tx and sends XYM to Account B)ACCOUNT_A_PRIVATE_KEY=os.getenv('ACCOUNT_A_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000000')account_a_key_pair=SymbolFacade.KeyPair(PrivateKey(ACCOUNT_A_PRIVATE_KEY))# Account B (sends custom mosaic to Account A)ACCOUNT_B_PRIVATE_KEY=os.getenv('ACCOUNT_B_PRIVATE_KEY','1111111111111111111111111111111111111111111111111111111111111111')account_b_key_pair=SymbolFacade.KeyPair(PrivateKey(ACCOUNT_B_PRIVATE_KEY))facade=SymbolFacade('testnet')account_a_address=facade.network.public_key_to_address(account_a_key_pair.public_key)account_b_address=facade.network.public_key_to_address(account_b_key_pair.public_key)print(f'Account A: {account_a_address}')print(f'Account B: {account_b_address}')try:# 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_multiplier=response_json['medianFeeMultiplier']minimum_multiplier=response_json['minFeeMultiplier']fee_multiplier=max(median_multiplier,minimum_multiplier)print(f' Fee multiplier: {fee_multiplier}')# Embedded tx 1: Account A transfers 10 XYM to Account Bembedded_transaction_1=(facade.create_embedded_transaction_from_descriptor({'type':'transfer_transaction_v1','recipient_address':account_b_address,'mosaics':[{'mosaic_id':generate_mosaic_alias_id('symbol.xym'),'amount':10_000_000# 10 XYM}]},account_a_key_pair.public_key))# Embedded tx 2: Account B transfers 1 custom mosaic to Account Acustom_mosaic_id=0x6D1314BE751B62C2embedded_transaction_2=(facade.create_embedded_transaction_from_descriptor({'type':'transfer_transaction_v1','recipient_address':account_a_address,'mosaics':[{'mosaic_id':custom_mosaic_id,'amount':1# 1 custom mosaic}]},account_b_key_pair.public_key))# Build the aggregate transactionembedded_transactions=[embedded_transaction_1,embedded_transaction_2]transaction=facade.create_transaction_from_descriptor({'type':'aggregate_complete_transaction_v3','transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions},account_a_key_pair.public_key,fee_multiplier,2*60*60,1)print('Built aggregate transaction without signatures:')print(json.dumps(transaction.to_json(),indent=2))# --- ACCOUNT A (Initiator) ---print('[Account A] Signing the aggregate...')signature_a=facade.sign_transaction(account_a_key_pair,transaction)transaction_payload=facade.transaction_factory.attach_signature(transaction,signature_a)payload_formatted=json.dumps(json.loads(transaction_payload),indent=2)print(f'[Account A] Payload ready to share:\n{payload_formatted}')# --- OFF-CHAIN COORDINATION ---# Account A sends the payload to Account Bshared_payload=transaction_payloadprint('[Account A] ==> Payload sent to Account B (offchain)')# --- ACCOUNT B (Cosignatory) ---received_transaction=facade.transaction_factory.deserialize(bytes.fromhex(json.loads(shared_payload)['payload']))print('[Account B] Cosigning...')cosignature_b=facade.cosign_transaction(account_b_key_pair,received_transaction)cosignature_formatted=json.dumps(cosignature_b.to_json(),indent=2)print(f'[Account B] Cosignature created: {cosignature_formatted}')# --- OFF-CHAIN COORDINATION ---# Account B sends the cosignature back to Account Ashared_cosignature=cosignature_bprint('[Account B] <== Cosignature sent back to Account A (offchain)')# --- ACCOUNT A (Initiator) ---# Add cosignature to the transaction and rebuild payloadtransaction.cosignatures.append(shared_cosignature)transaction_payload=facade.transaction_factory.to_json(transaction)json_payload=transaction_payloadprint('[Account A] Ready to announce')# Announce the transactiontransaction_hash=facade.hash_transaction(transaction)print(f'Transaction hash: {transaction_hash}')announce_transaction(json_payload,'transaction')# Wait for confirmationwait_for_confirmation(transaction_hash,'transaction')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{SymbolFacade,descriptors,generateMosaicAliasId,models}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;60>attempt;attempt++){awaitnewPromise(resolve=>{setTimeout(resolve,1000);});constresponse=awaitfetch(`${NODE_URL}/transactionStatus/${transactionHash}`);if(!response.ok){if(404===response.status){console.log(' Transaction status: unknown');continue;}thrownewError(`HTTP ${response.status}`);}conststatus=awaitresponse.json();console.log(' Transaction status:',status.group);if('confirmed'===status.group){console.log(`${label} confirmed in`,attempt,'seconds');return;}if('failed'===status.group)thrownewError(`${label} failed: ${status.code}`);}thrownewError(`${label} not confirmed after 60 seconds`);}// Account A (initiates the aggregate tx and sends XYM to Account B)constACCOUNT_A_PRIVATE_KEY=process.env.ACCOUNT_A_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000000');constaccountAKeyPair=newSymbolFacade.KeyPair(newPrivateKey(ACCOUNT_A_PRIVATE_KEY));// Account B (sends custom mosaic to Account A)constACCOUNT_B_PRIVATE_KEY=process.env.ACCOUNT_B_PRIVATE_KEY||('1111111111111111111111111111111111111111111111111111111111111111');constaccountBKeyPair=newSymbolFacade.KeyPair(newPrivateKey(ACCOUNT_B_PRIVATE_KEY));constfacade=newSymbolFacade('testnet');constaccountAAddress=facade.network.publicKeyToAddress(accountAKeyPair.publicKey);constaccountBAddress=facade.network.publicKeyToAddress(accountBKeyPair.publicKey);console.log('Account A:',accountAAddress.toString());console.log('Account B:',accountBAddress.toString());try{// Fetch recommended feesconstfeePath='/network/fees/transaction';console.log('Fetching recommended fees from',feePath);constfeeResponse=awaitfetch(`${NODE_URL}${feePath}`);constfeeJSON=awaitfeeResponse.json();constmedianMultiplier=feeJSON.medianFeeMultiplier;constminimumMultiplier=feeJSON.minFeeMultiplier;constfeeMultiplier=Math.max(medianMultiplier,minimumMultiplier);console.log(' Fee multiplier:',feeMultiplier);// Embedded tx 1: Account A transfers 10 XYM to Account BconstembeddedTransaction1=facade.createEmbeddedTransactionFromTypedDescriptor(newdescriptors.TransferTransactionV1Descriptor(accountBAddress,[newdescriptors.UnresolvedMosaicDescriptor(generateMosaicAliasId('symbol.xym'),newmodels.Amount(10_000_000n))// 10 XYM],undefined),accountAKeyPair.publicKey);// Embedded tx 2: Account B transfers 1 custom mosaic to Account AconstcustomMosaicId=0x6D1314BE751B62C2n;constembeddedTransaction2=facade.createEmbeddedTransactionFromTypedDescriptor(newdescriptors.TransferTransactionV1Descriptor(accountAAddress,[newdescriptors.UnresolvedMosaicDescriptor(customMosaicId,newmodels.Amount(1n))// 1 custom mosaic],undefined),accountBKeyPair.publicKey);// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction1,embeddedTransaction2];consttransaction=facade.createTransactionFromTypedDescriptor(newdescriptors.AggregateCompleteTransactionV3Descriptor(facade.static.hashEmbeddedTransactions(embeddedTransactions),embeddedTransactions,undefined),accountAKeyPair.publicKey,feeMultiplier,2*60*60,1);console.log('Built aggregate transaction without signatures:');console.log(JSON.stringify(transaction.toJson(),null,2));// --- ACCOUNT A (Initiator) ---console.log('[Account A] Signing the aggregate...');constsignatureA=facade.signTransaction(accountAKeyPair,transaction);consttransactionPayload=facade.transactionFactory.static.attachSignature(transaction,signatureA);constpayloadFormatted=JSON.stringify(JSON.parse(transactionPayload),null,2);console.log('[Account A] Payload ready to share:\n',payloadFormatted);// --- OFF-CHAIN COORDINATION ---// Account A sends the payload to Account BconstsharedPayload=transactionPayload;console.log('[Account A] ==> Payload sent to Account B (offchain)');// --- ACCOUNT B (Cosignatory) ---constpayloadHex=JSON.parse(sharedPayload).payload;constreceivedTransaction=facade.transactionFactory.static.deserialize(Buffer.from(payloadHex,'hex'));console.log('[Account B] Cosigning...');constcosignatureB=facade.cosignTransaction(accountBKeyPair,receivedTransaction);constcosignatureFormatted=JSON.stringify(cosignatureB.toJson(),null,2);console.log('[Account B] Cosignature created:',cosignatureFormatted);// --- OFF-CHAIN COORDINATION ---// Account B sends the cosignature back to Account AconstsharedCosignature=cosignatureB;console.log('[Account B] <== Cosignature sent back to Account A','(offchain)');// --- ACCOUNT A (Initiator) ---// Add cosignature to the transaction and rebuild payloadtransaction.cosignatures.push(sharedCosignature);consttransactionPayloadFinal=facade.transactionFactory.static.toJson(transaction);constjsonPayload=transactionPayloadFinal;console.log('[Account A] Ready to announce');// Announce the transactionconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Transaction hash:',transactionHash);awaitannounceTransaction(jsonPayload,'transaction');// Wait for confirmationawaitwaitForConfirmation(transactionHash,'transaction');}catch(e){console.error(e.message,'| Cause:',e.cause?.code??'unknown');}
//JAVA 21+//DEPS org.symbol:symbol-sdk:3.3.1importjava.io.IOException;importjava.net.URI;importjava.net.http.HttpClient;importjava.net.http.HttpRequest;importjava.net.http.HttpResponse;importjava.net.http.HttpResponse.BodyHandlers;importjava.util.HexFormat;importjava.util.List;importcom.fasterxml.jackson.databind.JsonNode;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.symbol.sdk.CryptoTypes;importorg.symbol.sdk.facade.SymbolFacade;importorg.symbol.sdk.symbol.Address;importorg.symbol.sdk.symbol.IdGenerator;importorg.symbol.sdk.symbol.KeyPair;importorg.symbol.sdk.symbol.SymbolTransactionFactory;importorg.symbol.sdk.symbol.descriptors.*;importorg.symbol.sdk.symbol.models.*;publicfinalclassCompleteAggregate{privatestaticfinalObjectMapperJSON_MAPPER=newObjectMapper();privatestaticfinalHttpClientHTTP_CLIENT=HttpClient.newHttpClient();privatefinalStringnodeUrl=System.getenv().getOrDefault("NODE_URL","https://reference.symboltest.net:3001");privatefinalSymbolFacadefacade=newSymbolFacade("testnet");privatevoidannounceTransaction(finalStringpayload,finalStringlabel)throwsIOException,InterruptedException{System.out.printf("Announcing %s to /transactions%n",label);finalHttpRequestrequest=HttpRequest.newBuilder(URI.create(nodeUrl+"/transactions")).header("Content-Type","application/json").PUT(HttpRequest.BodyPublishers.ofString(payload)).build();finalHttpResponse<String>response=HTTP_CLIENT.send(request,BodyHandlers.ofString());System.out.printf(" Response: %s%n",response.body());}privatevoidwaitForConfirmation(finalStringtransactionHash,finalStringlabel)throwsIOException,InterruptedException{System.out.printf("Waiting for %s confirmation...%n",label);for(intattempt=0;60>attempt;++attempt){Thread.sleep(1000);finalStringstatusPath="/transactionStatus/"+transactionHash;finalHttpRequeststatusRequest=HttpRequest.newBuilder(URI.create(nodeUrl+statusPath)).GET().build();finalHttpResponse<String>statusResponse=HTTP_CLIENT.send(statusRequest,BodyHandlers.ofString());if(404==statusResponse.statusCode()){System.out.println(" Transaction status: unknown");continue;}if(2!=statusResponse.statusCode()/100)thrownewIOException("HTTP "+statusResponse.statusCode());finalJsonNodestatus=JSON_MAPPER.readTree(statusResponse.body());finalStringgroup=status.get("group").asText();System.out.printf(" Transaction status: %s%n",group);if("confirmed".equals(group)){System.out.printf("%s confirmed in %d seconds%n",label,attempt);return;}if("failed".equals(group))thrownewIOException(String.format("%s failed: %s",label,status.get("code").asText()));}thrownewIOException(String.format("%s not confirmed after 60 seconds",label));}publicstaticvoidmain(finalString[]args){try{newCompleteAggregate().run();}catch(finalExceptionex){System.out.println(null==ex.getMessage()?ex.toString():ex.getMessage());}}privatevoidrun()throwsIOException,InterruptedException{System.out.printf("Using node %s%n",nodeUrl);// Account A (initiates the aggregate tx and sends XYM to// Account B)finalStringaccountAPrivateKey=System.getenv().getOrDefault("ACCOUNT_A_PRIVATE_KEY","0".repeat(64));finalKeyPairaccountAKeyPair=newKeyPair(newCryptoTypes.PrivateKey(accountAPrivateKey));// Account B (sends custom mosaic to Account A)finalStringaccountBPrivateKey=System.getenv().getOrDefault("ACCOUNT_B_PRIVATE_KEY","1".repeat(64));finalKeyPairaccountBKeyPair=newKeyPair(newCryptoTypes.PrivateKey(accountBPrivateKey));finalAddressaccountAAddress=facade.network.publicKeyToAddress(accountAKeyPair.getPublicKey());finalAddressaccountBAddress=facade.network.publicKeyToAddress(accountBKeyPair.getPublicKey());System.out.printf("Account A: %s%n",accountAAddress);System.out.printf("Account B: %s%n",accountBAddress);// Fetch recommended feesfinalStringfeePath="/network/fees/transaction";System.out.printf("Fetching recommended fees from %s%n",feePath);finalHttpRequestfeeRequest=HttpRequest.newBuilder(URI.create(nodeUrl+feePath)).GET().build();finalHttpResponse<String>feeResponse=HTTP_CLIENT.send(feeRequest,BodyHandlers.ofString());finalJsonNodefeeJSON=JSON_MAPPER.readTree(feeResponse.body());finallongmedianMultiplier=feeJSON.get("medianFeeMultiplier").asLong();finallongminimumMultiplier=feeJSON.get("minFeeMultiplier").asLong();finallongfeeMultiplier=Math.max(medianMultiplier,minimumMultiplier);System.out.printf(" Fee multiplier: %d%n",feeMultiplier);// Embedded tx 1: Account A transfers 10 XYM to Account BfinalEmbeddedTransactionembeddedTransaction1=facade.createEmbeddedTransactionFromTypedDescriptor(newTransferTransactionV1Descriptor(accountBAddress,List.of(newUnresolvedMosaicDescriptor(newUnresolvedMosaicId(IdGenerator.generateMosaicAliasId("symbol.xym")),newAmount(10_000_000))),// 10 XYMnull),accountAKeyPair.getPublicKey());// Embedded tx 2: Account B transfers 1 custom mosaic to Account AfinallongcustomMosaicId=0x6D1314BE751B62C2L;finalEmbeddedTransactionembeddedTransaction2=facade.createEmbeddedTransactionFromTypedDescriptor(newTransferTransactionV1Descriptor(accountAAddress,List.of(newUnresolvedMosaicDescriptor(newUnresolvedMosaicId(customMosaicId),newAmount(1))),// 1 custom mosaicnull),accountBKeyPair.getPublicKey());// Build the aggregate transactionfinalList<EmbeddedTransaction>embeddedTransactions=List.of(embeddedTransaction1,embeddedTransaction2);finalAggregateCompleteTransactionV3transaction=(AggregateCompleteTransactionV3)facade.createTransactionFromTypedDescriptor(newAggregateCompleteTransactionV3Descriptor(SymbolFacade.hashEmbeddedTransactions(embeddedTransactions),embeddedTransactions,null),accountAKeyPair.getPublicKey(),feeMultiplier,2*60*60,1);System.out.println("Built aggregate transaction without signatures:");System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(transaction.toJson()));// --- ACCOUNT A (Initiator) ---System.out.println("[Account A] Signing the aggregate...");finalCryptoTypes.SignaturesignatureA=facade.signTransaction(accountAKeyPair,transaction);finalStringtransactionPayload=SymbolTransactionFactory.attachSignature(transaction,signatureA);finalStringpayloadFormatted=JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(JSON_MAPPER.readTree(transactionPayload));System.out.printf("[Account A] Payload ready to share:%n %s%n",payloadFormatted);// --- OFF-CHAIN COORDINATION ---// Account A sends the payload to Account BfinalStringsharedPayload=transactionPayload;System.out.println("[Account A] ==> Payload sent to Account B (offchain)");// --- ACCOUNT B (Cosignatory) ---finalStringpayloadHex=JSON_MAPPER.readTree(sharedPayload).get("payload").asText();finalTransactionreceivedTransaction=SymbolTransactionFactory.deserialize(HexFormat.of().parseHex(payloadHex));System.out.println("[Account B] Cosigning...");finalCosignaturecosignatureB=facade.cosignTransaction(accountBKeyPair,receivedTransaction);finalStringcosignatureFormatted=JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(cosignatureB.toJson());System.out.printf("[Account B] Cosignature created: %s%n",cosignatureFormatted);// --- OFF-CHAIN COORDINATION ---// Account B sends the cosignature back to Account AfinalCosignaturesharedCosignature=cosignatureB;System.out.println("[Account B] <== Cosignature sent back "+"to Account A (offchain)");// --- ACCOUNT A (Initiator) ---// Add cosignature to the transaction and rebuild payloadtransaction.getCosignatures().add(sharedCosignature);finalStringtransactionPayloadFinal=SymbolTransactionFactory.toJson(transaction);finalStringjsonPayload=transactionPayloadFinal;System.out.println("[Account A] Ready to announce");// Announce the transactionfinalStringtransactionHash=facade.hashTransaction(transaction).toString();System.out.printf("Transaction hash: %s%n",transactionHash);announceTransaction(jsonPayload,"transaction");// Wait for confirmationwaitForConfirmation(transactionHash,"transaction");}}
# Account A (initiates the aggregate tx and sends XYM to Account B)ACCOUNT_A_PRIVATE_KEY=os.getenv('ACCOUNT_A_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000000')account_a_key_pair=SymbolFacade.KeyPair(PrivateKey(ACCOUNT_A_PRIVATE_KEY))# Account B (sends custom mosaic to Account A)ACCOUNT_B_PRIVATE_KEY=os.getenv('ACCOUNT_B_PRIVATE_KEY','1111111111111111111111111111111111111111111111111111111111111111')account_b_key_pair=SymbolFacade.KeyPair(PrivateKey(ACCOUNT_B_PRIVATE_KEY))facade=SymbolFacade('testnet')account_a_address=facade.network.public_key_to_address(account_a_key_pair.public_key)account_b_address=facade.network.public_key_to_address(account_b_key_pair.public_key)print(f'Account A: {account_a_address}')print(f'Account B: {account_b_address}')
// Account A (initiates the aggregate tx and sends XYM to Account B)constACCOUNT_A_PRIVATE_KEY=process.env.ACCOUNT_A_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000000');constaccountAKeyPair=newSymbolFacade.KeyPair(newPrivateKey(ACCOUNT_A_PRIVATE_KEY));// Account B (sends custom mosaic to Account A)constACCOUNT_B_PRIVATE_KEY=process.env.ACCOUNT_B_PRIVATE_KEY||('1111111111111111111111111111111111111111111111111111111111111111');constaccountBKeyPair=newSymbolFacade.KeyPair(newPrivateKey(ACCOUNT_B_PRIVATE_KEY));constfacade=newSymbolFacade('testnet');constaccountAAddress=facade.network.publicKeyToAddress(accountAKeyPair.publicKey);constaccountBAddress=facade.network.publicKeyToAddress(accountBKeyPair.publicKey);console.log('Account A:',accountAAddress.toString());console.log('Account B:',accountBAddress.toString());
// Account A (initiates the aggregate tx and sends XYM to// Account B)finalStringaccountAPrivateKey=System.getenv().getOrDefault("ACCOUNT_A_PRIVATE_KEY","0".repeat(64));finalKeyPairaccountAKeyPair=newKeyPair(newCryptoTypes.PrivateKey(accountAPrivateKey));// Account B (sends custom mosaic to Account A)finalStringaccountBPrivateKey=System.getenv().getOrDefault("ACCOUNT_B_PRIVATE_KEY","1".repeat(64));finalKeyPairaccountBKeyPair=newKeyPair(newCryptoTypes.PrivateKey(accountBPrivateKey));finalAddressaccountAAddress=facade.network.publicKeyToAddress(accountAKeyPair.getPublicKey());finalAddressaccountBAddress=facade.network.publicKeyToAddress(accountBKeyPair.getPublicKey());System.out.printf("Account A: %s%n",accountAAddress);System.out.printf("Account B: %s%n",accountBAddress);
この例では、簡略化のため1つのスクリプトに両方の 秘密鍵 を含めています。実際には、各当事者が自身のマシンで署名します。
アカウント A は、 埋め込みトランザクション の署名者としてアカウント B を設定し、B の アドレス を派生させるために、アカウント B の 公開鍵 のみを必要とします 。
環境変数 ACCOUNT_A_PRIVATE_KEY と ACCOUNT_B_PRIVATE_KEY で各アカウントの鍵を設定します。提供されない場合は、デフォルトのテストキーが使用されます。自身の鍵を使用する場合は、アカウント A が XYM を持ち、アカウント B がスワップ用のカスタムモザイクを保持していることを確認してください。
# Build the aggregate transactionembedded_transactions=[embedded_transaction_1,embedded_transaction_2]transaction=facade.create_transaction_from_descriptor({'type':'aggregate_complete_transaction_v3','transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions},account_a_key_pair.public_key,fee_multiplier,2*60*60,1)print('Built aggregate transaction without signatures:')print(json.dumps(transaction.to_json(),indent=2))
// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction1,embeddedTransaction2];consttransaction=facade.createTransactionFromTypedDescriptor(newdescriptors.AggregateCompleteTransactionV3Descriptor(facade.static.hashEmbeddedTransactions(embeddedTransactions),embeddedTransactions,undefined),accountAKeyPair.publicKey,feeMultiplier,2*60*60,1);console.log('Built aggregate transaction without signatures:');console.log(JSON.stringify(transaction.toJson(),null,2));
// Build the aggregate transactionfinalList<EmbeddedTransaction>embeddedTransactions=List.of(embeddedTransaction1,embeddedTransaction2);finalAggregateCompleteTransactionV3transaction=(AggregateCompleteTransactionV3)facade.createTransactionFromTypedDescriptor(newAggregateCompleteTransactionV3Descriptor(SymbolFacade.hashEmbeddedTransactions(embeddedTransactions),embeddedTransactions,null),accountAKeyPair.getPublicKey(),feeMultiplier,2*60*60,1);System.out.println("Built aggregate transaction without signatures:");System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(transaction.toJson()));
# --- ACCOUNT A (Initiator) ---print('[Account A] Signing the aggregate...')signature_a=facade.sign_transaction(account_a_key_pair,transaction)transaction_payload=facade.transaction_factory.attach_signature(transaction,signature_a)payload_formatted=json.dumps(json.loads(transaction_payload),indent=2)print(f'[Account A] Payload ready to share:\n{payload_formatted}')# --- OFF-CHAIN COORDINATION ---# Account A sends the payload to Account Bshared_payload=transaction_payloadprint('[Account A] ==> Payload sent to Account B (offchain)')
// --- ACCOUNT A (Initiator) ---console.log('[Account A] Signing the aggregate...');constsignatureA=facade.signTransaction(accountAKeyPair,transaction);consttransactionPayload=facade.transactionFactory.static.attachSignature(transaction,signatureA);constpayloadFormatted=JSON.stringify(JSON.parse(transactionPayload),null,2);console.log('[Account A] Payload ready to share:\n',payloadFormatted);// --- OFF-CHAIN COORDINATION ---// Account A sends the payload to Account BconstsharedPayload=transactionPayload;console.log('[Account A] ==> Payload sent to Account B (offchain)');
// --- ACCOUNT A (Initiator) ---System.out.println("[Account A] Signing the aggregate...");finalCryptoTypes.SignaturesignatureA=facade.signTransaction(accountAKeyPair,transaction);finalStringtransactionPayload=SymbolTransactionFactory.attachSignature(transaction,signatureA);finalStringpayloadFormatted=JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(JSON_MAPPER.readTree(transactionPayload));System.out.printf("[Account A] Payload ready to share:%n %s%n",payloadFormatted);// --- OFF-CHAIN COORDINATION ---// Account A sends the payload to Account BfinalStringsharedPayload=transactionPayload;System.out.println("[Account A] ==> Payload sent to Account B (offchain)");
アカウント A は SDK を使用してトランザクションに署名し、中間ペイロードを生成します。アカウント B の連署が欠けているため、このペイロードはまだアナウンスできる状態ではありません。アカウント A はオフチェーンのチャネルを通じて、この中間ペイロードをアカウント B に送信します。
アグリゲートトランザクションの署名
複数の埋め込みトランザクションで署名者として表示される場合でも、アカウントは一度だけ署名します。このチュートリアルでは、アカウント A がアグリゲートトランザクションに署名しますが、これはアグリゲート自体と、アカウント A が署名者である最初の埋め込みトランザクションの両方をカバーします。
# --- ACCOUNT B (Cosignatory) ---received_transaction=facade.transaction_factory.deserialize(bytes.fromhex(json.loads(shared_payload)['payload']))print('[Account B] Cosigning...')cosignature_b=facade.cosign_transaction(account_b_key_pair,received_transaction)cosignature_formatted=json.dumps(cosignature_b.to_json(),indent=2)print(f'[Account B] Cosignature created: {cosignature_formatted}')# --- OFF-CHAIN COORDINATION ---# Account B sends the cosignature back to Account Ashared_cosignature=cosignature_bprint('[Account B] <== Cosignature sent back to Account A (offchain)')
// --- ACCOUNT B (Cosignatory) ---constpayloadHex=JSON.parse(sharedPayload).payload;constreceivedTransaction=facade.transactionFactory.static.deserialize(Buffer.from(payloadHex,'hex'));console.log('[Account B] Cosigning...');constcosignatureB=facade.cosignTransaction(accountBKeyPair,receivedTransaction);constcosignatureFormatted=JSON.stringify(cosignatureB.toJson(),null,2);console.log('[Account B] Cosignature created:',cosignatureFormatted);// --- OFF-CHAIN COORDINATION ---// Account B sends the cosignature back to Account AconstsharedCosignature=cosignatureB;console.log('[Account B] <== Cosignature sent back to Account A','(offchain)');
// --- ACCOUNT B (Cosignatory) ---finalStringpayloadHex=JSON_MAPPER.readTree(sharedPayload).get("payload").asText();finalTransactionreceivedTransaction=SymbolTransactionFactory.deserialize(HexFormat.of().parseHex(payloadHex));System.out.println("[Account B] Cosigning...");finalCosignaturecosignatureB=facade.cosignTransaction(accountBKeyPair,receivedTransaction);finalStringcosignatureFormatted=JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(cosignatureB.toJson());System.out.printf("[Account B] Cosignature created: %s%n",cosignatureFormatted);// --- OFF-CHAIN COORDINATION ---// Account B sends the cosignature back to Account AfinalCosignaturesharedCosignature=cosignatureB;System.out.println("[Account B] <== Cosignature sent back "+"to Account A (offchain)");
アカウント B はペイロードを受け取り、 を使用してデシリアライズし、トランザクションオブジェクトを再構築します。アカウント B は、埋め込みトランザクションが自身が署名しようとしている内容と一致しているか検証する必要があります。
その後、連署します。これによりトランザクションハッシュが計算され、連署オブジェクトが生成されます。この連署のみがアカウント A に送り返されます。
# --- ACCOUNT A (Initiator) ---# Add cosignature to the transaction and rebuild payloadtransaction.cosignatures.append(shared_cosignature)transaction_payload=facade.transaction_factory.to_json(transaction)json_payload=transaction_payloadprint('[Account A] Ready to announce')
// --- ACCOUNT A (Initiator) ---// Add cosignature to the transaction and rebuild payloadtransaction.cosignatures.push(sharedCosignature);consttransactionPayloadFinal=facade.transactionFactory.static.toJson(transaction);constjsonPayload=transactionPayloadFinal;console.log('[Account A] Ready to announce');
// --- ACCOUNT A (Initiator) ---// Add cosignature to the transaction and rebuild payloadtransaction.getCosignatures().add(sharedCosignature);finalStringtransactionPayloadFinal=SymbolTransactionFactory.toJson(transaction);finalStringjsonPayload=transactionPayloadFinal;System.out.println("[Account A] Ready to announce");
アカウント A はアカウント B の連署を受け取り、トランザクションオブジェクトの cosignatures 配列に追加し、アナウンス用のペイロードを再構築します。
// Announce the transactionconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Transaction hash:',transactionHash);awaitannounceTransaction(jsonPayload,'transaction');