Skip to content

Commit f28e244

Browse files
committed
Refactor API
1 parent 7433112 commit f28e244

19 files changed

Lines changed: 727 additions & 739 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pinocchio/interface/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ homepage = { workspace = true }
88
license = { workspace = true }
99

1010
[dependencies]
11-
# TODO: Waiting on 0.3.0 to be released
12-
wincode = { git = "https://github.com/anza-xyz/wincode.git", rev = "fa70c7c7c13885085f743e4f01deb1a4de0b64fb", default-features = false, features = ["derive"] }
11+
wincode = { git = "https://github.com/anza-xyz/wincode.git", rev = "6feb75ff4c5b26275b0ee8bc4a12ea906d996a02", default-features = false, features = ["derive"] }
1312

1413
[dev-dependencies]
1514
bincode = "1.3.3"

pinocchio/interface/src/error.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ use crate::state::StakeStateV2Tag;
22

33
#[derive(Debug)]
44
pub enum StakeStateError {
5-
/// The discriminant tag is not a valid variant (must be 0-3).
5+
/// Field access invalid for the current state.
6+
InvalidStateAccess(StakeStateV2Tag),
7+
/// Tag is not a valid variant (0-3).
68
InvalidTag(u32),
7-
/// An invalid state transition was attempted.
9+
/// Invalid state transition attempted.
810
InvalidTransition {
911
from: StakeStateV2Tag,
1012
to: StakeStateV2Tag,
1113
},
12-
/// Pass-through for wincode read errors when borrowing layout structs.
14+
/// Wincode deserialization error.
1315
Read(wincode::ReadError),
14-
/// Input buffer is shorter than 200 bytes.
16+
/// Buffer shorter than 200 bytes.
1517
UnexpectedEof,
16-
/// Pass-through for wincode write errors when serializing layout structs.
18+
/// Wincode serialization error.
1719
Write(wincode::WriteError),
1820
}
1921

pinocchio/interface/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
#![no_std]
2+
13
pub mod error;
4+
pub mod pod;
25
pub mod state;
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use wincode::{SchemaRead, SchemaWrite};
1111

