Skip to content

Commit eef358e

Browse files
authored
Add interface crate (#17)
* Use individual dependencies * Add interface crate * Review comments * Rearrange dependencies * Update dependencies
1 parent 08122b3 commit eef358e

File tree

11 files changed

+1144
-490
lines changed

11 files changed

+1144
-490
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
resolver = "2"
33
members = [
44
"clients/rust",
5+
"interface",
56
"program",
67
]
78

89
[workspace.metadata.cli]
9-
solana = "edge"
10+
solana = "2.1.0"
1011

11-
# Specify Rust toolchains for rustfmt, clippy, and build.
12-
# Any unprovided toolchains default to stable.
1312
[workspace.metadata.toolchains]
1413
format = "nightly-2024-08-08"
1514
lint = "nightly-2024-08-08"

interface/Cargo.toml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[package]
2+
name = "solana-stake-interface"
3+
version = "0.1.0"
4+
description = "Instructions and constructors for the Stake program"
5+
repository = "https://github.com/solana-program/stake"
6+
edition = "2021"
7+
readme = "README.md"
8+
license-file = "../LICENSE"
9+
10+
[dependencies]
11+
borsh = { version = "1.5.1", features = ["derive", "unstable__schema"], optional = true }
12+
borsh0-10 = { package = "borsh", version = "0.10.3", optional = true }
13+
num-traits = "0.2"
14+
serde = { version = "1.0.210", optional = true }
15+
serde_derive = { version = "1.0.210", optional = true }
16+
solana-decode-error = "^2.1"
17+
solana-clock = "^2.1"
18+
solana-cpi = { version = "^2.1", optional = true }
19+
solana-frozen-abi = { version = "^2.1", features = ["frozen-abi"], optional = true }
20+
solana-frozen-abi-macro = { version = "^2.1", features = ["frozen-abi"], optional = true }
21+
solana-instruction = "^2.1"
22+
solana-program-error = { version = "^2.1", optional = true }
23+
solana-pubkey = { version = "^2.1", default-features = false }
24+
solana-system-interface = "^1.0"
25+
26+
[dev-dependencies]
27+
assert_matches = "1.5.0"
28+
bincode = "1.3.3"
29+
solana-borsh = "^2.1"
30+
solana-program = { version = "^2.1", default-features = false }
31+
static_assertions = "1.1.0"
32+
strum = "0.24"
33+
strum_macros = "0.24"
34+
35+
[package.metadata.docs.rs]
36+
targets = ["x86_64-unknown-linux-gnu"]
37+
all-features = true
38+
rustdoc-args = ["--cfg=docsrs"]
39+
40+
[features]
41+
bincode = [
42+
"dep:solana-cpi",
43+
"dep:solana-program-error",
44+
"solana-instruction/bincode",
45+
"solana-instruction/serde",
46+
"serde"
47+
]
48+
borsh = [
49+
"dep:borsh",
50+
"dep:borsh0-10",
51+
"solana-instruction/borsh",
52+
"solana-program-error/borsh",
53+
"solana-pubkey/borsh"
54+
]
55+
frozen-abi = [
56+
"dep:solana-frozen-abi",
57+
"dep:solana-frozen-abi-macro",
58+
"solana-instruction/frozen-abi",
59+
"solana-pubkey/frozen-abi"
60+
]
61+
serde = ["dep:serde", "dep:serde_derive", "solana-pubkey/serde"]

interface/src/config.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
//! config for staking
2-
//! carries variables that the stake program cares about
1+
//! Config for staking.
2+
//!
3+
//! It carries variables that the stake program cares about.
34
45
#[deprecated(
56
since = "1.16.7",
6-
note = "Please use `solana_sdk::stake::state::{DEFAULT_SLASH_PENALTY, DEFAULT_WARMUP_COOLDOWN_RATE}` instead"
7+
note = "Please use `crate::state::{DEFAULT_SLASH_PENALTY, DEFAULT_WARMUP_COOLDOWN_RATE}` instead"
78
)]
89
pub use super::state::{DEFAULT_SLASH_PENALTY, DEFAULT_WARMUP_COOLDOWN_RATE};
9-
use serde_derive::{Deserialize, Serialize};
10+
use solana_pubkey::declare_deprecated_id;
1011

