Skip to content

Commit 8f34c20

Browse files
committed
Tests pt.1
1 parent 04d5879 commit 8f34c20

16 files changed

Lines changed: 1830 additions & 1121 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,12 @@ repository = { workspace = true }
77
homepage = { workspace = true }
88
license = { workspace = true }
99

10-
[features]
11-
serde = ["dep:serde"]
12-
1310
[dependencies]
14-
solana-address = "1.0.0"
1511
wincode = { git = "https://github.com/anza-xyz/wincode.git", branch = "master", default-features = false, features = ["derive"] }
16-
serde = { version = "1.0", features = ["derive"], optional = true }
1712

1813
[dev-dependencies]
1914
bincode = "1.3.3"
2015
proptest = "1.9.0"
2116
solana-pubkey = "3.0.0"
2217
solana-stake-interface = { path = "../../interface", features = ["bincode"] }
18+
test-case = "3.3.1"

pinocchio/interface/src/error.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
1-
//! Error types for stake state parsing.
2-
31
use crate::state::StakeStateV2Tag;
42

53
#[derive(Debug)]
64
pub enum StakeStateError {
7-
/// Input is shorter than 200 bytes.
8-
UnexpectedEof,
9-
10-
/// The discriminant tag is not a valid variant
5+
/// The discriminant tag is not a valid variant (must be 0-3).
116
InvalidTag(u32),
12-
137
/// An invalid state transition was attempted.
148
InvalidTransition {
159
from: StakeStateV2Tag,
1610
to: StakeStateV2Tag,
1711
},
18-
19-
/// Pass-through for wincode read errors.
12+
/// Pass-through for wincode read errors when borrowing layout structs.
2013
Read(wincode::ReadError),
21-
22-
/// Pass-through for wincode write errors.
14+
/// Input buffer is shorter than 200 bytes.
15+
UnexpectedEof,
16+
/// Pass-through for wincode write errors when serializing layout structs.
2317
Write(wincode::WriteError),
2418
}
2519

pinocchio/interface/src/state/entrypoint.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
//! High-level entrypoint for reading and writing stake account state.
12
use {
23
super::{view::StakeStateV2View, writer::StakeStateV2Writer},
34
crate::error::StakeStateError,
45
};
56

7+
/// Main entrypoint for parsing stake account data. Provides zero-copy access to the stake account state.
68
pub struct StakeStateV2;
79

