Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add debug_with_gas functionality #1643

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to

### Added

- Add `Api::debug_with_gas` for emitting debug messages with gas information
during development.
- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have
been checked before. This is useful for state-sync where we know the Wasm code
was checked when it was first uploaded. ([#1635])
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ extern "C" {
/// In production environments it is expected that those messages are discarded.
fn debug(source_ptr: u32);

/// Writes a debug message (UFT-8 encoded) to the host for debugging purposes.
/// Include remaining gas information.
/// The host is free to log or process this in any way it considers appropriate.
/// In production environments it is expected that those messages are discarded.
fn debug_with_gas(source_ptr: u32);

/// Executes a query on the chain (import). Not to be confused with the
/// query export, which queries the state of the contract.
fn query_chain(request: u32) -> u32;
Expand Down
1 change: 1 addition & 0 deletions contracts/hackatom/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn instantiate(
msg: InstantiateMsg,
) -> Result<Response, HackError> {
deps.api.debug("here we go 🚀");
deps.api.debug_with_gas("here we go with gas 🚀");

deps.storage.set(
CONFIG_KEY,
Expand Down
13 changes: 13 additions & 0 deletions packages/std/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ extern "C" {
/// In production environments it is expected that those messages are discarded.
fn debug(source_ptr: u32);

/// Writes a debug message (UFT-8 encoded) to the host for debugging purposes.
/// Include remaining gas information.
/// The host is free to log or process this in any way it considers appropriate.
/// In production environments it is expected that those messages are discarded.
fn debug_with_gas(source_ptr: u32);

/// Executes a query on the chain (import). Not to be confused with the
/// query export, which queries the state of the contract.
fn query_chain(request: u32) -> u32;
Expand Down Expand Up @@ -362,6 +368,13 @@ impl Api for ExternalApi {
let region_ptr = region.as_ref() as *const Region as u32;
unsafe { debug(region_ptr) };
}

fn debug_with_gas(&self, message: &str) {
// keep the boxes in scope, so we free it at the end (don't cast to pointers same line as build_region)
let region = build_region(message.as_bytes());
let region_ptr = region.as_ref() as *const Region as u32;
unsafe { debug_with_gas(region_ptr) };
}
}

/// Takes a pointer to a Region and reads the data into a String.
Expand Down
8 changes: 6 additions & 2 deletions packages/std/src/testing/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ const CANONICAL_LENGTH: usize = 90; // n = 45
const SHUFFLES_ENCODE: usize = 10;
const SHUFFLES_DECODE: usize = 2;

// MockPrecompiles zero pads all human addresses to make them fit the canonical_length
// Mock zero pads all human addresses to make them fit the canonical_length
// it trims off zeros for the reverse operation.
// not really smart, but allows us to see a difference (and consistent length for canonical adddresses)
// not really smart, but allows us to see a difference (and consistent length for canonical addresses)
#[derive(Copy, Clone)]
pub struct MockApi {
/// Length of canonical addresses created with this API. Contracts should not make any assumptions
Expand Down Expand Up @@ -228,6 +228,10 @@ impl Api for MockApi {
fn debug(&self, message: &str) {
println!("{}", message);
}

fn debug_with_gas(&self, message: &str) {
println!("{}, gas_left: NA", message);
}
}

/// Returns a default enviroment with height, time, chain_id, and contract address
Expand Down
5 changes: 5 additions & 0 deletions packages/std/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ pub trait Api {
/// Emits a debugging message that is handled depending on the environment (typically printed to console or ignored).
/// Those messages are not persisted to chain.
fn debug(&self, message: &str);

/// Emits a debugging message that is handled depending on the environment (typically printed to console or ignored).
/// Include gas left information.
/// Those messages are not persisted to chain.
fn debug_with_gas(&self, message: &str);
}

/// A short-hand alias for the two-level query result (1. accessing the contract, 2. executing query in the contract)
Expand Down
4 changes: 3 additions & 1 deletion packages/vm/src/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const SUPPORTED_IMPORTS: &[&str] = &[
"env.ed25519_verify",
"env.ed25519_batch_verify",
"env.debug",
"env.debug_with_gas",
"env.query_chain",
#[cfg(feature = "iterator")]
"env.db_scan",
Expand Down Expand Up @@ -798,6 +799,7 @@ mod tests {
"env.addr_canonicalize",
"env.addr_humanize",
"env.debug",
"env.debug_with_gas",
"env.query_chain",
];
let result = check_wasm_imports(&deserialize_wasm(&wasm).unwrap(), supported_imports);
Expand All @@ -806,7 +808,7 @@ mod tests {
println!("{}", msg);
assert_eq!(
msg,
r#"Wasm contract requires unsupported import: "env.foo". Required imports: {"env.bar", "env.foo", "env.spammyspam01", "env.spammyspam02", "env.spammyspam03", "env.spammyspam04", "env.spammyspam05", "env.spammyspam06", "env.spammyspam07", "env.spammyspam08", ... 2 more}. Available imports: ["env.db_read", "env.db_write", "env.db_remove", "env.addr_canonicalize", "env.addr_humanize", "env.debug", "env.query_chain"]."#
r#"Wasm contract requires unsupported import: "env.foo". Required imports: {"env.bar", "env.foo", "env.spammyspam01", "env.spammyspam02", "env.spammyspam03", "env.spammyspam04", "env.spammyspam05", "env.spammyspam06", "env.spammyspam07", "env.spammyspam08", ... 2 more}. Available imports: ["env.db_read", "env.db_write", "env.db_remove", "env.addr_canonicalize", "env.addr_humanize", "env.debug", "env.debug_with_gas", "env.query_chain"]."#
);
}
err => panic!("Unexpected error: {:?}", err),
Expand Down
16 changes: 16 additions & 0 deletions packages/vm/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,21 @@ pub fn do_debug<A: BackendApi, S: Storage, Q: Querier>(
Ok(())
}

/// Prints a debug message to console, including remaining gas information.
/// This does not charge gas, so debug printing should be disabled when used in a blockchain module.
pub fn do_debug_with_gas<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
message_ptr: u32,
) -> VmResult<()> {
if env.print_debug {
let message_data = read_region(&env.memory(), message_ptr, MAX_LENGTH_DEBUG)?;
let msg = String::from_utf8_lossy(&message_data);
let gas_remaining = env.get_gas_left();
println!("{}, gas_left: {}", msg, gas_remaining);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something we can also easily add to the output of the current debug implementation (for the same reasons I mentioned in the trace PR)

}
Ok(())
}

/// Aborts the contract and shows the given error message
pub fn do_abort<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
Expand Down Expand Up @@ -562,6 +577,7 @@ mod tests {
"ed25519_batch_verify" => Function::new_native(store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
"debug" => Function::new_native(store, |_a: u32| {}),
"abort" => Function::new_native(store, |_a: u32| {}),
"debug_with_gas" => Function::new_native(store, |_a: u32| {}),
},
};
let instance = Box::from(WasmerInstance::new(&module, &import_obj).unwrap());
Expand Down
14 changes: 12 additions & 2 deletions packages/vm/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::environment::Environment;
use crate::errors::{CommunicationError, VmError, VmResult};
use crate::imports::{
do_abort, do_addr_canonicalize, do_addr_humanize, do_addr_validate, do_db_read, do_db_remove,
do_db_write, do_debug, do_ed25519_batch_verify, do_ed25519_verify, do_query_chain,
do_secp256k1_recover_pubkey, do_secp256k1_verify,
do_db_write, do_debug, do_debug_with_gas, do_ed25519_batch_verify, do_ed25519_verify,
do_query_chain, do_secp256k1_recover_pubkey, do_secp256k1_verify,
};
#[cfg(feature = "iterator")]
use crate::imports::{do_db_next, do_db_scan};
Expand Down Expand Up @@ -181,6 +181,16 @@ where
Function::new_native_with_env(store, env.clone(), do_debug),
);

// Allows the contract to emit debug logs that the host can either process or ignore.
// Include remaining gas information.
// This is never written to chain.
// Takes a pointer argument of a memory region that must contain an UTF-8 encoded string.
// Ownership of both input and output pointer is not transferred to the host.
env_imports.insert(
"debug_with_gas",
Function::new_native_with_env(store, env.clone(), do_debug_with_gas),
);

// Aborts the contract execution with an error message provided by the contract.
// Takes a pointer argument of a memory region that must contain an UTF-8 encoded string.
// Ownership of both input and output pointer is not transferred to the host.
Expand Down