पृष्ठभूमि और उपयोग का परिदृश्य
Solana में transaction भेजते समय, slot का आगे बढ़ना, leader की स्थिति और नेटवर्क पाथ की भीड़ लगातार यह बदलते रहते हैं कि कौन-सा रास्ता leader तक सबसे तेज़ पहुँचेगा। यह किसी एक RPC प्रदाता या भेजने वाली सेवा तक सीमित नहीं है; यह Solana के execution मॉडल की एक संरचनात्मक विशेषता है।
ERPC का SWQoS endpoint भेजने का एक ऐसा रास्ता देता है जो transactions को उस priority lane में डालता है जिसे leaders, Stake-weighted Quality of Service (SWQoS) के आधार पर आवंटित करते हैं। यह priority bandwidth non-priority lane की लगभग 5 गुना है और priority fee के मूल्यांकन से पहले लागू होती है।
इसी कारण, transaction भेजने के लिए SWQoS endpoint एक महत्वपूर्ण विकल्प है, लेकिन production में कोई एक endpoint हमेशा सबसे तेज़ नहीं होता। एक ही slot के भीतर भी, पाथ में अस्थायी अंतर या लोड का असंतुलन किसी दूसरे तेज़ endpoint को आगे निकलने दे सकता है।
इन विशेषताओं को देखते हुए, एक ही रास्ते पर निर्भर रहने के बजाय, एक ही transaction को कई तेज़ रास्तों पर समानांतर रूप से भेजना और सबसे पहले process होने वाले को स्वीकार करना एक प्रभावी operational पैटर्न हो सकता है।
लेकिन जब एक ही transaction कई endpoints पर भेजा जाता है, तो अतिरिक्त नियंत्रण के बिना आप यह गारंटी नहीं दे सकते कि वह केवल एक बार execute होगा। इस नियंत्रण के बिना किया गया fan-out अनचाहे double execution या बिगड़े हुए retry नियंत्रण का कारण बन सकता है।
Solana इसके लिए Durable Nonce नाम का तंत्र देता है। Durable Nonce के साथ आप एक ही signed transaction को कई रास्तों से भेज सकते हैं, जबकि on-chain execution एक ही बार तक सीमित रहता है।
यह पेज बताता है कि Durable Nonce के साथ transaction भेजने को आधार मानकर, ERPC के SWQoS endpoint को दूसरे तेज़ RPC endpoints के साथ जोड़ने वाले fan-out operations कैसे लागू करें।
दायरा और पूर्व-आवश्यकताएँ
यह गाइड web3.js के साथ Durable Nonce account बनाने और उसे transaction भेजने तथा fan-out operations में इस्तेमाल करने को कवर करती है।
समझने के लिए पूर्व-आवश्यकताएँ:
- Durable Nonce transactions के लिए nonce मान को
recentBlockhash के रूप में इस्तेमाल करें और nonceAdvance को पहले instruction के रूप में रखें
nonceAdvance के execute होते ही, बाद के instructions विफल होने पर भी nonce खर्च हो सकता है। आप उसी rawTx को जैसा का तैसा दोबारा नहीं भेज सकते।
- Nonce account बनाना एक बार का सेटअप है और आम तौर पर उसे दोबारा इस्तेमाल किया जाता है
चरण 1: nonce authority और connection तैयार करें
Nonce authority एक Keypair है जो nonce को आगे बढ़ा सकता है।
import {
Connection,
Keypair,
SystemProgram,
NONCE_ACCOUNT_LENGTH,
} from '@solana/web3.js'
const connection = new Connection('https://<primary-rpc-endpoint>', 'confirmed')
const nonceAuthority = Keypair.fromSecretKey(/* secret key */)
import {
Connection,
Keypair,
SystemProgram,
NONCE_ACCOUNT_LENGTH,
} from '@solana/web3.js'
const connection = new Connection('https://<primary-rpc-endpoint>', 'confirmed')
const nonceAuthority = Keypair.fromSecretKey(/* secret key */)
- Nonce authority
nonceAdvance execute कर सकता है
- यह fee payer भी हो सकता है, या कोई अलग keypair
चरण 2: nonce account का Keypair बनाएँ
Nonce account एक System Account होता है।
const nonceAccount = Keypair.generate()
const nonceAccount = Keypair.generate()
यह Keypair इनके लिए इस्तेमाल होता है:
- nonce मान रखना (
recentBlockhash की जगह)
- केवल बनाते समय sign करना
- उसके बाद सुरक्षित रखना; रोज़ाना भेजने के लिए इसकी ज़रूरत नहीं होती
चरण 3: rent-exempt न्यूनतम राशि की गणना करें
Nonce accounts का rent-exempt होना ज़रूरी है। मानों को hardcode न करें; उन्हें RPC से प्राप्त करें।
const lamports =
await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH)
const lamports =
await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH)
चरण 4: nonce account बनाएँ और initialize करें
Nonce account को एक ही transaction में createAccount + nonceInitialize के साथ बनाएँ।
import { Transaction } from '@solana/web3.js'
const tx = new Transaction()
tx.add(
SystemProgram.createAccount({
fromPubkey: nonceAuthority.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
)
// fee payer is the nonce authority
tx.feePayer = nonceAuthority.publicKey
// use a normal blockhash for initialization
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('confirmed')
tx.recentBlockhash = blockhash
// sign with both the new account and the authority
tx.sign(nonceAccount, nonceAuthority)
const signature = await connection.sendRawTransaction(tx.serialize())
await connection.confirmTransaction(
{ signature, blockhash, lastValidBlockHeight },
'confirmed',
)
import { Transaction } from '@solana/web3.js'
const tx = new Transaction()
tx.add(
SystemProgram.createAccount({
fromPubkey: nonceAuthority.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
)
// fee payer is the nonce authority
tx.feePayer = nonceAuthority.publicKey
// use a normal blockhash for initialization
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('confirmed')
tx.recentBlockhash = blockhash
// sign with both the new account and the authority
tx.sign(nonceAccount, nonceAuthority)
const signature = await connection.sendRawTransaction(tx.serialize())
await connection.confirmTransaction(
{ signature, blockhash, lastValidBlockHeight },
'confirmed',
)
आगे भेजे जाने वाले transactions के लिए इसी nonceAccount.publicKey का इस्तेमाल करें।
चरण 5: हर बार भेजने से पहले nonce प्राप्त करें
हर transaction के लिए मौजूदा nonce मान प्राप्त करें।
import { NonceAccount } from '@solana/web3.js'
const { value, context } = await connection.getAccountInfoAndContext(
nonceAccount.publicKey,
'confirmed',
)
if (!value) {
throw new Error('Nonce account not found')
}
const nonce = NonceAccount.fromAccountData(value.data).nonce
const minContextSlot = context.slot
import { NonceAccount } from '@solana/web3.js'
const { value, context } = await connection.getAccountInfoAndContext(
nonceAccount.publicKey,
'confirmed',
)
if (!value) {
throw new Error('Nonce account not found')
}
const nonce = NonceAccount.fromAccountData(value.data).nonce
const minContextSlot = context.slot
nonce को recentBlockhash के रूप में इस्तेमाल करें
context.slot का इस्तेमाल confirmation में होता है
चरण 6: Durable Nonce transaction बनाएँ
Durable Nonce transactions को ये शर्तें पूरी करनी होंगी:
recentBlockhash = nonce
- पहला instruction
nonceAdvance है
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const instructions = [
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
// add your real instructions after this
]
const message = new TransactionMessage({
payerKey: nonceAuthority.publicKey,
recentBlockhash: nonce,
instructions,
}).compileToV0Message()
const tx = new VersionedTransaction(message)
tx.sign([nonceAuthority /* + other signers */])
const rawTx = tx.serialize()
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const instructions = [
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
// add your real instructions after this
]
const message = new TransactionMessage({
payerKey: nonceAuthority.publicKey,
recentBlockhash: nonce,
instructions,
}).compileToV0Message()
const tx = new VersionedTransaction(message)
tx.sign([nonceAuthority /* + other signers */])
const rawTx = tx.serialize()
नोट्स:
- अगर आप ComputeBudget instructions इस्तेमाल करते हैं, तो वे
nonceAdvance के बाद आने चाहिए
- अगर
nonceAdvance पहले नहीं है, तो transaction अस्वीकार कर दिया जाएगा
चरण 7: कई RPCs पर fan-out करें
उसी rawTx को एक साथ भेजें।
const endpoints = [
'https://swqos-fra2.erpc.global?api-key=YOUR_API_KEY',
'https://swqos-ams1.erpc.global?api-key=YOUR_API_KEY',
'https://<backup-rpc-1>',
]
const results = await Promise.allSettled(
endpoints.map((url) =>
new Connection(url, 'confirmed').sendRawTransaction(rawTx, {
skipPreflight: true,
minContextSlot,
}),
),
)
const success = results.find(
(r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled',
)
if (!success) {
throw new Error('All sends failed')
}
const signature = success.value
const endpoints = [
'https://swqos-fra2.erpc.global?api-key=YOUR_API_KEY',
'https://swqos-ams1.erpc.global?api-key=YOUR_API_KEY',
'https://<backup-rpc-1>',
]
const results = await Promise.allSettled(
endpoints.map((url) =>
new Connection(url, 'confirmed').sendRawTransaction(rawTx, {
skipPreflight: true,
minContextSlot,
}),
),
)
const success = results.find(
(r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled',
)
if (!success) {
throw new Error('All sends failed')
}
const signature = success.value
- सभी endpoints पर signature एक ही रहता है
- सफलतापूर्वक भेज देना confirmation नहीं है
चरण 8: Durable Nonce के साथ confirm करें
Durable Nonce में, confirmation के समय nonce की जानकारी शामिल करें।
await connection.confirmTransaction(
{
signature,
nonceAccountPubkey: nonceAccount.publicKey,
nonceValue: nonce,
minContextSlot,
},
'confirmed',
)
await connection.confirmTransaction(
{
signature,
nonceAccountPubkey: nonceAccount.publicKey,
nonceValue: nonce,
minContextSlot,
},
'confirmed',
)
अगर confirmation सफल होता है:
- nonce आगे बढ़ जाता है
- दूसरे RPCs पर भेजी गई प्रतियाँ
InvalidNonce लौटाती हैं
अगली बार भेजना
- confirmation के बाद नया nonce प्राप्त करें
- उसी rawTx या nonce मान का दोबारा इस्तेमाल न करें
- समानांतर workflows के लिए अलग-अलग nonce accounts इस्तेमाल करें
अगले transaction के लिए अपडेट किया गया nonce प्राप्त करें और उन्हीं चरणों से transaction बनाएँ।