810
impl StakeStateV2 {

pinocchio/interface/src/state/layout.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ pub struct Authorized {
1717
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
1818
#[wincode(assert_zero_copy)]
1919
pub struct Lockup {
20+
/// `UnixTimestamp` at which this stake will allow withdrawal, unless the
21+
/// transaction is signed by the custodian
2022
pub unix_timestamp: PodI64,
23+
/// epoch height at which this stake will allow withdrawal, unless the
24+
/// transaction is signed by the custodian
2125
pub epoch: PodU64,
26+
/// custodian signature on a transaction exempts the operation from
27+
/// lockup constraints
2228
pub custodian: PodAddress,
2329
}
2430

@@ -35,10 +41,15 @@ pub struct Meta {
3541
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
3642
#[wincode(assert_zero_copy)]
3743
pub struct Delegation {
44+
/// to whom the stake is delegated
3845
pub voter_pubkey: PodAddress,
46+
/// activated stake amount, set at delegate() time
3947
pub stake: PodU64,
48+
/// epoch at which this stake was activated, `std::u64::MAX` if is a bootstrap stake
4049
pub activation_epoch: PodU64,
50+
/// epoch the stake was deactivated, `std::u64::MAX` if not deactivated
4151
pub deactivation_epoch: PodU64,
52+
/// Legacy bytes from legacy warmup/cooldown rate encoding (deprecated).
4253
pub _reserved: [u8; 8],
4354
}
4455

@@ -47,12 +58,12 @@ pub struct Delegation {
4758
#[wincode(assert_zero_copy)]
4859
pub struct Stake {
4960
pub delegation: Delegation,
61+
/// credits observed is credits from vote account state when delegated or redeemed
5062
pub credits_observed: PodU64,
5163
}
5264

5365
#[repr(u32)]
5466
#[derive(Clone, Copy, Debug, PartialEq, Eq, SchemaRead, SchemaWrite)]
55-
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
5667
#[wincode(tag_encoding = "u32")]
5768
pub enum StakeStateV2Tag {
5869
#[wincode(tag = 0)]
@@ -91,11 +102,7 @@ impl StakeStateV2Tag {
91102
}
92103
}
93104

94-
/// Raw 200-byte stake account data.
95-
///
96-
/// # Layout
97-
///
98-
/// A stake account is always 200 bytes with this structure:
105+
/// Raw 200-byte stake account data with this structure:
99106
///
100107
/// ```text
101108
/// ┌────────┬──────┬────────────┐

pinocchio/interface/src/state/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Zero-copy stake state types
1+
//! Zero-copy stake state types.
22
33
mod entrypoint;
44
mod layout;
Lines changed: 6 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
11
//! Alignment-1 ("pod") primitives for zero-copy deserialization.
22
//!
3-
//! # Why alignment-1?
4-
//!
53
//! Solana account data is a raw `&[u8]` with no alignment guarantees.
64
//! Standard Rust primitives like `u64` require 8-byte alignment, so you can't
75
//! safely cast `&[u8]` to `&u64` without risking undefined behavior.
86
//!
97
//! These "pod" (plain old data) types wrap byte arrays and provide safe
108
//! get/set methods that handle little-endian conversion. They have alignment 1,
119
//! so they can be safely referenced from any byte offset.
12-
//!
13-
//! # Layout
14-
//!
15-
//! All types use `#[repr(transparent)]` to ensure the struct has the same
16-
//! memory layout as its inner byte array.
17-
18-
use {
19-
solana_address::Address,
20-
wincode::{SchemaRead, SchemaWrite},
21-
};
10+
use wincode::{SchemaRead, SchemaWrite};
2211

2312
/// Macro to define an alignment-1 little-endian integer wrapper.
24-
///
25-
/// Generates:
26-
/// - `#[repr(transparent)]` wrapper over `[u8; N]`
27-
/// - `Default` (all zeros)
28-
/// - `const fn from_primitive`
29-
/// - `get/set/as_bytes`
30-
/// - `From<prim> for Pod*` and `From<Pod*> for prim`
3113
#[macro_export]
32-
macro_rules! impl_pod_int_le {
14+
macro_rules! impl_pod_int {
3315
(
3416
$(#[$meta:meta])*
3517
$name:ident, $prim:ty, $n:expr
@@ -41,170 +23,57 @@ macro_rules! impl_pod_int_le {
4123
pub struct $name(pub [u8; $n]);
4224

4325
impl $name {
44-
/// Creates from a native primitive value, stored as little-endian bytes.
45-
#[inline(always)]
4626
pub const fn from_primitive(v: $prim) -> Self {
4727
Self(v.to_le_bytes())
4828
}
4929

50-
/// Reads the value as a native primitive.
51-
#[inline(always)]
5230
pub fn get(self) -> $prim {
5331
<$prim>::from_le_bytes(self.0)
5432
}
5533

56-
/// Writes a native primitive value.
57-
#[inline(always)]
5834
pub fn set(&mut self, v: $prim) {
5935
self.0 = v.to_le_bytes();
6036
}
6137

62-
/// Returns the raw little-endian bytes.
63-
#[inline(always)]
6438
pub fn as_bytes(&self) -> &[u8; $n] {
6539
&self.0
6640
}
6741

68-
/// Returns the raw little-endian bytes as a mutable slice.
69-
#[inline(always)]
7042
pub fn as_mut_slice(&mut self) -> &mut [u8] {
7143
&mut self.0
7244
}
7345
}
7446

7547
impl From<$prim> for $name {
76-
#[inline(always)]
7748
fn from(v: $prim) -> Self {
7849
Self::from_primitive(v)
7950
}
8051
}
8152

8253
impl From<$name> for $prim {
83-
#[inline(always)]
8454
fn from(v: $name) -> Self {
8555
v.get()
8656
}
8757
}
8858
};
8959
}
9060

91-
impl_pod_int_le!(
92-
/// A `u32` stored as 4 little-endian bytes with alignment 1.
93-
PodU32, u32, 4
94-
);
95-
96-
impl_pod_int_le!(
97-
/// A `u64` stored as 8 little-endian bytes with alignment 1.
98-
PodU64, u64, 8
99-
);
61+
impl_pod_int!(PodU32, u32, 4);
62+
impl_pod_int!(PodU64, u64, 8);
63+
impl_pod_int!(PodI64, i64, 8);
10064

101-
impl_pod_int_le!(
102-
/// An `i64` stored as 8 little-endian bytes with alignment 1.
103-
PodI64, i64, 8
104-
);
105-
106-
/// An `Address` (pubkey) stored as 32 bytes with alignment 1.
65+
/// An `Address` stored as 32 bytes with alignment 1.
10766
#[repr(transparent)]
10867
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, SchemaWrite, SchemaRead)]
10968
#[wincode(assert_zero_copy)]
11069
pub struct PodAddress(pub [u8; 32]);
11170

11271
impl PodAddress {
113-
/// Converts to a native `Address`.
114-
#[inline(always)]
115-
pub fn to_address(self) -> Address {
116-
Address::new_from_array(self.0)
117-
}
118-
119-
/// Returns the raw bytes.
120-
#[inline(always)]
12172
pub fn as_bytes(&self) -> &[u8; 32] {
12273
&self.0
12374
}
12475

125-
/// Creates from raw bytes.
126-
#[inline(always)]
12776
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
12877
Self(bytes)
12978
}
130-
131-
/// Creates from a native `Address`.
132-
#[inline(always)]
133-
pub fn from_address(addr: Address) -> Self {
134-
Self(addr.to_bytes())
135-
}
136-
}
137-
138-
impl From<Address> for PodAddress {
139-
#[inline(always)]
140-
fn from(addr: Address) -> Self {
141-
Self::from_address(addr)
142-
}
143-
}
144-
145-
impl From<PodAddress> for Address {
146-
#[inline(always)]
147-
fn from(p: PodAddress) -> Self {
148-
p.to_address()
149-
}
150-
}
151-
152-
#[cfg(test)]
153-
mod tests {
154-
use super::*;
155-
156-
#[test]
157-
fn pod_u32_le_layout() {
158-
let p = PodU32::from_primitive(1);
159-
assert_eq!(p.0, [1, 0, 0, 0]);
160-
assert_eq!(p.get(), 1);
161-
162-
let x: u32 = p.into();
163-
assert_eq!(x, 1);
164-
165-
let p2: PodU32 = 42u32.into();
166-
assert_eq!(p2.get(), 42);
167-
}
168-
169-
#[test]
170-
fn pod_u64_le_layout() {
171-
let p = PodU64::from_primitive(1);
172-
assert_eq!(p.0, [1, 0, 0, 0, 0, 0, 0, 0]);
173-
assert_eq!(p.get(), 1);
174-
175-
let x: u64 = p.into();
176-
assert_eq!(x, 1);
177-
178-
let mut p2: PodU64 = 0u64.into();
179-
p2.set(u64::MAX);
180-
assert_eq!(p2.get(), u64::MAX);
181-
}
182-
183-
#[test]
184-
fn pod_i64_le_layout() {
185-
let p = PodI64::from_primitive(-1);
186-
assert_eq!(p.get(), -1);
187-
188-
let x: i64 = p.into();
189-
assert_eq!(x, -1);
190-
191-
let mut p2: PodI64 = 0i64.into();
192-
p2.set(i64::MIN);
193-
assert_eq!(p2.get(), i64::MIN);
194-
}
195-
196-
#[test]
197-
fn pod_address_roundtrip() {
198-
let bytes = [7u8; 32];
199-
let p = PodAddress::from_bytes(bytes);
200-
assert_eq!(p.as_bytes(), &bytes);
201-
202-
let addr = p.to_address();
203-
let p2 = PodAddress::from_address(addr);
204-
assert_eq!(p2.as_bytes(), &bytes);
205-
206-
let addr2: Address = p2.into();
207-
let p3: PodAddress = addr2.into();
208-
assert_eq!(p3.as_bytes(), &bytes);
209-
}
21079
}

pinocchio/interface/src/state/writer.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use {
1111
wincode::ZeroCopy,
1212
};
1313

14-
/// Mutable handle to stake account data.
14+
/// Mutable handle for stake account state transitions.
15+
#[derive(Debug)]
1516
pub struct StakeStateV2Writer<'a> {
1617
data: &'a mut [u8],
1718
}

0 commit comments

Comments
 (0)