튼튼한 Nonce 팬-아웃을 위한 SWQoS

배경 및 사용 시나리오

In Solana 거래 전송, 슬롯 진행, 리더 배치, 네트워크 경로 혼잡 지속적으로 변화하는 가장 빠른 리더에 도달. 이것은 어떤 특정하지 않습니다 RPC 공급자 또는 서비스를 보내; 그것은 구조상 특성의 Solana's 실행 모델.
ERPC's SWQoS endpoint는 Stake-weighted Quality of Service를 기반으로 리더가 우선 순위를 할당 한 전송 경로 제공 (SWQoS). 이 우선권 대역폭은 비 선명한 차선의 대략 5×이고 우선권 평가의 앞에 적용됩니다.
그 이유를 위해, SWQoS endpoint는 거래 전송에 중요한 옵션이지만, 단일 엔드 포인트 생산은 항상 가장 빠르지 않습니다. 동일한 슬롯 내에서도, 일시적 경로 차이 또는 로드 스쿠는 다른 빠른 엔드 포인트를 리드 할 수 있습니다.
이 특성을 부여, 병렬에 여러 빠른 경로에 동일한 거래를 전송하는 작업 패턴을 허용하고, 첫 번째 처리 된 것을 허용하는 것은 단일 경로에 의존하는 것보다 효과적 일 수 있습니다.
그러나 동일한 거래가 다중 엔드포인트로 전송되면 추가 제어없이 한 번만 실행할 수 없습니다. 이 제어없이 팬 아웃은 이중 실행 또는 깨진 재스트 제어를 무인화 할 수 있습니다.
Solana 이 메커니즘으로 내구성이 좋은 노스를 제공합니다. 튼튼한 노스를 사용하면 단일 시간에 체인 실행을 제한하면서 여러 가지 경로에서 동일한 서명 트랜잭션을 보낼 수 있습니다.
이 페이지는 팬 아웃 작업을 수행하는 방법을 설명합니다. ERPC's SWQoS 다른 빠른 끝점 RPC endpoints, 튼튼한 Nonce로 전송 assuming 거래.

범위 및 prerequisites

이 가이드는 web3.js를 사용하여 튼튼한 Nonce 계정을 만들고 트랜잭션 전송 및 팬 아웃 작업을 위해 그것을 사용합니다.
이해하기 위한 필수 사항:
  • 튼튼한 Nonce 거래를 위해, nonce 가치를 것과 같이 사용하십시오 recentBlockhash - 연혁 nonceAdvance 첫 번째 명령으로
  • 을 읽다 nonceAdvance 실행, nonce는 나중에 지시가 실패하더라도 소모 될 수 있습니다. 같은 rawTx를 as-is를 다시 구할 수 없습니다.
  • Nonce 계정 생성은 한 번 설정이며 일반적으로 재사용됩니다.

단계 1: 비정규 및 연결을 준비

비정규는 비정규를 미리 할 수있는 Keypair입니다.
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 */)
  • nonce 권위는 실행할 수 있습니다 nonceAdvance
  • 그것은 수수료 지급기, 또는 별도의 Keypair일 수 있습니다

단계 2: 비계 계정 Keypair 생성

비계 계정은 시스템 계정입니다.
typescript
const nonceAccount = Keypair.generate()
이 Keypair는을 위해 사용됩니다:
  • 비계 값 보유 (replacement for recentBlockhash)
  • 창조 시간에서만 서명
  • 안전 저장 후; 그것은 매일 보낼 필요가 없습니다

3 단계: 임대 면제 최소를 계산

계정은 임대료가 있어야 합니다. 하드코드 값이 없거나, 그로부터 표를 붙일 수 없습니다. RPC.
typescript
const lamports =
  await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH,
  )

단계 4: 생성 및 초기화 nonce 계정

createAccount + nonceInitialize를 단일 트랜잭션에서 비례 계정 만들기.
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',
)
이 문서 nonceAccount.publicKey 이후의 전송

단계 5: 각 보내 전에 nonce를 흠뻑 취하십시오

모든 거래에 대해 현재 비례 값을 읽습니다.
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
  • 제품 정보 nonce as recentBlockhash
  • context.slot 확인에서 사용됩니다

단계 6: 튼튼한 Nonce 거래를 건설

튼튼한 Nonce 거래는 뒤에 오는 상태를 만족해야 합니다:
  • recentBlockhash = nonce
  • 첫 번째 명령은 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()
참고:
  • ComputeBudget 지침을 사용한다면, 그들은 ** 이후 ** nonceAdvance
  • If nonceAdvance 먼저 거래가 거부되지 않습니다.

단계 7: 다수 RPCs에 팬 밖으로

같은 rawTx를 concurrently 보내십시오.
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
  • 서명은 endpoints의 맞은편에 동일합니다
  • 성공적인 전송은 **** 확인되지 않습니다

단계 8: 내구재 Nonce로 확인

튼튼한 Nonce로, 확인에 있는 nonce 정보를 포함합니다.
typescript
await connection.confirmTransaction(
  {
    signature,
    nonceAccountPubkey: nonceAccount.publicKey,
    nonceValue: nonce,
    minContextSlot,
  },
  'confirmed',
)
확인이 성공하면:
  • 비오는 날
  • 다른 RPCs 반환에 보내진 경찰 InvalidNonce

다음 전송

  • 확인 후 신선한 비를 볶음
  • 같은 rawTx 또는 nonce 값을 재사용하지 마십시오.
  • 평행한 워크플로우를 위해, 별도의 비계 계정을 사용하십시오
다음 거래의 경우 업데이트된 비를 태워 동일한 단계를 사용하여 거래를 구축하십시오.