Durable Nonce Fan-out untuk SWQoS

Skenario latar belakang dan penggunaan

In Solana transaksi yang dikirim, slot progresi, penempatan pemimpin, dan jalur jaringan kemacetan terus-menerus mengubah rute mana yang mencapai pemimpin tercepat. Ini tidak spesifik bagi apapun RPC penyedia atau layanan pengiriman; ini adalah karakteristik struktural SolanaModel eksekusi.
ERPC's SWQoS titik akhir memberikan jalur pengiriman bahwa menyuntikkan transaksi ke jalur prioritas yang dialokasikan oleh para pemimpin berdasarkan Kualitas Layanan Stake- berbobot (SWQoS). Prioritas bandwidth ini adalah sekitar 5 × dari jalur bukan prioritas dan diterapkan sebelum evaluasi biaya prioritas.
Untuk alasan itu, SWQoS titik akhir adalah pilihan penting untuk pengiriman transaksi, tetapi dalam produksi titik akhir tunggal tidak selalu menjadi yang tercepat bahkan dalam slot yang sama, perbedaan jalur transien atau batang beban dapat memungkinkan titik akhir lain untuk memimpin.
Mengingat karakteristik ini, pola operasional mengirimkan transaksi yang sama ke beberapa jalan cepat secara paralel dan menerima yang pertama diproses dapat efektif, daripada mengandalkan pada satu rute.
Namun, ketika transaksi yang sama dikirim ke beberapa titik akhir, Anda tidak dapat menjamin bahwa itu akan mengeksekusi hanya sekali tanpa kontrol tambahan. Fan-out tanpa kontrol ini dapat menyebabkan eksekusi ganda yang tidak diinginkan atau kontrol ulang rusak.
Solana menyediakan Durable Nonce sebagai mekanisme untuk ini. Menggunakan Durable Noonce memungkinkan Anda mengirim transaksi yang sama ditandatangani di beberapa rute sambil membatasi on-rantai eksekusi untuk satu waktu.
Halaman ini menjelaskan bagaimana mengimplementasikan operasi fan-out yang menggabungkan ERPC's SWQoS titik akhir dengan cepat lainnya RPC titik akhir, dengan asumsi transaksi yang dikirim dengan Durable Nonce.

Scope dan prasyarat

Pemandu ini mencakup pembuatan akun Durable Nonce dengan web3.js dan menggunakannya untuk transaksi pengiriman dan operasi fan- out.
Prasyarat untuk memahami:
  • Untuk transaksi Durable Nonce, gunakan nilai nonce sebagai recentBlockhash dan tempat nonceAdvance sebagai instruksi pertama
  • Sekali nonceAdvance executes, nonce dapat dikonsumsi bahkan jika instruksi kemudian gagal. Anda tidak dapat mengirim ulang rawTx sama seperti - adalah.
  • Pembuatan akun nonce adalah setup satu waktu dan biasanya digunakan kembali

Langkah 1: Siapkan otoritas nonce dan koneksi

Otoritas nonce adalah Keypair yang dapat memajukan nonce tersebut.
typescript
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 */)
  • Otoritas nonce dapat mengeksekusi nonceAdvance
  • Ini bisa menjadi biaya pembayar, atau pasangan kunci terpisah

Langkah 2: Buat sebuah Keypair akun nonce

Akun nonce adalah akun sistem.
typescript
const nonceAccount = Keypair.generate()
Keypair ini digunakan untuk:
  • Memegang nilai nonce (pengganti untuk recentBlockhash)
  • Hanya menandai waktu penciptaan
  • Penyimpanan sesudahnya aman; tidak diperlukan untuk mengirim sehari-hari

Langkah 3: perhitungkan minimum yang dibebas-terminimalkan

Akun nonce harus dicantumkan. Jangan nilai kode keras; mengambilnya dari RPC.
typescript
const lamports =
  await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH,
  )

Langkah 4: Buat dan inisialisasi akun nonce

Buat akun nononce dengan * * createAccount + nonceInisialize * * dalam sebuah transaksi tunggal.
typescript
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',
)
Gunakan ini nonceAccount.publicKey untuk mengirimkan berikutnya.

Langkah 5: Ambil nonce sebelum masing-masing mengirim

Untuk setiap transaksi, ambil nilai nonce saat ini.
typescript
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
  • Gunakan nonce as recentBlockhash
  • context.slot Digunakan dalam konfirmasi

Langkah 6: Membangun transaksi Durable Nonce

Transaksi yang dapat didupkan Nonce harus memenuhi kondisi berikut:
  • recentBlockhash = nonce
  • Instruksi pertama adalah nonceAdvance
typescript
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()
Catatan:
  • Jika Anda menggunakan instruksi ComputeBudget, mereka harus datang * * setelah * * nonceAdvance
  • If nonceAdvance bukan yang pertama, transaksinya akan ditolak

Langkah 7: menyebar ke beberapa RPC

Kirim rawTx yang sama secara bersamaan.
typescript
const endpoints = [
  'https://<erpc-swqos-fra>',
  'https://<backup-rpc-1>',
  'https://<backup-rpc-2>',
]

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
  • Tanda tangan identik di seluruh titik akhir
  • Pengirimansukses adalah * * bukan * * konfirmasi

Langkah 8: Konfirmasi dengan Durable Nonce

Dengan Durable Nonce, masukkan info nonce dalam konfirmasi.
typescript
await connection.confirmTransaction(
  {
    signature,
    nonceAccountPubkey: nonceAccount.publicKey,
    nonceValue: nonce,
    minContextSlot,
  },
  'confirmed',
)
Jika konfirmasi berhasil:
  • The nonce kemajuan
  • Salinan dikirim ke RPC lain kembali InvalidNonce

Berikutnya kirim

  • Setelah konfirmasi, ambil satu nonce segar
  • Jangan gunakan nilai rawTx atau nonce yang sama
  • Untuk paralel mengalir, gunakan akun nonce terpisah
Untuk transaksi berikutnya, ambil nonce terbaru dan bangun transaksi menggunakan langkah yang sama.