Skip to content

Commit 1ab435f

Browse files
authored
feat: add active heap management (Lightprotocol#393)
- add heap-neutral proc macro which frees all heap memory allocated during the use of a method at the end of the method - the macro has a check that it can only be used with methods which do not modify its struct
1 parent 01caa5b commit 1ab435f

File tree

19 files changed

+423
-103
lines changed

19 files changed

+423
-103
lines changed

Cargo.lock

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

macros/light/src/lib.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use proc_macro::TokenStream;
2-
use syn::{parse_macro_input, ItemStruct};
2+
use quote::quote;
3+
use syn::{parse_macro_input, parse_quote, FnArg, ItemFn, ItemStruct, Receiver, Signature};
34

45
mod expand;
56

@@ -23,3 +24,48 @@ pub fn light_verifier_accounts(attr: TokenStream, item: TokenStream) -> TokenStr
2324
.unwrap_or_else(|err| err.to_compile_error())
2425
.into()
2526
}
27+
28+
#[proc_macro_attribute]
29+
pub fn heap_neutral(_: TokenStream, input: TokenStream) -> TokenStream {
30+
let mut function = parse_macro_input!(input as ItemFn);
31+
32+
// Check if the function signature uses `&self` and not `&mut self`
33+
if !is_immutable_self(&function.sig) {
34+
return syn::Error::new_spanned(
35+
&function.sig,
36+
"This macro requires the function to use `&self` and not `&mut self`",
37+
)
38+
.to_compile_error()
39+
.into();
40+
}
41+
42+
// Insert memory management code at the beginning of the function
43+
let init_code: syn::Stmt = parse_quote! {
44+
#[cfg(target_os = "solana")]
45+
let pos = custom_heap::get_heap_pos();
46+
};
47+
function.block.stmts.insert(0, init_code);
48+
49+
// Insert memory management code at the end of the function
50+
let cleanup_code: syn::Stmt = parse_quote! {
51+
#[cfg(target_os = "solana")]
52+
custom_heap::free_heap(pos);
53+
};
54+
let len = function.block.stmts.len();
55+
function.block.stmts.insert(len - 1, cleanup_code);
56+
57+
TokenStream::from(quote! { #function })
58+
}
59+
60+
fn is_immutable_self(signature: &Signature) -> bool {
61+
signature.inputs.iter().any(|arg| {
62+
matches!(
63+
arg,
64+
FnArg::Receiver(Receiver {
65+
reference: Some(_),
66+
mutability: None,
67+
..
68+
})
69+
)
70+
})
71+
}

programs/psp10in2out/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ no-entrypoint = []
1414
no-idl = []
1515
no-log-ix-name = []
1616
cpi = ["no-entrypoint"]
17-
default = []
17+
default = ["custom-heap", "memory-test"]
18+
custom-heap = []
19+
memory-test = []
1820

1921
[dependencies]
2022
anchor-lang = "0.28.0"
@@ -26,7 +28,7 @@ solana-security-txt = "1.1.0"
2628
groth16-solana = { git= "https://github.com/Lightprotocol/groth16-solana", branch="master"}
2729

2830
light-macros = { version = "0.3.1", path = "../../macros/light" }
29-
light-verifier-sdk = { version = "0.3.1", path = "../../verifier-sdk" }
31+
light-verifier-sdk = { version = "0.3.1", path = "../../verifier-sdk", features = ["custom-heap", "mem-profiling"] }
3032

3133
# TODO: Remove once https://github.com/solana-labs/solana/issues/33504 is resolved.
3234
ahash = "=0.8.6"

programs/psp10in2out/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,17 @@ pub mod light_psp10in2out {
136136
};
137137
let mut tx =
138138
Transaction::<0, 2, 10, 17, LightInstructionSecond<'info, 0, 2, 10>>::new(input);
139-
tx.transact()
139+
tx.transact()?;
140+
141+
#[cfg(all(feature = "memory-test", target_os = "solana"))]
142+
assert!(
143+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check")
144+
< 7000u64,
145+
"memory degression detected {} {}",
146+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check"),
147+
7000u64
148+
);
149+
Ok(())
140150
}
141151

142152
/// Close the verifier state to reclaim rent in case the proofdata is wrong and does not verify.

programs/psp2in2out-storage/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ no-entrypoint = []
1414
no-idl = []
1515
no-log-ix-name = []
1616
cpi = ["no-entrypoint"]
17-
default = []
17+
default = ["custom-heap", "memory-test"]
18+
custom-heap = []
19+
memory-test = []
1820

1921
[dependencies]
2022
anchor-lang = { version = "0.28.0", features = ["init-if-needed"] }

programs/psp2in2out-storage/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,17 @@ pub mod light_psp2in2out_storage {
115115
};
116116
let mut transaction = Transaction::<0, 2, 2, 9, LightInstructionSecond<'info>>::new(input);
117117

118-
transaction.transact()
118+
transaction.transact()?;
119+
120+
#[cfg(all(feature = "memory-test", target_os = "solana"))]
121+
assert!(
122+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check")
123+
< 5000u64,
124+
"memory degression detected {} {}",
125+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check"),
126+
5000u64
127+
);
128+
Ok(())
119129
}
120130
}
121131