1212
/// Macro to define an alignment-1 little-endian integer wrapper.
13-
#[macro_export]
1413
macro_rules! impl_pod_int {
1514
(
1615
$(#[$meta:meta])*

pinocchio/interface/src/state.rs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
//! Zero-copy stake state types.
2+
3+
use {
4+
crate::{
5+
error::StakeStateError,
6+
pod::{PodAddress, PodI64, PodU32, PodU64},
7+
},
8+
core::mem::size_of,
9+
wincode::{SchemaRead, SchemaWrite, ZeroCopy},
10+
};
11+
12+
#[repr(C)]
13+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
14+
#[wincode(assert_zero_copy)]
15+
pub struct Authorized {
16+
pub staker: PodAddress,
17+
pub withdrawer: PodAddress,
18+
}
19+
20+
#[repr(C)]
21+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
22+
#[wincode(assert_zero_copy)]
23+
pub struct Lockup {
24+
/// `UnixTimestamp` at which this stake will allow withdrawal, unless the
25+
/// transaction is signed by the custodian.
26+
pub unix_timestamp: PodI64,
27+
/// Epoch height at which this stake will allow withdrawal, unless the
28+
/// transaction is signed by the custodian.
29+
pub epoch: PodU64,
30+
/// Custodian signature on a transaction exempts the operation from
31+
/// lockup constraints.
32+
pub custodian: PodAddress,
33+
}
34+
35+
#[repr(C)]
36+
#[derive(Clone, Copy, Debug, PartialEq, Eq, SchemaWrite, SchemaRead, Default)]
37+
#[wincode(assert_zero_copy)]
38+
pub struct Meta {
39+
pub rent_exempt_reserve: PodU64,
40+
pub authorized: Authorized,
41+
pub lockup: Lockup,
42+
}
43+
44+
#[repr(C)]
45+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
46+
#[wincode(assert_zero_copy)]
47+
pub struct Delegation {
48+
/// To whom the stake is delegated.
49+
pub voter_pubkey: PodAddress,
50+
/// Activated stake amount, set at delegate() time.
51+
pub stake: PodU64,
52+
/// Epoch at which this stake was activated, `u64::MAX` if is a bootstrap stake.
53+
pub activation_epoch: PodU64,
54+
/// Epoch the stake was deactivated, `u64::MAX` if not deactivated.
55+
pub deactivation_epoch: PodU64,
56+
/// Reserved bytes (formerly warmup/cooldown rate).
57+
/// Deprecated in the runtime but preserved for ABI compatibility.
58+
pub _reserved: [u8; 8],
59+
}
60+
61+
#[repr(C)]
62+
#[derive(Clone, Copy, Debug, PartialEq, Eq, SchemaWrite, SchemaRead, Default)]
63+
#[wincode(assert_zero_copy)]
64+
pub struct Stake {
65+
pub delegation: Delegation,
66+
/// Credits observed is credits from vote account state when delegated or redeemed.
67+
pub credits_observed: PodU64,
68+
}
69+
70+
/// Discriminant tag for stake account state (first 4 bytes).
71+
#[repr(u32)]
72+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73+
pub enum StakeStateV2Tag {
74+
Uninitialized = 0,
75+
Initialized = 1,
76+
Stake = 2,
77+
RewardsPool = 3,
78+
}
79+
80+
impl StakeStateV2Tag {
81+
pub const TAG_LEN: usize = size_of::<PodU32>();
82+
83+
#[inline]
84+
pub(crate) fn from_u32(v: u32) -> Result<Self, StakeStateError> {
85+
match v {
86+
0 => Ok(Self::Uninitialized),
87+
1 => Ok(Self::Initialized),
88+
2 => Ok(Self::Stake),
89+
3 => Ok(Self::RewardsPool),
90+
other => Err(StakeStateError::InvalidTag(other)),
91+
}
92+
}
93+
94+
#[inline]
95+
pub fn from_bytes(bytes: &[u8]) -> Result<Self, StakeStateError> {
96+
if bytes.len() < 4 {
97+
return Err(StakeStateError::UnexpectedEof);
98+
}
99+
let raw = u32::from_le_bytes(bytes[..4].try_into().unwrap());
100+
Self::from_u32(raw)
101+
}
102+
}
103+
104+
/// 200-byte stake account layout:
105+
///
106+
/// ```text
107+
/// ┌────────┬──────┬────────────┐
108+
/// │ Offset │ Size │ Field │
109+
/// ├────────┼──────┼────────────┤
110+
/// │ 0 │ 4 │ Tag │
111+
/// │ 4 │ 120 │ Meta │
112+
/// │ 124 │ 72 │ Stake │
113+
/// │ 196 │ 1 │ StakeFlags │
114+
/// │ 197 │ 3 │ Padding │
115+
/// └────────┴──────┴────────────┘
116+
/// ```
117+
///
118+
/// All fields are alignment-1 for safe zero-copy from unaligned byte slices.
119+
#[repr(C)]
120+
#[derive(Clone, Copy, Debug, PartialEq, Eq, SchemaWrite, SchemaRead)]
121+
#[wincode(assert_zero_copy)]
122+
pub struct StakeStateV2 {
123+
tag: PodU32,
124+
meta: Meta,
125+
stake: Stake,
126+
stake_flags: u8,
127+
padding: [u8; 3],
128+
}
129+
130+
// compile-time size check
131+
const _: () = assert!(size_of::<StakeStateV2>() == 200);
132+
133+
impl StakeStateV2 {
134+
/// The fixed size of a stake account in bytes.
135+
pub const LEN: usize = size_of::<StakeStateV2>();
136+
137+
/// Parse stake account data into a read-only reference.
138+
pub fn from_bytes(data: &[u8]) -> Result<&Self, StakeStateError> {
139+
if data.len() < Self::LEN {
140+
return Err(StakeStateError::UnexpectedEof);
141+
}
142+
let state = <Self as ZeroCopy>::from_bytes(data)?;
143+
StakeStateV2Tag::from_u32(state.tag.get())?;
144+
Ok(state)
145+
}
146+
147+
/// Parse stake account data into a mutable reference.
148+
pub fn from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, StakeStateError> {
149+
if data.len() < Self::LEN {
150+
return Err(StakeStateError::UnexpectedEof);
151+
}
152+
let state = <Self as ZeroCopy>::from_bytes_mut(data)?;
153+
StakeStateV2Tag::from_u32(state.tag.get())?;
154+
Ok(state)
155+
}
156+
157+
/// Returns the state tag (infallible since validated at construction).
158+
#[inline]
159+
pub fn tag(&self) -> StakeStateV2Tag {
160+
StakeStateV2Tag::from_u32(self.tag.get()).unwrap()
161+
}
162+
163+
/// Returns a reference to `Meta` if in the `Initialized` or `Stake` state.
164+
#[inline]
165+
pub fn meta(&self) -> Option<&Meta> {
166+
match self.tag() {
167+
StakeStateV2Tag::Initialized | StakeStateV2Tag::Stake => Some(&self.meta),
168+
_ => None,
169+
}
170+
}
171+
172+
/// Returns a reference to `Stake` if in the `Stake` state.
173+
#[inline]
174+
pub fn stake(&self) -> Option<&Stake> {
175+
match self.tag() {
176+
StakeStateV2Tag::Stake => Some(&self.stake),
177+
_ => None,
178+
}
179+
}
180+
181+
/// Returns a mutable reference to `Meta` if in the `Initialized` or `Stake` state.
182+
#[inline]
183+
pub fn meta_mut(&mut self) -> Result<&mut Meta, StakeStateError> {
184+
match self.tag() {
185+
StakeStateV2Tag::Initialized | StakeStateV2Tag::Stake => Ok(&mut self.meta),
186+
tag => Err(StakeStateError::InvalidStateAccess(tag)),
187+
}
188+
}
189+
190+
/// Returns a mutable reference to `Stake` if in the `Stake` state.
191+
#[inline]
192+
pub fn stake_mut(&mut self) -> Result<&mut Stake, StakeStateError> {
193+
match self.tag() {
194+
StakeStateV2Tag::Stake => Ok(&mut self.stake),
195+
tag => Err(StakeStateError::InvalidStateAccess(tag)),
196+
}
197+
}
198+
199+
/// Transition from `Uninitialized` to `Initialized`.
200+
/// Clears the stake and tail regions.
201+
pub fn initialize(&mut self, meta: Meta) -> Result<(), StakeStateError> {
202+
let from = self.tag();
203+
if from != StakeStateV2Tag::Uninitialized {
204+
return Err(StakeStateError::InvalidTransition {
205+
from,
206+
to: StakeStateV2Tag::Initialized,
207+
});
208+
}
209+
210+
self.stake = Stake::default();
211+
self.stake_flags = 0;
212+
self.padding.fill(0);
213+
self.meta = meta;
214+
self.tag.set(StakeStateV2Tag::Initialized as u32);
215+
216+
Ok(())
217+
}
218+
219+
/// Transition to `Stake` state from `Initialized` or `Stake`.
220+
/// - From `Initialized`: clears tail region to zero.
221+
/// - From `Stake`: preserves existing tail region.
222+
pub fn delegate(&mut self, meta: Meta, stake: Stake) -> Result<(), StakeStateError> {
223+
let from = self.tag();
224+
if !matches!(from, StakeStateV2Tag::Initialized | StakeStateV2Tag::Stake) {
225+
return Err(StakeStateError::InvalidTransition {
226+
from,
227+
to: StakeStateV2Tag::Stake,
228+
});
229+
}
230+
231+
if from == StakeStateV2Tag::Initialized {
232+
self.stake_flags = 0;
233+
self.padding.fill(0);
234+
}
235+
236+
self.meta = meta;
237+
self.stake = stake;
238+
self.tag.set(StakeStateV2Tag::Stake as u32);
239+
240+
Ok(())
241+
}
242+
}

pinocchio/interface/src/state/entrypoint.rs

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)