8.Transazioni Lock

La blockchain Symbol offre due tipi di LockTransacion:

  • Hash Lock Transaction
  • Secret Lock Transaction

8.1 Transazione Hash Lock

Le transazioni Hash Lock hanno la caratteristica di poter essere propagate in ritardo (annunciate in differita). Una transazione di questo tipo viene memorizzata nell'area temporanea e limitata (cache) di ogni nodo della rete, usando come riferimento l'impronta hash. Questo dato rimarrà nella cache del nodo fino al momento della richiesta di propagazione vera e propria. La transazione rimane bloccata nella memoria del nodo della rete (locked) e verrà elaborata solo previa esecuzione della firma di tutti i cofirmatari. Le monete dell'Indirizzo non sono vincolate/bloccate, eccezzion fatta per l'importo di 10 XYM che va pagato dal proponente esecutore della Transazione. Tali fondi vincolati verranno restituiti all'Indirizzo del proponente esecutore, al completamento delle operazioni di firma. L'intervallo di validità di una transazione Hash Lock, è di circa 48 ore. Se alla scadenza del periodo di validità la transazione no verrà firmata e confermata, la cauzione di 10 XYM andrà perduta.

Creazione di una transazione Aggregate Bonded

bob = sym.Account.generateNewAccount(networkType);

tx1 = sym.TransferTransaction.create(
    undefined,
    bob.address,  //Il destinatario è Bob
    [ //invio di 1 XYM
      new sym.Mosaic(
        new sym.NamespaceId("symbol.xym"),
        sym.UInt64.fromUint(1000000)
      )
    ],
    sym.EmptyMessage, //senza messaggio 
    networkType
);

tx2 = sym.TransferTransaction.create(
    undefined,
    alice.address,  //Il destinatario è Alice
    [],
    sym.PlainMessage.create('Grazie!'), //Messaggio
    networkType
);

aggregateArray = [
    tx1.toAggregate(alice.publicAccount), //Il mittente è Alice
    tx2.toAggregate(bob.publicAccount), //Il mittente è  Bob
]

//Transazione di gruppo legata: Aggregate Bonded 
aggregateTx = sym.AggregateTransaction.createBonded(
    sym.Deadline.create(epochAdjustment),
    aggregateArray,
    networkType,
    [],
).setMaxFeeForAggregate(100, 1);

//Operazione di firma
signedAggregateTx = alice.sign(aggregateTx, generationHash);

Specificare la chiave pubblica dell'Indirizzo del mittente negli elementi del parametro AggregateArray, per le due transazioni tx1 e tx2 inserite nella transazione di gruppo. Per recuperare la chiave pubblica di un Indirizzo confrontare il capitolo sugli Indirizzi. Le transazioni di gruppo subiscono la verifica di integrità, nell'ordine che corrispone alla posizione in cui sono state inserite nell'array parametro, tale verifica verrà eseguita durante la creazione del prossimo blocco.

Per esempio, si potrebbe inviare un NFT (Non Fungible Token) da Alice a Bob definendo una transazione tx1 e contestualmente trasferirlo da Bob a Carol definendo una transazione tx2 nell'ordine. Invertendo l'ordine nel parametro array della transazione di gruppo specificando tx2, seguito da tx1 si otterrà invece un errore. Inoltre la presenza di almeno una transazione inconsistente nella transazione di gruppo, solleverà un errore invalidando l'approvazione di tutte le transazioni del gruppo.

Creazione, firma e propagazione di una transazione Hash

//Creazione di transazione Hash Lock 
hashLockTx = sym.HashLockTransaction.create(
  sym.Deadline.create(epochAdjustment),
    new sym.Mosaic(new sym.NamespaceId("symbol.xym"),sym.UInt64.fromUint(10 * 1000000)), //cauzione predefinita di 10xym 
    sym.UInt64.fromUint(480), // Tempo di scadenza della transazione
    signedAggregateTx,// Prenotata con il riferimento all'impronta hash
    networkType
).setMaxFee(100);

//Firma
signedLockTx = alice.sign(hashLockTx, generationHash);

//Propagazione della transazione Hash Lock 
await txRepo.announce(signedLockTx).toPromise();

Propagazione di una transazione Aggregate Bonded