1112
// stake config ID
12-
crate::declare_deprecated_id!("StakeConfig11111111111111111111111111111111");
13+
declare_deprecated_id!("StakeConfig11111111111111111111111111111111");
1314

1415
#[deprecated(
1516
since = "1.16.7",
16-
note = "Please use `solana_sdk::stake::state::warmup_cooldown_rate()` instead"
17+
note = "Please use `crate::state::warmup_cooldown_rate()` instead"
1718
)]
18-
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
19+
#[cfg_attr(
20+
feature = "serde",
21+
derive(serde_derive::Deserialize, serde_derive::Serialize)
22+
)]
23+
#[derive(Debug, PartialEq, Clone, Copy)]
1924
pub struct Config {
2025
/// how much stake we can activate/deactivate per-epoch as a fraction of currently effective stake
2126
pub warmup_cooldown_rate: f64,

interface/src/error.rs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
use {
2+
num_traits::{FromPrimitive, ToPrimitive},
3+
solana_decode_error::DecodeError,
4+
};
5+
6+
/// Reasons the Stake might have had an error.
7+
#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
8+
#[cfg_attr(
9+
feature = "serde",
10+
derive(serde_derive::Deserialize, serde_derive::Serialize)
11+
)]
12+
#[derive(Clone, Debug, PartialEq, Eq)]
13+
pub enum StakeError {
14+
// 0
15+
/// Not enough credits to redeem.
16+
NoCreditsToRedeem,
17+
18+
/// Lockup has not yet expired.
19+
LockupInForce,
20+
21+
/// Stake already deactivated.
22+
AlreadyDeactivated,
23+
24+
/// One re-delegation permitted per epoch.
25+
TooSoonToRedelegate,
26+
27+
/// Split amount is more than is staked.
28+
InsufficientStake,
29+
30+
// 5
31+
/// Stake account with transient stake cannot be merged.
32+
MergeTransientStake,
33+
34+
/// Stake account merge failed due to different authority, lockups or state.
35+
MergeMismatch,
36+
37+
/// Custodian address not present.
38+
CustodianMissing,
39+
40+
/// Custodian signature not present.
41+
CustodianSignatureMissing,
42+
43+
/// Insufficient voting activity in the reference vote account.
44+
InsufficientReferenceVotes,
45+
46+
// 10
47+
/// Stake account is not delegated to the provided vote account.
48+
VoteAddressMismatch,
49+
50+
/// Stake account has not been delinquent for the minimum epochs required
51+
/// for deactivation.
52+
MinimumDelinquentEpochsForDeactivationNotMet,
53+
54+
/// Delegation amount is less than the minimum.
55+
InsufficientDelegation,
56+
57+
/// Stake account with transient or inactive stake cannot be redelegated.
58+
RedelegateTransientOrInactiveStake,
59+
60+
/// Stake redelegation to the same vote account is not permitted.
61+
RedelegateToSameVoteAccount,
62+
63+
// 15
64+
/// Redelegated stake must be fully activated before deactivation.
65+
RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted,
66+
67+
/// Stake action is not permitted while the epoch rewards period is active.
68+
EpochRewardsActive,
69+
}
70+
71+
impl FromPrimitive for StakeError {
72+
#[inline]
73+
fn from_i64(n: i64) -> Option<Self> {
74+
if n == Self::NoCreditsToRedeem as i64 {
75+
Some(Self::NoCreditsToRedeem)
76+
} else if n == Self::LockupInForce as i64 {
77+
Some(Self::LockupInForce)
78+
} else if n == Self::AlreadyDeactivated as i64 {
79+
Some(Self::AlreadyDeactivated)
80+
} else if n == Self::TooSoonToRedelegate as i64 {
81+
Some(Self::TooSoonToRedelegate)
82+
} else if n == Self::InsufficientStake as i64 {
83+
Some(Self::InsufficientStake)
84+
} else if n == Self::MergeTransientStake as i64 {
85+
Some(Self::MergeTransientStake)
86+
} else if n == Self::MergeMismatch as i64 {
87+
Some(Self::MergeMismatch)
88+
} else if n == Self::CustodianMissing as i64 {
89+
Some(Self::CustodianMissing)
90+
} else if n == Self::CustodianSignatureMissing as i64 {
91+
Some(Self::CustodianSignatureMissing)
92+
} else if n == Self::InsufficientReferenceVotes as i64 {
93+
Some(Self::InsufficientReferenceVotes)
94+
} else if n == Self::VoteAddressMismatch as i64 {
95+
Some(Self::VoteAddressMismatch)
96+
} else if n == Self::MinimumDelinquentEpochsForDeactivationNotMet as i64 {
97+
Some(Self::MinimumDelinquentEpochsForDeactivationNotMet)
98+
} else if n == Self::InsufficientDelegation as i64 {
99+
Some(Self::InsufficientDelegation)
100+
} else if n == Self::RedelegateTransientOrInactiveStake as i64 {
101+
Some(Self::RedelegateTransientOrInactiveStake)
102+
} else if n == Self::RedelegateToSameVoteAccount as i64 {
103+
Some(Self::RedelegateToSameVoteAccount)
104+
} else if n == Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64 {
105+
Some(Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted)
106+
} else if n == Self::EpochRewardsActive as i64 {
107+
Some(Self::EpochRewardsActive)
108+
} else {
109+
None
110+
}
111+
}
112+
#[inline]
113+
fn from_u64(n: u64) -> Option<Self> {
114+
Self::from_i64(n as i64)
115+
}
116+
}
117+
118+
impl ToPrimitive for StakeError {
119+
#[inline]
120+
fn to_i64(&self) -> Option<i64> {
121+
Some(match *self {
122+
Self::NoCreditsToRedeem => Self::NoCreditsToRedeem as i64,
123+
Self::LockupInForce => Self::LockupInForce as i64,
124+
Self::AlreadyDeactivated => Self::AlreadyDeactivated as i64,
125+
Self::TooSoonToRedelegate => Self::TooSoonToRedelegate as i64,
126+
Self::InsufficientStake => Self::InsufficientStake as i64,
127+
Self::MergeTransientStake => Self::MergeTransientStake as i64,
128+
Self::MergeMismatch => Self::MergeMismatch as i64,
129+
Self::CustodianMissing => Self::CustodianMissing as i64,
130+
Self::CustodianSignatureMissing => Self::CustodianSignatureMissing as i64,
131+
Self::InsufficientReferenceVotes => Self::InsufficientReferenceVotes as i64,
132+
Self::VoteAddressMismatch => Self::VoteAddressMismatch as i64,
133+
Self::MinimumDelinquentEpochsForDeactivationNotMet => {
134+
Self::MinimumDelinquentEpochsForDeactivationNotMet as i64
135+
}
136+
Self::InsufficientDelegation => Self::InsufficientDelegation as i64,
137+
Self::RedelegateTransientOrInactiveStake => {
138+
Self::RedelegateTransientOrInactiveStake as i64
139+
}
140+
Self::RedelegateToSameVoteAccount => Self::RedelegateToSameVoteAccount as i64,
141+
Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
142+
Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64
143+
}
144+
Self::EpochRewardsActive => Self::EpochRewardsActive as i64,
145+
})
146+
}
147+
#[inline]
148+
fn to_u64(&self) -> Option<u64> {
149+
self.to_i64().map(|x| x as u64)
150+
}
151+
}
152+
153+
impl std::error::Error for StakeError {}
154+
155+
impl core::fmt::Display for StakeError {
156+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
157+
match self {
158+
StakeError::NoCreditsToRedeem => f.write_str("not enough credits to redeem"),
159+
StakeError::LockupInForce => f.write_str("lockup has not yet expired"),
160+
StakeError::AlreadyDeactivated => f.write_str("stake already deactivated"),
161+
StakeError::TooSoonToRedelegate => f.write_str("one re-delegation permitted per epoch"),
162+
StakeError::InsufficientStake => f.write_str("split amount is more than is staked"),
163+
StakeError::MergeTransientStake => {
164+
f.write_str("stake account with transient stake cannot be merged")
165+
}
166+
StakeError::MergeMismatch => f.write_str(
167+
"stake account merge failed due to different authority, lockups or state",
168+
),
169+
StakeError::CustodianMissing => f.write_str("custodian address not present"),
170+
StakeError::CustodianSignatureMissing => f.write_str("custodian signature not present"),
171+
StakeError::InsufficientReferenceVotes => {
172+
f.write_str("insufficient voting activity in the reference vote account")
173+
}
174+
StakeError::VoteAddressMismatch => {
175+
f.write_str("stake account is not delegated to the provided vote account")
176+
}
177+
StakeError::MinimumDelinquentEpochsForDeactivationNotMet => f.write_str(
178+
"stake account has not been delinquent for the minimum epochs required for \
179+
deactivation",
180+
),
181+
StakeError::InsufficientDelegation => {
182+
f.write_str("delegation amount is less than the minimum")
183+
}
184+
StakeError::RedelegateTransientOrInactiveStake => {
185+
f.write_str("stake account with transient or inactive stake cannot be redelegated")
186+
}
187+
StakeError::RedelegateToSameVoteAccount => {
188+
f.write_str("stake redelegation to the same vote account is not permitted")
189+
}
190+
StakeError::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
191+
f.write_str("redelegated stake must be fully activated before deactivation")
192+
}
193+
StakeError::EpochRewardsActive => f.write_str(
194+
"stake action is not permitted while the epoch rewards period is active",
195+
),
196+
}
197+
}
198+
}
199+
200+
impl<E> DecodeError<E> for StakeError {
201+
fn type_of() -> &'static str {
202+
"StakeError"
203+
}
204+
}
205+
206+
#[cfg(test)]
207+
mod tests {
208+
use {
209+
super::StakeError, num_traits::FromPrimitive, solana_decode_error::DecodeError,
210+
solana_instruction::error::InstructionError, strum::IntoEnumIterator,
211+
};
212+
213+
#[test]
214+
fn test_stake_error_from_primitive_exhaustive() {
215+
for variant in StakeError::iter() {
216+
let variant_i64 = variant.clone() as i64;
217+
assert_eq!(
218+
StakeError::from_repr(variant_i64 as usize),
219+
StakeError::from_i64(variant_i64)
220+
);
221+
}
222+
}
223+
224+
#[test]
225+
fn test_custom_error_decode() {
226+
use num_traits::FromPrimitive;
227+
fn pretty_err<T>(err: InstructionError) -> String
228+
where
229+
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
230+
{
231+
if let InstructionError::Custom(code) = err {
232+
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
233+
format!(
234+
"{:?}: {}::{:?} - {}",
235+
err,
236+
T::type_of(),
237+
specific_error,
238+
specific_error,
239+
)
240+
} else {
241+
"".to_string()
242+
}
243+
}
244+
assert_eq!(
245+
"Custom(0): StakeError::NoCreditsToRedeem - not enough credits to redeem",
246+
pretty_err::<StakeError>(StakeError::NoCreditsToRedeem.into())
247+
)
248+
}
249+
}

0 commit comments

Comments
 (0)