Skip to content

Commit

Permalink
Add return data
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Sep 1, 2021
1 parent c550b32 commit 99e1f45
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions program-runtime/src/instruction_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,9 @@ impl InstructionProcessor {
let keyed_accounts =
Self::create_keyed_accounts(message, instruction, executable_accounts, accounts);

// clear the return data
invoke_context.set_return_data(&[]);

// Invoke callee
invoke_context.push(program_id, &keyed_accounts)?;

Expand Down
1 change: 1 addition & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@ impl Executor for BpfExecutor {
let trace_string = String::from_utf8(trace_buffer).unwrap();
trace!("BPF Program Instruction Trace:\n{}", trace_string);
}
drop(vm);
let return_data = invoke_context.get_return_data();
if !return_data.is_empty() {
stable_log::program_return_data(&logger, return_data);
}
match result {
Ok(status) => {
if status != SUCCESS {
Expand Down
101 changes: 101 additions & 0 deletions programs/bpf_loader/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ use thiserror::Error as ThisError;
/// Maximum signers
pub const MAX_SIGNERS: usize = 16;

/// Maximum size return data
pub const MAX_RETURN_DATA: u64 = 1024;

/// Error definitions
#[derive(Debug, ThisError, PartialEq)]
pub enum SyscallError {
Expand Down Expand Up @@ -172,6 +175,12 @@ pub fn register_syscalls(
// Memory allocator
syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?;

// Return data
syscall_registry
.register_syscall_by_name(b"sol_set_return_data", SyscallSetReturnData::call)?;
syscall_registry
.register_syscall_by_name(b"sol_get_return_data", SyscallGetReturnData::call)?;

Ok(syscall_registry)
}

Expand Down Expand Up @@ -380,6 +389,22 @@ pub fn bind_syscall_context_objects<'a>(
None,
)?;

// Return data
vm.bind_syscall_context_object(
Box::new(SyscallSetReturnData {
invoke_context: invoke_context.clone(),
loader_id,
}),
None,
)?;
vm.bind_syscall_context_object(
Box::new(SyscallGetReturnData {
invoke_context: invoke_context.clone(),
loader_id,
}),
None,
)?;

// Cross-program invocation syscalls
vm.bind_syscall_context_object(
Box::new(SyscallInvokeSignedC {
Expand Down Expand Up @@ -2463,6 +2488,82 @@ fn call<'a>(
Ok(SUCCESS)
}

// Return data hanlding
pub struct SyscallSetReturnData<'a> {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallSetReturnData<'a> {
fn call(
&mut self,
addr: u64,
len: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
if len > MAX_RETURN_DATA {
*result = Err(SyscallError::InstructionError(InstructionError::InvalidArgument).into());
return;
}

let mut invoke_context = question_mark!(
self.invoke_context
.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed),
result
);

let return_data = question_mark!(
translate_slice::<u8>(memory_mapping, addr, len, self.loader_id, true,),
result
);

invoke_context.set_return_data(return_data);

*result = Ok(0);
}
}

pub struct SyscallGetReturnData<'a> {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallGetReturnData<'a> {
fn call(
&mut self,
addr: u64,
len: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
let invoke_context = question_mark!(
self.invoke_context
.try_borrow()
.map_err(|_| SyscallError::InvokeContextBorrowFailed),
result
);

let return_data = invoke_context.get_return_data();

let length = std::cmp::min(return_data.len() as u64, len);

let return_data_result = question_mark!(
translate_slice_mut::<u8>(memory_mapping, addr, length, self.loader_id, true,),
result
);

return_data_result.copy_from_slice(&return_data[..length as usize]);

*result = Ok(return_data.len() as u64);
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 8 additions & 0 deletions runtime/src/message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct ThisInvokeContext<'a> {
sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
blockhash: &'a Hash,
fee_calculator: &'a FeeCalculator,
return_data: Vec<u8>,
}
impl<'a> ThisInvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -121,6 +122,7 @@ impl<'a> ThisInvokeContext<'a> {
sysvars: RefCell::new(vec![]),
blockhash,
fee_calculator,
return_data: Vec::new(),
};
invoke_context
.invoke_stack
Expand Down Expand Up @@ -312,6 +314,12 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn get_fee_calculator(&self) -> &FeeCalculator {
self.fee_calculator
}
fn set_return_data(&mut self, data: &[u8]) {
self.return_data = data.to_vec();
}
fn get_return_data(&self) -> &[u8] {
&self.return_data
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
Expand Down
1 change: 1 addition & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ full = [
assert_matches = { version = "1.5.0", optional = true }
bincode = "1.3.3"
borsh = "0.9.0"
base64 = "0.13"
borsh-derive = "0.9.0"
bs58 = "0.4.0"
bv = { version = "0.11.1", features = ["serde"] }
Expand Down
39 changes: 39 additions & 0 deletions sdk/bpf/c/inc/sol/return_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once
/**
* @brief Solana keccak system call
**/

#include <sol/types.h>

#ifdef __cplusplus
extern "C"
{
#endif

/**
* Maximum size of return data
*/
#define MAX_RETURN_DATA 1024

/**
* Set the return data
*
* @param bytes byte array to set
* @param bytes_len length of byte array. This may not exceed MAX_RETURN_DATA.
*/
void sol_set_return_data(const uint8_t *bytes, uint64_t bytes_len);

/**
* Get the return data
*
* @param bytes byte buffer
* @param bytes_len maximum length of buffer
* @param result length of return data (may exceed bytes_len if the return data is longer)
*/
uint64_t sol_get_return_data(const uint8_t *bytes, uint64_t bytes_len);

#ifdef __cplusplus
}
#endif

/**@}*/
1 change: 1 addition & 0 deletions sdk/bpf/c/inc/solana_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sol/keccak.h>
#include <sol/log.h>
#include <sol/pubkey.h>
#include <sol/return_data.h>
#include <sol/secp256k1.h>
#include <sol/sha.h>
#include <sol/string.h>
Expand Down
26 changes: 26 additions & 0 deletions sdk/src/process_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ pub trait InvokeContext {
fn get_blockhash(&self) -> &Hash;
/// Get this invocation's `FeeCalculator`
fn get_fee_calculator(&self) -> &FeeCalculator;
/// Set the return data
fn set_return_data(&mut self, data: &[u8]);
/// Get the return data
fn get_return_data(&self) -> &[u8];
}

/// Convenience macro to log a message with an `Rc<RefCell<dyn Logger>>`
Expand Down Expand Up @@ -309,6 +313,19 @@ pub mod stable_log {
ic_logger_msg!(logger, "Program log: {}", message);
}

/// Log return data as from the program itself.
///
/// The general form is:
///
/// ```notrust
/// "Program return data: <program-generated-in-base64>"
/// ```
///
/// That is, any program-generated output is guaranteed to be prefixed by "Program return data: "
pub fn program_return_data(logger: &Rc<RefCell<dyn Logger>>, data: &[u8]) {
ic_logger_msg!(logger, "Program return data: {}", base64::encode(data));
}

/// Log successful program execution.
///
/// The general form is:
Expand Down Expand Up @@ -393,7 +410,9 @@ pub struct MockInvokeContext<'a> {
pub disabled_features: HashSet<Pubkey>,
pub blockhash: Hash,
pub fee_calculator: FeeCalculator,
pub return_data: Vec<u8>,
}

impl<'a> MockInvokeContext<'a> {
pub fn new(keyed_accounts: Vec<KeyedAccount<'a>>) -> Self {
let compute_budget = ComputeBudget::default();
Expand All @@ -411,6 +430,7 @@ impl<'a> MockInvokeContext<'a> {
disabled_features: HashSet::default(),
blockhash: Hash::default(),
fee_calculator: FeeCalculator::default(),
return_data: Vec::new(),
};
invoke_context
.invoke_stack
Expand Down Expand Up @@ -539,4 +559,10 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
fn get_fee_calculator(&self) -> &FeeCalculator {
&self.fee_calculator
}
fn set_return_data(&mut self, data: &[u8]) {
self.return_data = data.to_vec();
}
fn get_return_data(&self) -> &[u8] {
&self.return_data
}
}

0 comments on commit 99e1f45

Please sign in to comment.