Skip to content

Commit ac2a9d3

Browse files
authored
processor: allocate (#40)
* processor: allocate * end the fn with realloc result * refactor addresses helper to eliminate hash set
1 parent db4b96c commit ac2a9d3

File tree

7 files changed

+372
-6
lines changed

7 files changed

+372
-6
lines changed

Cargo.lock

Lines changed: 110 additions & 0 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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ license = "Apache-2.0"
1414
edition = "2021"
1515

1616
[workspace.dependencies]
17+
solana-account = "2.1"
1718
solana-account-info = "2.1"
19+
solana-bincode = "2.1"
1820
solana-cpi = "2.1"
1921
solana-decode-error = "2.1"
2022
solana-frozen-abi = "2.1"

interface/src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use {
22
num_traits::{FromPrimitive, ToPrimitive},
33
solana_decode_error::DecodeError,
44
solana_msg::msg,
5-
solana_program_error::PrintProgramError,
5+
solana_program_error::{PrintProgramError, ProgramError},
66
};
77

88
// Use strum when testing to ensure our FromPrimitive
@@ -128,6 +128,12 @@ impl PrintProgramError for SystemError {
128128
}
129129
}
130130

131+
impl From<SystemError> for ProgramError {
132+
fn from(e: SystemError) -> Self {
133+
Self::Custom(e as u32)
134+
}
135+
}
136+
131137
impl<T> DecodeError<T> for SystemError {
132138
fn type_of() -> &'static str {
133139
"SystemError"

program/Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ bpf-entrypoint = []
1717

1818
[dependencies]
1919
solana-account-info = { workspace = true }
20+
solana-bincode = { workspace = true }
21+
solana-msg = { workspace = true }
2022
solana-program-entrypoint = { workspace = true }
2123
solana-program-error = { workspace = true }
22-
solana-pubkey = { workspace = true }
23-
solana-system-interface = { path = "../interface" }
24+
solana-pubkey = { workspace = true, features = ["sha2"] }
25+
solana-system-interface = { path = "../interface", features = ["serde"] }
2426

2527
[dev-dependencies]
28+
mollusk-svm = "0.0.15"
29+
solana-account = { workspace = true }
30+
solana-system-interface = { path = "../interface", features = ["bincode"] }
2631

2732
[lib]
2833
crate-type = ["cdylib"]

program/src/processor.rs

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,132 @@
11
//! Program processor.
22
33
use {
4-
solana_account_info::AccountInfo, solana_program_error::ProgramResult, solana_pubkey::Pubkey,
4+
solana_account_info::AccountInfo,
5+
solana_msg::msg,
6+
solana_program_error::{ProgramError, ProgramResult},
7+
solana_pubkey::Pubkey,
8+
solana_system_interface::{
9+
error::SystemError, instruction::SystemInstruction, MAX_PERMITTED_DATA_LENGTH,
10+
},
511
};
612

7-
pub fn process(_program_id: &Pubkey, _accounts: &[AccountInfo], _input: &[u8]) -> ProgramResult {
8-
Ok(())
13+
// Maximum input buffer length that can be deserialized.
14+
// https://github.com/anza-xyz/solana-sdk/blob/41c663a8ec7269aa1117a2e3b0e24ff9ee1ac4af/packet/src/lib.rs#L32
15+
const MAX_INPUT_LEN: u64 = 1232;
16+
17+
macro_rules! accounts {
18+
( $infos:ident, $( $i:literal => $name:ident ),* $(,)? ) => {
19+
$(
20+
let $name = $infos.get($i).ok_or(ProgramError::NotEnoughAccountKeys)?;
21+
)*
22+
};
23+
}
24+
25+
// Represents an address that may or may not have been generated from a seed.
26+
struct AddressInfo<'a, 'b> {
27+
info: &'a AccountInfo<'b>,
28+
base: Option<&'a AccountInfo<'b>>,
29+
}
30+
31+
impl std::fmt::Debug for AddressInfo<'_, '_> {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
f.debug_struct("AddressInfo")
34+
.field("address", &self.info.key)
35+
.field("base", &self.base.map(|info| info.key))
36+
.finish()
37+
}
38+
}
39+
40+
impl<'a, 'b> AddressInfo<'a, 'b> {
41+
fn is_signer(&self) -> bool {
42+
if let Some(base) = self.base {
43+
base.is_signer
44+
} else {
45+
self.info.is_signer
46+
}
47+
}
48+
49+
fn create(
50+
info: &'a AccountInfo<'b>,
51+
with_seed: Option<(&'a AccountInfo<'b>, &str, &Pubkey)>,
52+
) -> Result<Self, ProgramError> {
53+
let base = if let Some((base, seed, owner)) = with_seed {
54+
// Re-derive the address. It must match the supplied address.
55+
let address_with_seed = Pubkey::create_with_seed(base.key, seed, owner)?;
56+
if *info.key != address_with_seed {
57+
msg!(
58+
"Create: address {} does not match derived address {}",
59+
info.key,
60+
address_with_seed
61+
);
62+
Err(SystemError::AddressWithSeedMismatch)?
63+
}
64+
Some(base)
65+
} else {
66+
None
67+
};
68+
69+
Ok(Self { info, base })
70+
}
71+
}
72+
73+
fn allocate(info: &AccountInfo, address: &AddressInfo, space: u64) -> Result<(), ProgramError> {
74+
if !address.is_signer() {
75+
msg!("Allocate: 'to' account {:?} must sign", address);
76+
Err(ProgramError::MissingRequiredSignature)?
77+
}
78+
79+
// If it looks like the account is already in use, bail.
80+
if !info.data_is_empty() || !solana_system_interface::program::check_id(info.owner) {
81+
msg!("Allocate: account {:?} already in use", address);
82+
Err(SystemError::AccountAlreadyInUse)?
83+
}
84+
85+
if space > MAX_PERMITTED_DATA_LENGTH {
86+
msg!(
87+
"Allocate: requested {}, max allowed {}",
88+
space,
89+
MAX_PERMITTED_DATA_LENGTH
90+
);
91+
Err(SystemError::InvalidAccountDataLength)?
92+
}
93+
94+
/*
95+
[TODO: CORE_BPF]:
96+
97+
This is going to behave differently than the builtin program right now,
98+
since reallocations are limited to `MAX_PERMITTED_DATA_INCREASE``, which is
99+
smaller than `MAX_PERMITTED_DATA_LENGTH`.
100+
101+
* `MAX_PERMITTED_DATA_LENGTH` : 1_024 * 10 * 1_024
102+
* `MAX_PERMITTED_DATA_INCREASE` : 1_024 * 10
103+
104+
https://github.com/solana-program/system/issues/30
105+
*/
106+
info.realloc(space as usize, true)
107+
}
108+
109+
fn process_allocate(accounts: &[AccountInfo], space: u64) -> ProgramResult {
110+
accounts!(
111+
accounts,
112+
0 => account_info,
113+
);
114+
allocate(
115+
account_info,
116+
&AddressInfo::create(account_info, None)?,
117+
space,
118+
)
119+
}
120+
121+
pub fn process(_program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
122+
match solana_bincode::limited_deserialize::<SystemInstruction>(input, MAX_INPUT_LEN)
123+
.map_err(|_| ProgramError::InvalidInstructionData)?
124+
{
125+
SystemInstruction::Allocate { space } => {
126+
msg!("Instruction: Allocate");
127+
process_allocate(accounts, space)
128+
}
129+
/* TODO: Remaining instruction implementations... */
130+
_ => Err(ProgramError::InvalidInstructionData),
131+
}
9132
}

0 commit comments

Comments
 (0)