Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit d9ae617

Browse files
committed
Add compute metering
1 parent e9b610b commit d9ae617

File tree

14 files changed

+281
-120
lines changed

14 files changed

+281
-120
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/bpf/Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/bpf/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ solana-bpf-loader-program = { path = "../bpf_loader", version = "1.4.0" }
2626
solana-logger = { path = "../../logger", version = "1.4.0" }
2727
solana-runtime = { path = "../../runtime", version = "1.4.0" }
2828
solana-sdk = { path = "../../sdk", version = "1.4.0" }
29-
solana_rbpf = "=0.1.28"
29+
solana_rbpf = "=0.1.30"
3030

3131
[[bench]]
3232
name = "bpf_loader"

programs/bpf/c/src/invoke/invoke.c

+27
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,33 @@ extern uint64_t entrypoint(const uint8_t *input) {
165165
SOL_ARRAY_SIZE(signers_seeds)));
166166
}
167167

168+
sol_log("Test multiple derived signers");
169+
{
170+
SolAccountMeta arguments[] = {
171+
{accounts[DERIVED_KEY1_INDEX].key, true, false},
172+
{accounts[DERIVED_KEY2_INDEX].key, true, true},
173+
{accounts[DERIVED_KEY3_INDEX].key, false, true}};
174+
uint8_t data[] = {TEST_VERIFY_NESTED_SIGNERS};
175+
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
176+
arguments, SOL_ARRAY_SIZE(arguments),
177+
data, SOL_ARRAY_SIZE(data)};
178+
uint8_t seed1[] = {'L', 'i', 'l', '\''};
179+
uint8_t seed2[] = {'B', 'i', 't', 's'};
180+
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
181+
{seed2, SOL_ARRAY_SIZE(seed2)},
182+
{&nonce2, 1}};
183+
const SolSignerSeed seeds2[] = {
184+
{(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY},
185+
{&nonce3, 1}};
186+
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
187+
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
188+
189+
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
190+
SOL_ARRAY_SIZE(accounts),
191+
signers_seeds,
192+
SOL_ARRAY_SIZE(signers_seeds)));
193+
}
194+
168195
sol_log("Test readonly with writable account");
169196
{
170197
SolAccountMeta arguments[] = {

programs/bpf/c/src/invoked/invoked.c

-25
Original file line numberDiff line numberDiff line change
@@ -97,31 +97,6 @@ extern uint64_t entrypoint(const uint8_t *input) {
9797
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
9898
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
9999

100-
uint8_t nonce2 = params.data[1];
101-
uint8_t nonce3 = params.data[2];
102-
103-
SolAccountMeta arguments[] = {
104-
{accounts[DERIVED_KEY1_INDEX].key, true, false},
105-
{accounts[DERIVED_KEY2_INDEX].key, true, true},
106-
{accounts[DERIVED_KEY3_INDEX].key, false, true}};
107-
uint8_t data[] = {TEST_VERIFY_NESTED_SIGNERS};
108-
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
109-
arguments, SOL_ARRAY_SIZE(arguments),
110-
data, SOL_ARRAY_SIZE(data)};
111-
uint8_t seed1[] = {'L', 'i', 'l', '\''};
112-
uint8_t seed2[] = {'B', 'i', 't', 's'};
113-
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
114-
{seed2, SOL_ARRAY_SIZE(seed2)},
115-
{&nonce2, 1}};
116-
const SolSignerSeed seeds2[] = {
117-
{(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY},
118-
{&nonce3, 1}};
119-
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
120-
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
121-
122-
sol_assert(SUCCESS == sol_invoke_signed(
123-
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
124-
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
125100
break;
126101
}
127102

programs/bpf/rust/invoke/src/lib.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,24 @@ fn process_instruction(
163163
accounts,
164164
&[&[b"You pass butter", &[nonce1]]],
165165
)?;
166+
167+
let invoked_instruction = create_instruction(
168+
*accounts[INVOKED_PROGRAM_INDEX].key,
169+
&[
170+
(accounts[DERIVED_KEY1_INDEX].key, true, false),
171+
(accounts[DERIVED_KEY2_INDEX].key, true, true),
172+
(accounts[DERIVED_KEY3_INDEX].key, false, true),
173+
],
174+
vec![TEST_VERIFY_NESTED_SIGNERS],
175+
);
176+
invoke_signed(
177+
&invoked_instruction,
178+
accounts,
179+
&[
180+
&[b"Lil'", b"Bits", &[nonce2]],
181+
&[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[nonce3]],
182+
],
183+
)?;
166184
}
167185

168186
info!("Test readonly with writable account");
@@ -188,20 +206,16 @@ fn process_instruction(
188206
&[
189207
(accounts[ARGUMENT_INDEX].key, true, true),
190208
(accounts[INVOKED_ARGUMENT_INDEX].key, true, true),
191-
(accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false),
192-
(accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false),
193209
],
194210
vec![TEST_NESTED_INVOKE],
195211
);
196212
invoke(&instruction, accounts)?;
197213
info!("2nd invoke from first program");
198214
invoke(&instruction, accounts)?;
199215

200-
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1 + 1 + 1);
201-
assert_eq!(
202-
accounts[INVOKED_ARGUMENT_INDEX].lamports(),
203-
10 + 5 - 1 - 1 - 1 - 1
204-
);
216+
info!(line!(), 0, 0, 0, accounts[ARGUMENT_INDEX].lamports());
217+
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1);
218+
assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].lamports(), 10 + 5 - 1 - 1);
205219
}
206220

