Proposal: log binary data for Solidity
The program_id is not needed on "Program return data: " because it always preceeded by the program invoke message, so no need to repeat the program id. Also rename this to "Program return: " since "data" is redundant.
This commit is contained in:
@ -40,6 +40,11 @@ static void sol_log_array(const uint8_t *array, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the base64 representation of some arrays.
|
||||
*/
|
||||
void sol_log_data(SolBytes *fields, uint64_t fields_len);
|
||||
|
||||
/**
|
||||
* Prints the program's input parameters
|
||||
*
|
||||
|
@ -36,10 +36,12 @@ thiserror = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
|
||||
bitflags = "1.3.1"
|
||||
base64 = "0.13"
|
||||
curve25519-dalek = "3.0.0"
|
||||
libsecp256k1 = "0.6.0"
|
||||
rand = "0.7.0"
|
||||
solana-logger = { path = "../../logger", version = "=1.8.0" }
|
||||
itertools = "0.10.1"
|
||||
|
||||
[dev-dependencies]
|
||||
static_assertions = "1.1.0"
|
||||
|
@ -85,6 +85,23 @@ extern "C" {
|
||||
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64);
|
||||
}
|
||||
|
||||
/// Print some slices as base64
|
||||
///
|
||||
/// @param data - The slices to print
|
||||
pub fn sol_log_data(data: &[&[u8]]) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_log_data(data: *const u8, data_len: u64);
|
||||
}
|
||||
|
||||
unsafe { sol_log_data(data as *const _ as *const u8, data.len() as u64) };
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_log_data(data);
|
||||
}
|
||||
|
||||
/// Print the hexadecimal representation of a slice
|
||||
///
|
||||
/// @param slice - The array to print
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
|
||||
program_error::UNSUPPORTED_SYSVAR, pubkey::Pubkey,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@ -51,7 +52,7 @@ pub trait SyscallStubs: Sync + Send {
|
||||
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
// cannot be overlapping
|
||||
if dst as usize + n > src as usize && src as usize > dst as usize {
|
||||
panic!("memcpy does not support oveerlapping regions");
|
||||
panic!("memcpy does not support overlapping regions");
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(src, dst, n as usize);
|
||||
}
|
||||
@ -84,6 +85,9 @@ pub trait SyscallStubs: Sync + Send {
|
||||
None
|
||||
}
|
||||
fn sol_set_return_data(&mut self, _data: &[u8]) {}
|
||||
fn sol_log_data(&self, fields: &[&[u8]]) {
|
||||
println!("data: {}", fields.iter().map(base64::encode).join(" "));
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultSyscallStubs {}
|
||||
@ -165,3 +169,7 @@ pub(crate) fn sol_get_return_data() -> Option<(Pubkey, Vec<u8>)> {
|
||||
pub(crate) fn sol_set_return_data(data: &[u8]) {
|
||||
SYSCALL_STUBS.write().unwrap().sol_set_return_data(data)
|
||||
}
|
||||
|
||||
pub(crate) fn sol_log_data(data: &[&[u8]]) {
|
||||
SYSCALL_STUBS.read().unwrap().sol_log_data(data)
|
||||
}
|
||||
|
@ -211,6 +211,10 @@ pub mod reduce_required_deploy_balance {
|
||||
solana_sdk::declare_id!("EBeznQDjcPG8491sFsKZYBi5S5jTVXMpAKNDJMQPS2kq");
|
||||
}
|
||||
|
||||
pub mod sol_log_data_syscall_enabled {
|
||||
solana_sdk::declare_id!("HYPs7jyJ3KwQFdDpuSzMtVKf1MLJDaZRv3CSWvfUqdFo");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
@ -259,6 +263,7 @@ lazy_static! {
|
||||
(return_data_syscall_enabled::id(), "enable sol_{set,get}_return_data syscall"),
|
||||
(fix_write_privs::id(), "fix native invoke write privileges"),
|
||||
(reduce_required_deploy_balance::id(), "reduce required payer balance for program deploys"),
|
||||
(sol_log_data_syscall_enabled::id(), "enable sol_log_data syscall"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
use itertools::Itertools;
|
||||
use solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
compute_budget::ComputeBudget,
|
||||
@ -317,20 +318,37 @@ pub mod stable_log {
|
||||
ic_logger_msg!(logger, "Program log: {}", message);
|
||||
}
|
||||
|
||||
/// Emit a program data.
|
||||
///
|
||||
/// The general form is:
|
||||
///
|
||||
/// ```notrust
|
||||
/// "Program data: <binary-data-in-base64>*"
|
||||
/// ```
|
||||
///
|
||||
/// That is, any program-generated output is guaranteed to be prefixed by "Program data: "
|
||||
pub fn program_data(logger: &Rc<RefCell<dyn Logger>>, data: &[&[u8]]) {
|
||||
ic_logger_msg!(
|
||||
logger,
|
||||
"Program data: {}",
|
||||
data.iter().map(base64::encode).join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
/// Log return data as from the program itself. This line will not be present if no return
|
||||
/// data was set, or if the return data was set to zero length.
|
||||
///
|
||||
/// The general form is:
|
||||
///
|
||||
/// ```notrust
|
||||
/// "Program return data: <program-id> <program-generated-data-in-base64>"
|
||||
/// "Program return: <program-id> <program-generated-data-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>>, program_id: &Pubkey, data: &[u8]) {
|
||||
/// That is, any program-generated output is guaranteed to be prefixed by "Program return: "
|
||||
pub fn program_return(logger: &Rc<RefCell<dyn Logger>>, program_id: &Pubkey, data: &[u8]) {
|
||||
ic_logger_msg!(
|
||||
logger,
|
||||
"Program return data: {} {}",
|
||||
"Program return: {} {}",
|
||||
program_id,
|
||||
base64::encode(data)
|
||||
);
|
||||
|
Reference in New Issue
Block a user