@rpc-bastion/testkit
v0.4.1
Published
Deterministic in-process Solana network chaos simulator.
Downloads
471
Readme
@rpc-bastion/testkit
Deterministic, in-process Solana network chaos simulator — the SDK's test
foundation and a public tool for testing your own dApp's failure handling.
Speaks the web3.js v2.0 (@solana/kit) transport contract, so it drops in
wherever a Kit transport goes.
What's inside
| Export | Purpose |
|---|---|
| createMockRpcServer(config?) | Fake Solana JSON-RPC over a Kit transport, with a simulated chain clock and scriptable confirmations |
| server.endpoint(view?) | A transport for ONE endpoint that diverges from the shared chain (status lag, block-height skew, blockhash propagation delay) |
| createChaosTransport(inner, config?) | Wraps a transport with seeded faults: latency, drops, HTTP 429/500/503, malformed JSON, periodic disconnects |
| ChaosPlan / resolvePlanState | Timeline DSL: change an endpoint's faults at scripted times |
| createSeededRng(seed) | Deterministic PRNG (mulberry32) |
| createManualClock(start?) / sleep | Injectable time |
| base58Encode/Decode, extractSignatureFromWireTransaction | Dependency-free Base58 + tx signature id |
Example
import { createSolanaRpcFromTransport } from '@solana/kit';
import { createMockRpcServer, createChaosTransport } from '@rpc-bastion/testkit';
const server = createMockRpcServer({ slot: 1000 });
const flaky = createChaosTransport(server.transport, {
seed: 42,
faults: { errorRate: { http429: 0.3 }, latency: { minMs: 20, maxMs: 80 } },
});
const rpc = createSolanaRpcFromTransport(flaky);
await rpc.getSlot().send(); // sometimes 429s, deterministically, for seed 42Confirmation policies
{ mode: 'auto', afterSlots }— confirms once the chain advances (happy path){ mode: 'manual' }— confirms only viaserver.confirmTransaction(sig){ mode: 'afterSends', sends }— confirms once a signature is (re)sent N times (models "dropped, lands on rebroadcast N")
Determinism is the core guarantee: same seed + same request sequence ⇒
identical injected faults across runs and machines.
Cross-node divergence
Real clusters disagree about the chain: one node's status index lags, another is
a few slots behind on block height, a third is slow to surface a fresh blockhash.
server.endpoint(view) hands you a transport for ONE such node. Sends from any
endpoint land in the single shared truth ledger — only that endpoint's reads
are skewed. Point a pool at several to test your own dApp against a divergent
cluster (e.g. that your confirmation logic doesn't falsely expire a tx the
status-blind node simply hasn't surfaced yet):
const server = createMockRpcServer({ slot: 1000 });
const pool = createResilientRpc(
[{ url: 'a' }, { url: 'b' }, { url: 'c' }],
{
pool: {
transportFactory: ({ url }) =>
url === 'a' ? server.endpoint({ statusLagSlots: 40 }) // status-blind node
: url === 'b' ? server.endpoint({ blockHeightSkewSlots: -5 }) // a few slots behind
: server.endpoint(), // in sync
},
},
);| View field | Effect |
|---|---|
| statusLagSlots | getSignatureStatuses won't surface a landed tx until currentSlot ≥ landedSlot + lag |
| blockHeightSkewSlots | getBlockHeight / getSlot are offset (negative = behind, positive = ahead) |
| blockhashPropagationSlots | getLatestBlockhash returns the blockhash that was current this many slots ago |