Chiamata di propagazione della transazione di gruppo legata. (Per es. dopo aver controllato con l'Explorer dei blocchi)

await txRepo.announceAggregateBonded(signedAggregateTx).toPromise();

Cofirmatari

Firme dei cointestatari della transazione. Nel nostro caso Bob.

txInfo = await txRepo.getTransaction(signedAggregateTx.hash,sym.TransactionGroup.Partial).toPromise();
cosignatureTx = sym.CosignatureTransaction.create(txInfo);
signedCosTx = bob.signCosignatureTransaction(cosignatureTx);
await txRepo.announceAggregateBondedCosignature(signedCosTx).toPromise();

Note

Le transazioni Hash Lock possono essere create e propagate da chiunque, non sono limitate all'Indirizzo che le ha create e firmate. Tuttavia, assicurarsi che questo tipo di transazioni includa almeno una transazione in cui il firmatario coincida. Sono accettate anche transazioni nelle quali non viene specificato alcun Mosaic o con messaggio vuoto.

8.2 Transazioni Secret Lock e Secret Proof

La transazione Secret Lock registra una password condivisa in precedenza e blocca/vincola il Mosaic specificato. Ciò permette al destinatario di ricevere il Mosaic dando prova di conoscenza della password prima della scadenza della transazione.

Questa sezione descrive un vincolo del valore di 1 XYM fissato da Alice e sbloccato da Bob con il fine di ricevere i fondi.

Creiamo gli Indirizzi di Bob e di Alice. Bob deve propagare la transazione per avviare lo sblocco, quindi richiediamo 10 XYM dal faucet.

bob = sym.Account.generateNewAccount(networkType);
console.log(bob.address);

//FAUCET URL 
console.log("https://testnet.symbol.tools/?recipient=" + bob.address.plain() +"&amount=10");

Creazione del Secret Lock

Creazione di una parola chiave condivisa per il blocco e sblocco della transazione.

sha3_256 = require('/node_modules/js-sha3').sha3_256;

random = sym.Crypto.randomBytes(20);
hash = sha3_256.create();
secret = hash.update(random).hex();//hash per bloccare la transazione (lock)
proof = random.toString('hex'); //chiave segreta di sblocco (unlock)
console.log("secret:" + secret);
console.log("proof:" + proof);
Output esemplificativo
> secret:f260bfb53478f163ee61ee3e5fb7cfcaf7f0b663bc9dd4c537b958d4ce00e240
  proof:7944496ac0f572173c2549baf9ac18f893aab6d0

Creazione, firma e propagazione della transazione:

lockTx = sym.SecretLockTransaction.create(
    sym.Deadline.create(epochAdjustment),
    new sym.Mosaic(
      new sym.NamespaceId("symbol.xym"),
      sym.UInt64.fromUint(1000000) //1XYM
    ), //Mosaic da vincolare
    sym.UInt64.fromUint(480), //Durata del vincolo (numero di blocchi)
    sym.LockHashAlgorithm.Op_Sha3_256, //Algorithm used for lock keyword generation
    secret, //Password di blocco
    bob.address, //Indirizzo di destinazione allo sblocco:Bob
    networkType
).setMaxFee(100);

signedLockTx = alice.sign(lockTx,generationHash);
await txRepo.announce(signedLockTx).toPromise();

Descrizione dell'algoritmo LockHashAlgorithm

{0: 'Op_Sha3_256', 1: 'Op_Hash_160', 2: 'Op_Hash_256'}

L'Indirizzo destinatario, viene specificato quando si crea la transazione di lock, nel nostro caso Bob. quindi non può essere modificato a posteriori, nemmeno dalla transazione di sblocco.

La durata massima di un lock è fissata in 365 giorni (misurata in numero di blocchi).

Effettuare la convalida delle transazioni.

slRepo = repo.createSecretLockRepository();
res = await slRepo.search({secret:secret}).toPromise();
console.log(res.data[0]);
Output esemplificativo
> SecretLockInfo
    amount: UInt64 {lower: 1000000, higher: 0}
    compositeHash: "770F65CB0CC0CA17370DE961B2AA5B48B8D86D6DB422171AB00DF34D19DEE2F1"
    endHeight: UInt64 {lower: 323495, higher: 0}
    hashAlgorithm: 0
    mosaicId: MosaicId {id: Id}
    ownerAddress: Address {address: 'TBXUTAX6O6EUVPB6X7OBNX6UUXBMPPAFX7KE5TQ', networkType: 152}
    recipientAddress: Address {address: 'TBTWKXCNROT65CJHEBPL7F6DRHX7UKSUPD7EUGA', networkType: 152}
    recordId: "6260A1D3205E94BEA3D9E3E9"
    secret: "F260BFB53478F163EE61EE3E5FB7CFCAF7F0B663BC9DD4C537B958D4CE00E240"
    status: 0
    version: 1

Ciò mostra che Alice, esecutore del lock, risulta l'Indirizzo proprietario del lock, mentre Bob risulta l'Indirizzo destinatario dei fondi. Alla pubblicazione del secret Bob potrà informare la rete dando la prova per lo sblocco.

Transazione di sblocco (unlock)

Per sbloccare la transazione usando la parola chiave di sblocco (secret proof), che Bob ha preventivamente ricevuto.

proofTx = sym.SecretProofTransaction.create(
    sym.Deadline.create(epochAdjustment),
    sym.LockHashAlgorithm.Op_Sha3_256, //Algoritmo di hash per verificare la chiave di sblocco
    secret, //Chiave di sblocco
    bob.address, //Deactivated accounts (receiving accounts)
    proof, //Unlock keyword
    networkType
).setMaxFee(100);

signedProofTx = bob.sign(proofTx,generationHash);
await txRepo.announce(signedProofTx).toPromise();

Ottenere la convalida:

txInfo = await txRepo.getTransaction(signedProofTx.hash,sym.TransactionGroup.Confirmed).toPromise();
console.log(txInfo);
Output esemplificativo
> SecretProofTransaction
  > deadline: Deadline {adjustedValue: 12669305546}
    hashAlgorithm: 0
    maxFee: UInt64 {lower: 20700, higher: 0}
    networkType: 152
    payloadSize: 207
    proof: "A6431E74005585779AD5343E2AC5E9DC4FB1C69E"
    recipientAddress: Address {address: 'TBTWKXCNROT65CJHEBPL7F6DRHX7UKSUPD7EUGA', networkType: 152}
    secret: "4C116F32D986371D6BCC44CE64C970B6567686E79850E4A4112AF869580B7C3C"
    signature: "951F440860E8F24F6F3AB8EC670A3D448B12D75AB954012D9DB70030E31DA00B965003D88B7B94381761234D5A66BE989B5A8009BB234716CA3E5847C33F7005"
    signer: PublicAccount {publicKey: '9DC9AE081DF2E76554084DFBCCF2BC992042AA81E8893F26F8504FCED3692CFB', address: Address}
  > transactionInfo: TransactionInfo
        hash: "85044FF702A6966AB13D05DBE4AC4C3A13520C7381F32540429987C207B2056B"
        height: UInt64 {lower: 323805, higher: 0}
        id: "6260CC7F60EE2B0EA10CCEDA"
        merkleComponentHash: "85044FF702A6966AB13D05DBE4AC4C3A13520C7381F32540429987C207B2056B"
    type: 16978

La transazione per lo sblocco SecretProofTransaction non contiene informazioni sulla quantità di monete Mosaic trasferite. Per controllare tale valore è necessario leggerlo dalle informazioni della ricevuta di creazione dell'ultimo blocco della blockchain, cercando la ricevuta con destinatario Bob e di tipo:LockHash_Completed.

receiptRepo = repo.createReceiptRepository();

receiptInfo = await receiptRepo.searchReceipts({
    receiptType:sym.ReceiptTypeLockHash_Completed,
    targetAddress:bob.address
}).toPromise();
console.log(receiptInfo.data);
Output esemplificativo
> data: Array(1)
  >  0: TransactionStatement
        height: UInt64 {lower: 323805, higher: 0}
     >  receipts: Array(1)
          > 0: BalanceChangeReceipt
                amount: UInt64 {lower: 1000000, higher: 0}
            > mosaicId: MosaicId
                  id: Id {lower: 760461000, higher: 981735131}
              targetAddress: Address {address: 'TBTWKXCNROT65CJHEBPL7F6DRHX7UKSUPD7EUGA', networkType: 152}
              type: 8786

I tipi delle ricevute sono definiti come segue:

{4685: 'Mosaic_Rental_Fee', 4942: 'Namespace_Rental_Fee', 8515: 'Harvest_Fee', 8776: 'LockHash_Completed', 8786: 'LockSecret_Completed', 9032: 'LockHash_Expired', 9042: 'LockSecret_Expired', 12616: 'LockHash_Created', 12626: 'LockSecret_Created', 16717: 'Mosaic_Expired', 16718: 'Namespace_Expired', 16974: 'Namespace_Deleted', 20803: 'Inflation', 57667: 'Transaction_Group', 61763: 'Address_Alias_Resolution', 62019: 'Mosaic_Alias_Resolution'}

8786: 'LockSecret_Completed' : LockSecret is completed
9042: 'LockSecret_Expired' :LockSecret is expired

8.3 Consigli pratici

Sul pagamento di commissioni delle transazioni

Di solito le blockchain applicano delle commissioni alle esecuzioni di transazioni di trasferimenti. Gli utenti della blockchain devono ottenere dal sito di scambio, le rispettive monete per pagare le commissioni (per es. la moneta di Symbol ha nome XYM). Se l'utente è una società, la gestione delle monete potrebbe essere problematica. Per questo motivo il pagamento di commissioni di Transazioni di gruppo (aggregate) può essere delegato a fornitori esterni di monete mediante l'utilizzo di transazioni Hash Lock.

Schedulare transazioni

Le transazioni Secret Lock rimborsano l'Indirizzo che le ha create quando la blockchain è cresciuta di un certo numero di blocchi. Quando un fornitore di servizi esterno, addebita il costo del lock all'Indirizzo, e la quantità di monete possedute dall'utente aumenterà dopo la scadenza. Viceversa, propagare una transazione di tipo secret proof prima della scadenza viene considerata una richiesta di cancellazione, e i fondi vengono restituiti al fornitore di servizi.

Operazione di scambio atomica (Atomic swap)

I Secret lock si possono usare per scambio di Mosaic tra blockchain eterogenee. Altre implementazioni di blockchain danno nome a questa operazione Hash time lock contract (HTLC) che non va confuda con l'implementazione Symbol della transazione Hash Lock.