207221
info!("Verify data values are retained and updated");

programs/bpf/rust/invoked/src/lib.rs

-20
Original file line numberDiff line numberDiff line change
@@ -119,26 +119,6 @@ fn process_instruction(
119119
assert!(accounts[DERIVED_KEY1_INDEX].is_signer);
120120
assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
121121
assert!(!accounts[DERIVED_KEY3_INDEX].is_signer);
122-
123-
let nonce2 = instruction_data[1];
124-
let nonce3 = instruction_data[2];
125-
let invoked_instruction = create_instruction(
126-
*accounts[INVOKED_PROGRAM_INDEX].key,
127-
&[
128-
(accounts[DERIVED_KEY1_INDEX].key, true, false),
129-
(accounts[DERIVED_KEY2_INDEX].key, true, true),
130-
(accounts[DERIVED_KEY3_INDEX].key, false, true),
131-
],
132-
vec![TEST_VERIFY_NESTED_SIGNERS],
133-
);
134-
invoke_signed(
135-
&invoked_instruction,
136-
accounts,
137-
&[
138-
&[b"Lil'", b"Bits", &[nonce2]],
139-
&[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[nonce3]],
140-
],
141-
)?;
142122
}
143123
TEST_VERIFY_NESTED_SIGNERS => {
144124
info!("verify nested derived signers");

programs/bpf/tests/programs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,9 @@ fn test_program_bpf_invoke() {
360360
let (derived_key1, nonce1) =
361361
Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id);
362362
let (derived_key2, nonce2) =
363-
Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id);
363+
Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoke_program_id);
364364
let (derived_key3, nonce3) =
365-
Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id);
365+
Pubkey::find_program_address(&[derived_key2.as_ref()], &invoke_program_id);
366366

