튼튼한 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 */)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()const nonceAccount = Keypair.generate()이 Keypair는을 위해 사용됩니다:
- 비계 값 보유 (replacement for
recentBlockhash) - 창조 시간에서만 서명
- 안전 저장 후; 그것은 매일 보낼 필요가 없습니다
3 단계: 임대 면제 최소를 계산
계정은 임대료가 있어야 합니다. 하드코드 값이 없거나, 그로부터 표를 붙일 수 없습니다. RPC.
typescript
const lamports =
await connection.getMinimumBalanceForRentExemption(
NONCE_ACCOUNT_LENGTH,
)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',
)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.slotimport { 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- 제품 정보
nonceasrecentBlockhash 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()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.valueconst 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',
)await connection.confirmTransaction(
{
signature,
nonceAccountPubkey: nonceAccount.publicKey,
nonceValue: nonce,
minContextSlot,
},
'confirmed',
)확인이 성공하면:
- 비오는 날
- 다른 RPCs 반환에 보내진 경찰
InvalidNonce
다음 전송
- 확인 후 신선한 비를 볶음
- 같은 rawTx 또는 nonce 값을 재사용하지 마십시오.
- 평행한 워크플로우를 위해, 별도의 비계 계정을 사용하십시오
다음 거래의 경우 업데이트된 비를 태워 동일한 단계를 사용하여 거래를 구축하십시오.