Κάθε συναλλαγή Solana περιλαμβάνει ένα πρόσφατο blockhash—μια αναφορά σε μια πρόσφατη κατάσταση δικτύου που αποδεικνύει ότι η συναλλαγή δημιουργήθηκε "τώρα". Το δίκτυο απορρίπτει οποιαδήποτε συναλλαγή με blockhash παλαιότερο από ~150 blocks (~60-90 δευτερόλεπτα), αποτρέποντας επιθέσεις επανάληψης και ξεπερασμένες υποβολές. Αυτό λειτουργεί τέλεια για πληρωμές σε πραγματικό χρόνο. Αλλά διακόπτει ροές εργασίας που χρειάζονται χρονικό διάστημα μεταξύ υπογραφής και υποβολής, όπως:
| Σενάριο | Γιατί αποτυγχάνουν οι τυπικές συναλλαγές |
|---|---|
| Λειτουργίες ταμείου | Ο CFO στο Τόκιο υπογράφει, ο Controller στη Νέα Υόρκη εγκρίνει—90 δευτερόλεπτα δεν αρκούν |
| Ροές εργασίας συμμόρφωσης | Οι συναλλαγές χρειάζονται νομική/κανονιστική επανεξέταση πριν την εκτέλεση |
| Υπογραφή cold storage | Τα air-gapped μηχανήματα απαιτούν χειροκίνητη μεταφορά υπογεγραμμένων συναλλαγών |
| Προετοιμασία παρτίδας | Προετοιμάστε μισθοδοσία ή εκταμιεύσεις κατά τις εργάσιμες ώρες, εκτελέστε τη νύχτα |
| Συντονισμός multi-sig | Πολλαπλοί εγκρίνοντες σε διαφορετικές ζώνες ώρας |
| Προγραμματισμένες πληρωμές | Προγραμματίστε πληρωμές για εκτέλεση σε μελλοντική ημερομηνία |
Στα παραδοσιακά χρηματοοικονομικά, μια υπογεγραμμένη επιταγή δεν λήγει σε 90 δευτερόλεπτα. Ορισμένες λειτουργίες blockchain δεν θα έπρεπε επίσης. Τα durable nonces λύνουν αυτό αντικαθιστώντας το πρόσφατο blockhash με μια αποθηκευμένη, μόνιμη τιμή που προχωρά μόνο όταν τη χρησιμοποιείτε—δίνοντάς σας συναλλαγές που παραμένουν έγκυρες μέχρι να είστε έτοιμοι να τις υποβάλετε.
Πώς λειτουργεί
Αντί για ένα πρόσφατο blockhash (έγκυρο ~150 blocks), χρησιμοποιείτε έναν λογαριασμό nonce, έναν ειδικό λογαριασμό που αποθηκεύει μια μοναδική τιμή η οποία μπορεί να χρησιμοποιηθεί στη θέση ενός blockhash. Κάθε συναλλαγή που χρησιμοποιεί αυτό το nonce πρέπει να το "προωθήσει" ως πρώτη εντολή. Κάθε τιμή nonce μπορεί να χρησιμοποιηθεί μόνο για μία συναλλαγή.
Ο λογαριασμός nonce κοστίζει ~0.0015 SOL για απαλλαγή από rent. Ένας λογαριασμός nonce = μία εκκρεμής συναλλαγή τη φορά. Για παράλληλες ροές εργασίας, δημιουργήστε πολλαπλούς λογαριασμούς nonce.
Δημιουργία λογαριασμού nonce
Η δημιουργία ενός λογαριασμού nonce απαιτεί δύο εντολές σε μία μόνο συναλλαγή:
- Δημιουργήστε τον λογαριασμό χρησιμοποιώντας
getCreateAccountInstructionαπό το System Program - Αρχικοποιήστε τον ως nonce χρησιμοποιώντας
getInitializeNonceAccountInstruction
Δημιουργία keypair
Δημιουργήστε ένα νέο keypair για χρήση ως διεύθυνση λογαριασμού nonce και υπολογίστε τον απαιτούμενο χώρο και το rent.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();
Εντολή δημιουργίας λογαριασμού
Δημιουργήστε τον λογαριασμό που ανήκει στο System Program με αρκετά lamports για απαλλαγή από rent.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});
Εντολή αρχικοποίησης nonce
Αρχικοποιήστε τον λογαριασμό ως λογαριασμό nonce, ορίζοντας την εξουσία που μπορεί να τον προωθήσει.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});const initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});
Δημιουργία συναλλαγής
Δημιουργήστε μια συναλλαγή με και τις δύο εντολές.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});const initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});const { value: blockhash } = await rpc.getLatestBlockhash().send();const createNonceTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),(tx) =>appendTransactionMessageInstructions([createNonceAccountIx, initNonceIx],tx));
Υπογραφή και αποστολή
Υπογράψτε και στείλτε τη συναλλαγή για να δημιουργήσετε και να αρχικοποιήσετε τον λογαριασμό nonce.
Δημιουργία αναβαλλόμενης συναλλαγής
Αντί για ένα πρόσφατο blockhash, χρησιμοποιήστε το blockhash του λογαριασμού
nonce ως διάρκεια ζωής της συναλλαγής.
Ανάκτηση του nonce
Ανακτήστε τα δεδομένα από τον λογαριασμό nonce. Χρησιμοποιήστε το blockhash
από τον λογαριασμό nonce ως διάρκεια ζωής της συναλλαγής.
{version: 1,state: 1,authority: 'HgjaL8artMtmntaQDVM2UBk3gppsYYERS4PkUhiaLZD1',blockhash: '5U7seXqfgZx1uh5DFhdH1vyBhr7XGRrKxBAnJJTbbUa',lamportsPerSignature: 5000n}
Δημιουργία εντολής μεταφοράς
Δημιουργήστε την εντολή για την πληρωμή σας. Αυτό το παράδειγμα δείχνει μια μεταφορά token.
Δημιουργία συναλλαγής με durable nonce
Χρησιμοποιήστε το setTransactionMessageLifetimeUsingDurableNonce που ορίζει το
nonce ως blockhash και αυτόματα προσθέτει την εντολή προώθησης nonce στην αρχή.
Υπογραφή συναλλαγής
Υπογράψτε τη συναλλαγή. Τώρα χρησιμοποιεί το durable nonce αντί για ένα τυπικό blockhash.
Αποθήκευση ή αποστολή συναλλαγής
Μετά την υπογραφή, κωδικοποιήστε τη συναλλαγή για αποθήκευση. Όταν είστε έτοιμοι, στείλτε την στο δίκτυο.
Κωδικοποίηση για αποθήκευση
Κωδικοποιήστε την υπογεγραμμένη συναλλαγή σε base64. Αποθηκεύστε αυτή την τιμή στη βάση δεδομένων σας.
Αποστολή συναλλαγής
Στείλτε την υπογεγραμμένη συναλλαγή όταν είστε έτοιμοι. Η συναλλαγή παραμένει έγκυρη μέχρι να προχωρήσει το nonce.
Επίδειξη
// Generate keypairs for sender and recipientconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();console.log("Sender Address:", sender.address);console.log("Recipient Address:", recipient.address);// Demo Setup: Create RPC connection, mint, and token accountsconst { rpc, rpcSubscriptions, mint } = await demoSetup(sender, recipient);// =============================================================================// Step 1: Create a Nonce Account// =============================================================================const nonceKeypair = await generateKeyPairSigner();console.log("\nNonce Account Address:", nonceKeypair.address);const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();// Instruction to create new account for the nonceconst createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});// Instruction to initialize the nonce accountconst initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});// Build and send nonce account creation transactionconst { value: blockhash } = await rpc.getLatestBlockhash().send();const createNonceTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),(tx) =>appendTransactionMessageInstructions([createNonceAccountIx, initNonceIx],tx));const signedCreateNonceTx =await signTransactionMessageWithSigners(createNonceTx);assertIsTransactionWithBlockhashLifetime(signedCreateNonceTx);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedCreateNonceTx,{ commitment: "confirmed" });console.log("Nonce Account created.");// =============================================================================// Step 2: Token Payment with Durable Nonce// =============================================================================// Fetch current nonce value from the nonce accountconst { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);console.log("Nonce Account data:", nonceData);const [senderAta] = await findAssociatedTokenPda({mint: mint.address,owner: sender.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [recipientAta] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});console.log("\nMint Address:", mint.address);console.log("Sender Token Account:", senderAta);console.log("Recipient Token Account:", recipientAta);const transferInstruction = getTransferInstruction({source: senderAta,destination: recipientAta,authority: sender.address,amount: 250_000n // 0.25 tokens});// Create transaction message using durable nonce lifetime// setTransactionMessageLifetimeUsingDurableNonce automatically prepends// the AdvanceNonceAccount instructionconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) =>setTransactionMessageLifetimeUsingDurableNonce({nonce: nonceData.blockhash as string as Nonce,nonceAccountAddress: nonceKeypair.address,nonceAuthorityAddress: nonceData.authority},tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);assertIsTransactionWithDurableNonceLifetime(signedTransaction);const transactionSignature = getSignatureFromTransaction(signedTransaction);// Encode the transaction to base64, optionally save and send at a later timeconst base64EncodedTransaction =getBase64EncodedWireTransaction(signedTransaction);console.log("\nBase64 Encoded Transaction:", base64EncodedTransaction);// Send the encoded transaction, blockhash does not expireawait rpc.sendTransaction(base64EncodedTransaction, {encoding: "base64",skipPreflight: true}).send();console.log("\n=== Token Payment with Durable Nonce Complete ===");console.log("Transaction Signature:", transactionSignature);// =============================================================================// Demo Setup Helper Function// =============================================================================
Ακύρωση εκκρεμούς συναλλαγής
Κάθε λογαριασμός nonce blockhash μπορεί να χρησιμοποιηθεί μόνο μία φορά. Για
να ακυρώσετε μια εκκρεμή συναλλαγή ή να προετοιμάσετε τον λογαριασμό nonce για
επαναχρησιμοποίηση, προχωρήστε τον χειροκίνητα:
import { getAdvanceNonceAccountInstruction } from "@solana-program/system";// Submit this instruction (with a regular blockhash) to invalidate any pending transactiongetAdvanceNonceAccountInstruction({nonceAccount: nonceAddress,nonceAuthority});
Αυτό δημιουργεί μια νέα τιμή nonce, καθιστώντας οποιαδήποτε συναλλαγή υπογεγραμμένη με την παλιά τιμή μόνιμα άκυρη.
Ροή εργασίας έγκρισης πολλαπλών μερών
Αποσειριοποιήστε τη συναλλαγή για να προσθέσετε πρόσθετες υπογραφές, στη συνέχεια σειριοποιήστε ξανά για αποθήκευση ή υποβολή:
import {getBase64Decoder,getTransactionDecoder,getBase64EncodedWireTransaction,partiallySignTransaction} from "@solana/kit";// Deserialize the stored transactionconst txBytes = getBase64Decoder().decode(serializedString);const partiallySignedTx = getTransactionDecoder().decode(txBytes);// Each approver adds their signatureconst fullySignedTx = await partiallySignTransaction([newSigner],partiallySignedTx);// Serialize again for storage or submissionconst serialized = getBase64EncodedWireTransaction(fullySignedTx);
Η συναλλαγή μπορεί να σειριοποιηθεί, να αποθηκευτεί και να μεταβιβαστεί μεταξύ των εγκριτών. Μόλις συλλεχθούν όλες οι απαιτούμενες υπογραφές, υποβάλετε στο δίκτυο.
Ζητήματα παραγωγής
Διαχείριση λογαριασμών nonce:
- Δημιουργήστε μια δεξαμενή λογαριασμών nonce για παράλληλη προετοιμασία συναλλαγών
- Παρακολουθήστε ποια nonces είναι "σε χρήση" (έχουν εκκρεμείς υπογεγραμμένες συναλλαγές)
- Υλοποιήστε ανακύκλωση nonce μετά την υποβολή ή την εγκατάλειψη των συναλλαγών
Ασφάλεια:
- Η εξουσία nonce ελέγχει εάν οι συναλλαγές μπορούν να ακυρωθούν. Εξετάστε το ενδεχόμενο διαχωρισμού της εξουσίας nonce από τους υπογράφοντες συναλλαγών για πρόσθετο έλεγχο και διαχωρισμό καθηκόντων
- Οποιοσδήποτε με τα σειριοποιημένα bytes συναλλαγής μπορεί να την υποβάλει στο δίκτυο
Σχετικοί πόροι
Is this page helpful?