367367
let mint_pubkey = mint_keypair.pubkey();
368368
let account_metas = vec![

programs/bpf_loader/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ num-derive = { version = "0.3" }
1515
num-traits = { version = "0.2" }
1616
solana-runtime = { path = "../../runtime", version = "1.4.0" }
1717
solana-sdk = { path = "../../sdk", version = "1.4.0" }
18-
solana_rbpf = "=0.1.28"
18+
solana_rbpf = "=0.1.30"
1919
thiserror = "1.0"
2020

2121
[dev-dependencies]

programs/bpf_loader/src/lib.rs

+84-19
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@ use num_derive::{FromPrimitive, ToPrimitive};
1414
use solana_rbpf::{
1515
ebpf::{EbpfError, UserDefinedError},
1616
memory_region::MemoryRegion,
17-
EbpfVm,
17+
EbpfVm, InstructionMeter,
1818
};
1919
use solana_sdk::{
2020
account::{is_executable, next_keyed_account, KeyedAccount},
2121
bpf_loader, bpf_loader_deprecated,
2222
decode_error::DecodeError,
2323
entrypoint::SUCCESS,
24-
entrypoint_native::InvokeContext,
24+
entrypoint_native::{ComputeMeter, InvokeContext},
2525
instruction::InstructionError,
2626
loader_instruction::LoaderInstruction,
2727
program_utils::limited_deserialize,
2828
pubkey::Pubkey,
2929
};
30+
use std::{cell::RefCell, rc::Rc};
3031
use thiserror::Error;
3132

3233
solana_sdk::declare_builtin!(
@@ -65,7 +66,6 @@ pub fn create_vm<'a>(
6566
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
6667
let mut vm = EbpfVm::new(None)?;
6768
vm.set_verifier(bpf_verifier::check)?;
68-
vm.set_max_instruction_count(100_000)?;
6969
vm.set_elf(&prog)?;
7070

7171
let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?;
@@ -105,6 +105,20 @@ macro_rules! log{
105105
};
106106
}
107107

108+
struct ThisInstructionMeter {
109+
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
110+
}
111+
impl InstructionMeter for ThisInstructionMeter {
112+
fn consume(&mut self, amount: u64) {
113+
// 1 to 1 instruction to compute unit mapping
114+
// ignore error, Ebpf will bail if exceeded
115+
let _ = self.compute_meter.borrow_mut().consume(amount);
116+
}
117+
fn get_remaining(&self) -> u64 {
118+
self.compute_meter.borrow().get_remaining()
119+
}
120+
}
121+
108122
pub fn process_instruction(
109123
program_id: &Pubkey,
110124
keyed_accounts: &[KeyedAccount],
@@ -132,6 +146,7 @@ pub fn process_instruction(
132146
&instruction_data,
133147
)?;
134148
{
149+
let compute_meter = invoke_context.get_compute_meter();
135150
let program_account = program.try_account_ref_mut()?;
136151
let (mut vm, heap_region) =
137152
match create_vm(&program_account.data, &parameter_accounts, invoke_context) {
@@ -143,7 +158,13 @@ pub fn process_instruction(
143158
};
144159

145160
log!(logger, "Call BPF program {}", program.unsigned_key());
146-
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
161+
let instruction_meter = ThisInstructionMeter { compute_meter };
162+
match vm.execute_program_metered(
163+
parameter_bytes.as_slice(),
164+
&[],
165+
&[heap_region],
166+
instruction_meter,
167+
) {
147168
Ok(status) => {
148169
if status != SUCCESS {
149170
let error: InstructionError = status.into();
@@ -228,17 +249,57 @@ mod tests {
228249
use rand::Rng;
229250
use solana_sdk::{
230251
account::Account,
231-
entrypoint_native::{Logger, ProcessInstruction},
252+
entrypoint_native::{ComputeMeter, Logger, ProcessInstruction},
232253
instruction::CompiledInstruction,
233254
message::Message,
234255
rent::Rent,
235256
};
236257
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc};
237258

238-
#[derive(Debug, Default)]
259+
#[derive(Debug, Default, Clone)]
260+
pub struct MockComputeMeter {
261+
pub remaining: u64,
262+
}
263+
impl ComputeMeter for MockComputeMeter {
264+
fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
265+
self.remaining = self.remaining.saturating_sub(amount);
266+
if self.remaining == 0 {
267+
return Err(InstructionError::ComputationalBudgetExceeded);
268+
}
269+
Ok(())
270+
}
271+
fn get_remaining(&self) -> u64 {
272+
self.remaining
273+
}
274+
}
275+
#[derive(Debug, Default, Clone)]
276+
pub struct MockLogger {
277+
pub log: Rc<RefCell<Vec<String>>>,
278+
}
279+
impl Logger for MockLogger {
280+
fn log_enabled(&self) -> bool {
281+
true
282+
}
283+
fn log(&mut self, message: &str) {
284+
self.log.borrow_mut().push(message.to_string());
285+
}
286+
}
287+
#[derive(Debug)]
239288
pub struct MockInvokeContext {
240-
key: Pubkey,
241-
mock_logger: MockLogger,
289+
pub key: Pubkey,
290+
pub logger: MockLogger,
291+
pub compute_meter: MockComputeMeter,
292+
}
293+
impl Default for MockInvokeContext {
294+
fn default() -> Self {
295+
MockInvokeContext {
296+
key: Pubkey::default(),
297+
logger: MockLogger::default(),
298+
compute_meter: MockComputeMeter {
299+
remaining: std::u64::MAX,
300+
},
301+
}
302+
}
242303
}
243304
impl InvokeContext for MockInvokeContext {
244305
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
@@ -260,22 +321,25 @@ mod tests {
260321
&[]
261322
}
262323
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
263-
Rc::new(RefCell::new(self.mock_logger.clone()))
324+
Rc::new(RefCell::new(self.logger.clone()))
264325
}
265326
fn is_cross_program_supported(&self) -> bool {
266327
true
267328
}
329+
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
330+
Rc::new(RefCell::new(self.compute_meter.clone()))
331+
}
268332
}
269-
#[derive(Debug, Default, Clone)]
270-
pub struct MockLogger {
271-
pub log: Rc<RefCell<Vec<String>>>,
333+
334+
struct TestInstructionMeter {
335+
remaining: u64,
272336
}
273-
impl Logger for MockLogger {
274-
fn log_enabled(&self) -> bool {
275-
true
337+
impl InstructionMeter for TestInstructionMeter {
338+
fn consume(&mut self, amount: u64) {
339+
self.remaining = self.remaining.saturating_sub(amount);
276340
}
277-
fn log(&mut self, message: &str) {
278-
self.log.borrow_mut().push(message.to_string());
341+
fn get_remaining(&self) -> u64 {
342+
self.remaining
279343
}
280344
}
281345

@@ -292,9 +356,10 @@ mod tests {
292356

293357
let mut vm = EbpfVm::<BPFError>::new(None).unwrap();
294358
vm.set_verifier(bpf_verifier::check).unwrap();
295-
vm.set_max_instruction_count(10).unwrap();
359+
let instruction_meter = TestInstructionMeter { remaining: 10 };
296360
vm.set_program(program).unwrap();
297-
vm.execute_program(input, &[], &[]).unwrap();
361+
vm.execute_program_metered(input, &[], &[], instruction_meter)
362+
.unwrap();
298363
}
299364

300365
#[test]

0 commit comments

Comments
 (0)