pub struct RpcSnapshotSource { /* private fields */ }Expand description
A SnapshotSource backed by a Soroban RPC + local cache.
Cache semantics:
Some(Some(entry))→ we’ve seen it, entry exists.Some(None)→ we’ve asked, RPC said the entry doesn’t exist. Negative cache — stops us re-asking for keys we know are absent.None→ we haven’t asked yet.
Thread-safety: this type is Send + Sync. Wrap it in Arc to share
across threads — for example, between a future RPC-server worker pool
and the Env instances handed to each request. The SDK’s
SnapshotSource trait still expects an Rc at its boundary, so a
fresh Rc<LedgerEntry> is built per get call from cached bytes; the
Rc never escapes its caller’s thread.
Implementations§
Source§impl RpcSnapshotSource
impl RpcSnapshotSource
Sourcepub fn new(client: Arc<RpcClient>) -> Self
pub fn new(client: Arc<RpcClient>) -> Self
Wrap the given RPC client. Arc so the source can be cloned cheaply
and shared with other harnesses (e.g. a pre-warmer).
Sourcepub fn with_fetch_mode(self, mode: FetchMode) -> Self
pub fn with_fetch_mode(self, mode: FetchMode) -> Self
Set the fetch mode. Builder-style — consumes self and returns it.
Sourcepub fn preload(
&self,
entries: impl IntoIterator<Item = (LedgerKey, LedgerEntry, Option<u32>)>,
)
pub fn preload( &self, entries: impl IntoIterator<Item = (LedgerKey, LedgerEntry, Option<u32>)>, )
Pre-populate the cache from a snapshot file. Entries loaded this
way do not count towards fetch_count.
Sourcepub fn fetch_count(&self) -> u32
pub fn fetch_count(&self) -> u32
How many RPC fetches this source has attempted since creation (counts both successful and failed attempts — the counter increments before the network call, so a connect timeout still shows up here). Useful for asserting cache hit-rates in tests.
Sourcepub fn set_entry(
&self,
key: LedgerKey,
entry: LedgerEntry,
live_until: Option<u32>,
)
pub fn set_entry( &self, key: LedgerKey, entry: LedgerEntry, live_until: Option<u32>, )
Force-write a single LedgerEntry into the cache, replacing
whatever was there (or creating a fresh entry if the key was
absent). Powers the JSON-RPC fork_setLedgerEntry extension
— clients hand us an XDR-encoded entry, we trust them and
install it. Subsequent reads (including via the host’s
recording-mode storage in simulateTransaction /
sendTransaction) see the new entry.
Stellar’s storage model maps every piece of network state to
one LedgerEntry per key, so this single primitive covers
every flavor of fork-mode state mutation: oracle-price
rewrites (a ContractData entry), token balance overrides
(a Trustline or ContractData entry), contract code
replacement (a ContractCode entry). Higher-level wrappers
(setBalance, setCode, etc.) compose on top.
live_until carries forward an optional TTL hint — pass
None for entries that don’t have one (Account, Trustline)
or when the test doesn’t care about expiry.
Sourcepub fn bump_account_seq(&self, account_id: &AccountId) -> Option<i64>
pub fn bump_account_seq(&self, account_id: &AccountId) -> Option<i64>
Bump the seq_num of an Account ledger entry that’s already
in the cache. Returns the new sequence on success, None if
the account isn’t cached or the cached entry isn’t an
AccountEntry (so the caller can decide whether absence is
an error or just “first send from a never-touched account”).
Stellar’s transaction validation expects tx.seq_num == account.seq_num + 1 and post-success leaves the account at
tx.seq_num. The fork’s trust mode skips the pre-check but
still must increment so the next envelope a JS-SDK client
builds (via getAccount → tx.seq_num + 1) lines up with
what the host expects.
Sourcepub fn apply_changes<I>(&self, changes: I) -> u32where
I: IntoIterator<Item = LedgerEntryChange>,
pub fn apply_changes<I>(&self, changes: I) -> u32where
I: IntoIterator<Item = LedgerEntryChange>,
Apply a batch of LedgerEntryChanges back to the cache so that
subsequent reads see the writes. Powers the JSON-RPC server’s
sendTransaction — recording-mode invocation gives us a list
of changes; we walk them and update the cached bytes.
Semantics per change:
read_only == true→ ignored. The host won’t have produced anew_valueand TTL bumps don’t change entry contents.encoded_new_value == Some(bytes)→ overwrite the cached entry. Existing live-until is kept (TTL changes are tracked separately by the host but not yet plumbed here).encoded_new_value == None(read-write) → entry was removed; flip the cache to the negative-cacheNonemarker so subsequentgets see absence locally without re-asking upstream RPC.
key and entry decode panics in this method are the same
“structural bug, not recoverable” class as elsewhere in this
file: bytes came directly from the host that just produced
them, so a decode failure means the host violated its own
XDR-shape invariant.
Sourcepub fn entries(&self) -> Vec<(LedgerKey, LedgerEntry, Option<u32>)>
pub fn entries(&self) -> Vec<(LedgerKey, LedgerEntry, Option<u32>)>
Export the cache for persistence. Negative-cache entries (confirmed missing) are intentionally omitted — they aren’t useful across processes and bloat the on-disk snapshot.
The decode step runs outside the cache lock: we snapshot raw
bytes under the lock, release it, then parse. With a 10k-entry
fork this avoids blocking concurrent get and fetch calls for
the duration of a few-ms parse loop.
Trait Implementations§
Source§impl SnapshotSource for RpcSnapshotSource
impl SnapshotSource for RpcSnapshotSource
Auto Trait Implementations§
impl !Freeze for RpcSnapshotSource
impl !RefUnwindSafe for RpcSnapshotSource
impl Send for RpcSnapshotSource
impl Sync for RpcSnapshotSource
impl Unpin for RpcSnapshotSource
impl UnsafeUnpin for RpcSnapshotSource
impl !UnwindSafe for RpcSnapshotSource
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for C
impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for C
type Error = E
fn compare( &self, a: &(T, U, V, W), b: &(T, U, V, W), ) -> Result<Ordering, <C as Compare<(T, U, V, W)>>::Error>
Source§impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for C
impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for C
type Error = E
fn compare( &self, a: &(T, U, V, W, X), b: &(T, U, V, W, X), ) -> Result<Ordering, <C as Compare<(T, U, V, W, X)>>::Error>
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more