Skip to content

Commit

Permalink
Add addr_validate import
Browse files Browse the repository at this point in the history
  • Loading branch information
webmaster128 committed Mar 2, 2021
1 parent 5a6c3ca commit 87dd82d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 3 deletions.
2 changes: 2 additions & 0 deletions packages/vm/src/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const SUPPORTED_IMPORTS: &[&str] = &[
"env.db_read",
"env.db_write",
"env.db_remove",
"env.addr_validate",
"env.addr_canonicalize",
"env.addr_humanize",
"env.secp256k1_verify",
Expand Down Expand Up @@ -321,6 +322,7 @@ mod tests {
(import "env" "db_read" (func (param i32 i32) (result i32)))
(import "env" "db_write" (func (param i32 i32) (result i32)))
(import "env" "db_remove" (func (param i32) (result i32)))
(import "env" "addr_validate" (func (param i32) (result i32)))
(import "env" "addr_canonicalize" (func (param i32 i32) (result i32)))
(import "env" "addr_humanize" (func (param i32 i32) (result i32)))
(import "env" "secp256k1_verify" (func (param i32 i32 i32) (result i32)))
Expand Down
1 change: 1 addition & 0 deletions packages/vm/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ mod tests {
"db_scan" => Function::new_native(&store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }),
"db_next" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"query_chain" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"addr_validate" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"addr_canonicalize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }),
"addr_humanize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }),
"secp256k1_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
Expand Down
125 changes: 125 additions & 0 deletions packages/vm/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ pub fn native_db_remove<A: BackendApi, S: Storage, Q: Querier>(
do_remove(env, key_ptr)
}

pub fn native_addr_validate<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
source_ptr: u32,
) -> VmResult<u32> {
do_addr_validate(&env, source_ptr)
}

pub fn native_addr_canonicalize<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
source_ptr: u32,
Expand Down Expand Up @@ -226,6 +233,37 @@ fn do_remove<A: BackendApi, S: Storage, Q: Querier>(
Ok(())
}

fn do_addr_validate<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
source_ptr: u32,
) -> VmResult<u32> {
let source_data = read_region(&env.memory(), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?;
if source_data.is_empty() {
return Ok(write_to_contract::<A, S, Q>(env, b"Input is empty")?);
}

let source_string = match String::from_utf8(source_data) {
Ok(s) => s,
Err(_) => {
return Ok(write_to_contract::<A, S, Q>(
env,
b"Input is not valid UTF-8",
)?)
}
};
let human: HumanAddr = source_string.into();

let (result, gas_info) = env.api.canonical_address(&human);
process_gas_info::<A, S, Q>(env, gas_info)?;
match result {
Ok(_canonical) => Ok(0),
Err(BackendError::UserErr { msg, .. }) => {
Ok(write_to_contract::<A, S, Q>(env, msg.as_bytes())?)
}
Err(err) => Err(VmError::from(err)),
}
}

fn do_addr_canonicalize<A: BackendApi, S: Storage, Q: Querier>(
env: &Environment<A, S, Q>,
source_ptr: u32,
Expand Down Expand Up @@ -569,6 +607,7 @@ mod tests {
"db_scan" => Function::new_native(&store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }),
"db_next" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"query_chain" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"addr_validate" => Function::new_native(&store, |_a: u32| -> u32 { 0 }),
"addr_canonicalize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }),
"addr_humanize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }),
"secp256k1_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
Expand Down Expand Up @@ -896,6 +935,92 @@ mod tests {
}
}

#[test]
fn do_addr_validate_works() {
let api = MockApi::default();
let (env, _instance) = make_instance(api);

let source_ptr = write_data(&env, b"foo");

leave_default_data(&env);

let res = do_addr_validate::<MA, MS, MQ>(&env, source_ptr).unwrap();
assert_eq!(res, 0);
}

#[test]
fn do_addr_validate_reports_invalid_input_back_to_contract() {
let api = MockApi::default();
let (env, _instance) = make_instance(api);

let source_ptr1 = write_data(&env, b"fo\x80o"); // invalid UTF-8 (fo�o)
let source_ptr2 = write_data(&env, b""); // empty
let source_ptr3 = write_data(&env, b"addressexceedingaddressspace"); // too long

leave_default_data(&env);

let res = do_addr_validate::<MA, MS, MQ>(&env, source_ptr1).unwrap();
assert_ne!(res, 0);
let err = String::from_utf8(force_read(&env, res)).unwrap();
assert_eq!(err, "Input is not valid UTF-8");

let res = do_addr_validate::<MA, MS, MQ>(&env, source_ptr2).unwrap();
assert_ne!(res, 0);
let err = String::from_utf8(force_read(&env, res)).unwrap();
assert_eq!(err, "Input is empty");

let res = do_addr_validate::<MA, MS, MQ>(&env, source_ptr3).unwrap();
assert_ne!(res, 0);
let err = String::from_utf8(force_read(&env, res)).unwrap();
assert_eq!(err, "Invalid input: human address too long");
}

#[test]
fn do_addr_validate_fails_for_broken_backend() {
let api = MockApi::new_failing("Temporarily unavailable");
let (env, _instance) = make_instance(api);

let source_ptr = write_data(&env, b"foo");

leave_default_data(&env);

let result = do_addr_validate::<MA, MS, MQ>(&env, source_ptr);
match result.unwrap_err() {
VmError::BackendErr {
source: BackendError::Unknown { msg, .. },
..
} => {
assert_eq!(msg.unwrap(), "Temporarily unavailable");
}
err => panic!("Incorrect error returned: {:?}", err),
}
}

#[test]
fn do_addr_validate_fails_for_large_inputs() {
let api = MockApi::default();
let (env, _instance) = make_instance(api);

let source_ptr = write_data(&env, &vec![61; 100]);

leave_default_data(&env);

let result = do_addr_validate::<MA, MS, MQ>(&env, source_ptr);
match result.unwrap_err() {
VmError::CommunicationErr {
source:
CommunicationError::RegionLengthTooBig {
length, max_length, ..
},
..
} => {
assert_eq!(length, 100);
assert_eq!(max_length, 90);
}
err => panic!("Incorrect error returned: {:?}", err),
}
}

#[test]
fn do_addr_canonicalize_works() {
let api = MockApi::default();
Expand Down
15 changes: 12 additions & 3 deletions packages/vm/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use crate::environment::Environment;
use crate::errors::{CommunicationError, VmError, VmResult};
use crate::features::required_features_from_wasmer_instance;
use crate::imports::{
native_addr_canonicalize, native_addr_humanize, native_db_read, native_db_remove,
native_db_write, native_debug, native_ed25519_batch_verify, native_ed25519_verify,
native_query_chain, native_secp256k1_recover_pubkey, native_secp256k1_verify,
native_addr_canonicalize, native_addr_humanize, native_addr_validate, native_db_read,
native_db_remove, native_db_write, native_debug, native_ed25519_batch_verify,
native_ed25519_verify, native_query_chain, native_secp256k1_recover_pubkey,
native_secp256k1_verify,
};
#[cfg(feature = "iterator")]
use crate::imports::{native_db_next, native_db_scan};
Expand Down Expand Up @@ -105,6 +106,14 @@ where
Function::new_native_with_env(store, env.clone(), native_db_remove),
);

// Reads human address from source_ptr and checks if it is valid.
// Returns 0 on if the input is valid. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs.
// Ownership of the input pointer is not transferred to the host.
env_imports.insert(
"addr_validate",
Function::new_native_with_env(store, env.clone(), native_addr_validate),
);

// Reads human address from source_ptr and writes canonicalized representation to destination_ptr.
// A prepared and sufficiently large memory Region is expected at destination_ptr that points to pre-allocated memory.
// Returns 0 on success. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs.
Expand Down

0 comments on commit 87dd82d

Please sign in to comment.