programs/psp2in2out/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ crate-type = ["cdylib", "lib"]
1010
name = "light_psp2in2out"
1111

1212
[features]
13+
default = ["custom-heap", "memory-test"]
14+
custom-heap = []
15+
memory-test = []
1316
no-entrypoint = []
1417
no-idl = []
1518
no-log-ix-name = []
1619
cpi = ["no-entrypoint"]
17-
default = []
1820

1921
[dependencies]
2022
anchor-lang = "0.28.0"
@@ -25,7 +27,7 @@ solana-security-txt = "1.1.0"
2527
# Light Deps
2628
groth16-solana = { git= "https://github.com/Lightprotocol/groth16-solana", branch="master"}
2729
light-macros = { version = "0.3.1", path = "../../macros/light" }
28-
light-verifier-sdk = { version = "0.3.1", path = "../../verifier-sdk" }
30+
light-verifier-sdk = { version = "0.3.1", path = "../../verifier-sdk" , features = ["custom-heap", "mem-profiling"] }
2931

3032
# TODO: Remove once https://github.com/solana-labs/solana/issues/33504 is resolved.
3133
ahash = "=0.8.6"

programs/psp2in2out/src/lib.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use light_macros::light_verifier_accounts;
33
use light_verifier_sdk::light_transaction::{
44
Amounts, ProofCompressed, Transaction, TransactionInput,
55
};
6+
use verifying_key::VERIFYINGKEY_TRANSACTION_MASP2_MAIN;
67

78
pub mod verifying_key;
8-
use verifying_key::VERIFYINGKEY_TRANSACTION_MASP2_MAIN;
99

1010
declare_id!("J1RRetZ4ujphU75LP8RadjXMf3sA12yC2R44CF7PmU7i");
1111

@@ -39,6 +39,7 @@ pub mod light_psp2in2out {
3939
InstructionDataCompressedTransferFirst::try_deserialize_unchecked(
4040
&mut [vec![0u8; 8], inputs].concat().as_slice(),
4141
)?;
42+
4243
let len_missing_bytes = 256 - inputs.encrypted_utxos.len();
4344
let mut enc_utxos = inputs.encrypted_utxos;
4445
enc_utxos.append(&mut vec![0u8; len_missing_bytes]);
@@ -68,7 +69,17 @@ pub mod light_psp2in2out {
6869
};
6970
let mut transaction = Transaction::<0, 2, 2, 9, LightInstruction<'info>>::new(input);
7071

71-
transaction.transact()
72+
transaction.transact()?;
73+
74+
#[cfg(all(feature = "memory-test", target_os = "solana"))]
75+
assert!(
76+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check")
77+
< 6000u64,
78+
"memory degression detected {} {}",
79+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check"),
80+
6000u64
81+
);
82+
Ok(())
7283
}
7384
}
7485

programs/psp4in4out-app-storage/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ no-entrypoint = []
1414
no-idl = []
1515
no-log-ix-name = []
1616
cpi = ["no-entrypoint"]
17-
default = []
17+
default = ["custom-heap", "memory-test"]
18+
custom-heap = []
19+
memory-test = []
1820

1921
[dependencies]
2022
anchor-lang = "0.28.0"

programs/psp4in4out-app-storage/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,17 @@ pub mod light_psp4in4out_app_storage {
9292
};
9393
let mut tx = Transaction::<2, 4, 4, 15, LightInstruction<'info>>::new(input);
9494

95-
tx.transact()
95+
tx.transact()?;
96+
97+
#[cfg(all(feature = "memory-test", target_os = "solana"))]
98+
assert!(
99+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check")
100+
< 7000u64,
101+
"memory degression detected {} {}",
102+
light_verifier_sdk::light_transaction::custom_heap::log_total_heap("memory_check"),
103+
7000u64
104+
);
105+
Ok(())
96106
}
97107
}
98108

0 commit comments

Comments
 (0)