Langlebige Nonce Fan-out für SWQoS

Hintergrund und Nutzungsszenario

In der Solana-Transaktion senden, Slot Progression, Leader-Platzierung und Netzwerk-Weg-Verstopfung ständig ändern, welche Route erreicht den führenden schnellsten. Dies ist nicht spezifisch für einen RPC-Anbieter oder einen Sendedienst; es ist ein strukturelles Merkmal von Solanas Ausführungsmodell.
Der SWQoS-Endpunkt von ERPC bietet einen Sendepfad, der Transaktionen in die von Führungskräften auf Basis von Stake-weighted Quality of Dienst (SWQoS) zugewiesene Prioritätsspur injiziert. Diese Prioritätsbandbreite beträgt ca. 5 x die der Nicht-Prioritätsspur und wird vor der Prioritätsgebührenauswertung angewendet.
Aus diesem Grund ist der SWQoS Endpunkt eine wichtige Option für die Transaktionsübertragung, aber in der Produktion ist ein einziger Endpunkt nicht immer der schnellste. Auch innerhalb desselben Slotss können transiente Bahndifferenzen oder Lastskew einen weiteren schnellen Endpunkt führen.
Bei diesen Merkmalen kann ein operationelles Muster des Sendens derselben Transaktion auf mehrere schnelle Pfade parallel und die Einführung des ersten verarbeiteten wirksam sein, anstatt auf eine einzige Route zu verlassen.
Wenn jedoch dieselbe Transaktion an mehrere Endpunkte gesendet wird, können Sie nicht garantieren, dass sie nur einmal ohne zusätzliche Kontrolle ausgeführt wird. Fan-out ohne diese Steuerung kann zu einer unbeabsichtigten Doppelausführung oder einer gebrochenen Wiederaufnahmesteuerung führen.
Solana bietet dauerhafte Nonce als Mechanismus dafür. Mit Durable Nonce können Sie die gleiche unterzeichnete Transaktion über mehrere Routen senden, während Sie die on-chain Ausführung auf eine einzige Zeit begrenzen.
Diese Seite erklärt, wie man Fan-out-Operationen implementiert, die den SWQoS-Endpunkt von ERPC mit anderen schnellen RPC-Endpunkten kombinieren, vorausgesetzt, dass die Transaktion mit Durable Nonce gesendet wird.

Umfang und Voraussetzungen

Dieser Leitfaden umfasst die Erstellung eines dauerhaften Nonce-Accounts mit web3.js und die Verwendung für Transaktions- und Fan-out-Operationen.
Voraussetzungen zu verstehen:
  • Für dauerhafte Nonce-Transaktionen verwenden Sie den Nonce-Wert als recentBlockhash und Ort nonceAdvance als erste Anleitung
  • Einmal nonceAdvance führt aus, das Nonce kann auch verbraucht werden, wenn spätere Anweisungen ausfallen. Sie können nicht die gleiche rohe Tx unverändert.
  • Nonce-Account-Erstellung ist ein einmaliges Setup und wird typischerweise wiederverwendet

Schritt 1: Bereiten Sie die nichtze Autorität und Verbindung

Die Nicht-Behörde ist eine Schlüsselfunktion, die die Nicht-Eigenschaft voranbringen kann.
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 */)
  • Die Nicht-Behörde kann nonceAdvance
  • Es kann der Gebührenzahler sein, oder eine separate Keypair

Schritt 2: Erstellen eines Nicht-E-Kontos

Ein Nonce-Konto ist ein *System-Konto.
typescript
const nonceAccount = Keypair.generate()
Diese Keypair wird verwendet für:
  • Nichterfüllungswert (Ersetzung für recentBlockhash)
  • Anmeldung nur zu Schöpfungszeit
  • Sichere Aufbewahrung nachher; es wird nicht für tägliche Sendungen benötigt

Schritt 3: Berechnen Sie das Miet-Befreiungsminimum

Nonce-Konten müssen mietbefreit sein. Nicht Hardcode-Werte; holen Sie sie vom RPC.
typescript
const lamports =
  await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH,
  )

Schritt 4: Erstellen und initialisieren Sie das Nonce-Konto

Erstellen Sie das Nonce-Konto mit erstellen Account + nonce Initialize in einer einzigen Transaktion.
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',
)
Verwenden Sie diese nonceAccount.publicKey für spätere Sendungen.

Schritt 5: Holen Sie die Nonce vor jedem Senden

Für jede Transaktion, holen Sie den aktuellen Nonce-Wert.
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
  • Verwendung nonce wie recentBlockhash
  • context.slot wird in der Bestätigung verwendet

Schritt 6: Erstellen Sie die dauerhafte Nonce Transaktion

Dauerhafte Nonce-Transaktionen müssen folgende Bedingungen erfüllen:
  • recentBlockhash = nonce
  • Die erste Anweisung ist 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()
Anmerkungen:
  • Wenn Sie Compute Budget-Anweisungen verwenden, müssen sie nach kommen nonceAdvance
  • wenn nonceAdvance wird nicht zuerst die Transaktion abgelehnt

Schritt 7: Fan out zu mehreren RPCs

Senden Sie den gleichen Roh Tx gleichzeitig.
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
  • Die Signatur ist über Endpunkte identisch
  • Ein erfolgreicher Versand ist nicht eine Bestätigung

Schritt 8: Bestätigen Sie mit dauerhaften Nonce

Mit Durable Nonce, beinhalten die nicht-ce-Info in Bestätigung.
typescript
await connection.confirmTransaction(
  {
    signature,
    nonceAccountPubkey: nonceAccount.publicKey,
    nonceValue: nonce,
    minContextSlot,
  },
  'confirmed',
)
Erfolgt die Bestätigung:
  • Die Nichterfüllung
  • Kopien an andere RPCs zurückgeschickt InvalidNonce

Nächster sendet

  • Nach der Bestätigung, holen Sie eine frische Nonce
  • Verwenden Sie nicht den gleichen Roh Tx oder Nichtze-Wert
  • Für parallele Workflows verwenden Sie separate nicht-ce-Konten
Für die nächste Transaktion, holen Sie die aktualisierte Nonce und bauen Sie die Transaktion mit den gleichen Schritten.