1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use core::fmt;
use solana_clock::Slot;
use solana_pubkey::Pubkey;
use crate::{AccountMode, AccountSharedData, OwnedAccount, WritableAccount};
const MAX_DATA_CHUNK_SIZE: usize = (u16::MAX - 256) as usize;
/// Failure to apply an account lifecycle or ordering patch.
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
pub enum AccountPatchError {
/// The requested account mode transition is not part of the lifecycle.
#[error("invalid account mode transition: {from:?} -> {to:?}")]
InvalidModeTransition {
/// Current account mode.
from: AccountMode,
/// Requested account mode.
to: AccountMode,
},
/// The requested slot regresses or fails the mode pair's advancement requirement.
#[error("invalid account slot transition: {from} -> {to}")]
InvalidSlotTransition {
/// Current account slot.
from: Slot,
/// Requested account slot.
to: Slot,
},
}
/// A bounded account-image patch.
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
pub enum AccountFieldPatch {
/// Replaces the lamport balance.
Lamports(u64),
/// Replaces the owner.
Owner(Pubkey),
/// Writes bytes starting at `offset`, extending the account data if needed.
DataAt {
/// Byte offset into the current data buffer.
offset: usize,
/// Bytes to write.
data: Vec<u8>,
},
/// Replaces the account lifecycle.
Lifecycle {
/// Requested lifecycle mode.
mode: AccountMode,
/// Requested lifecycle slot.
slot: Slot,
},
/// Resizes the data buffer to an exact length, zero-filling when growing.
DataLen(usize),
}
impl AccountFieldPatch {
/// Applies this patch to `account`.
///
/// The account methods mark dirtiness and preserve the writable invariants.
/// Invalid mode and slot transitions leave the account unchanged and return
/// their transition context.
pub fn apply(self, account: &mut AccountSharedData) -> Result<(), AccountPatchError> {
match self {
Self::Lamports(v) => account.set_lamports(v),
Self::Owner(v) => account.set_owner(v),
Self::Lifecycle { mode, slot } => return account.set_lifecycle(mode, slot),
Self::DataAt { offset, data } => account.set_data_at(offset, &data),
Self::DataLen(len) => account.resize(len, 0),
}
Ok(())
}
/// Decomposes an owned account into the ordered sequence of patches that
/// reconstruct its non-flag fields: lamports, lifecycle, owner, the exact
/// data length, then the data in `MAX_DATA_CHUNK_SIZE`-sized chunks.
pub fn sequence(account: OwnedAccount) -> Vec<Self> {
let mut sequence = Vec::with_capacity(5);
sequence.push(Self::Lamports(account.core.lamports));
sequence.push(Self::Lifecycle {
mode: account.core.mode,
slot: account.core.slot,
});
sequence.push(Self::Owner(account.core.owner));
sequence.push(Self::DataLen(account.data.len()));
let mut offset = 0;
for data in account.data.chunks(MAX_DATA_CHUNK_SIZE) {
sequence.push(Self::DataAt { offset, data: data.into() });
offset += data.len();
}
sequence
}
}
/// Concise, log-friendly rendering: scalar fields show their value, `DataAt`
/// shows only `offset+len` (never the raw bytes).
impl fmt::Debug for AccountFieldPatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Lamports(v) => write!(f, "lamports={v}"),
Self::Owner(v) => write!(f, "owner={v}"),
Self::Lifecycle { mode, slot } => write!(f, "lifecycle={mode:?}@{slot}"),
Self::DataAt { offset, data } => write!(f, "data@{offset}+{}", data.len()),
Self::DataLen(len) => write!(f, "data_len={len}"),
}
}
}