Skip to content

Commit

Permalink
Vp signature verification in host for gas metering
Browse files Browse the repository at this point in the history
  • Loading branch information
grarco committed Jul 21, 2023
1 parent a6c478a commit 184929d
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 116 deletions.
4 changes: 4 additions & 0 deletions core/src/ledger/vp_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::storage_api::{self, StorageRead};
use crate::proto::Tx;
use crate::types::address::Address;
use crate::types::hash::Hash;
use crate::types::key::common::PublicKey;
use crate::types::storage::{
BlockHash, BlockHeight, Epoch, Header, Key, TxIndex,
};
Expand Down Expand Up @@ -94,6 +95,9 @@ where
input_data: Tx,
) -> Result<bool, storage_api::Error>;

/// Verify the signature of a tx section
fn verify_tx_section_signature(&self, pk: &PublicKey, section_hash: &Hash) -> Result<bool, storage_api::Error>;

/// Get a tx hash
fn get_tx_code_hash(&self) -> Result<Option<Hash>, storage_api::Error>;

Expand Down
1 change: 0 additions & 1 deletion core/src/proto/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,6 @@ impl Tx {
/// Verify that the section with the given hash has been signed by the given
/// public key
pub fn verify_signature(
//FIXME: need a host env function for gas metering!
&self,
pk: &common::PublicKey,
hash: &crate::types::hash::Hash,
Expand Down
9 changes: 9 additions & 0 deletions shared/src/ledger/native_vp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::cell::RefCell;
use std::collections::BTreeSet;

pub use namada_core::ledger::vp_env::VpEnv;
use namada_core::types::key::common::PublicKey;

use super::storage_api::{self, ResultExt, StorageRead};
use super::vp_host_fns;
Expand Down Expand Up @@ -532,6 +533,14 @@ where
}
}

fn verify_tx_section_signature(
&self,
pk: &PublicKey,
section_hash: &Hash,
) -> Result<bool, storage_api::Error> {
unimplemented!("Native VPs don't validate tx singature")
}

fn verify_masp(&self, _tx: Vec<u8>) -> Result<bool, storage_api::Error> {
unimplemented!("no masp native vp")
}
Expand Down
2 changes: 1 addition & 1 deletion shared/src/ledger/vp_host_fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::num::TryFromIntError;
use namada_core::ledger::gas::VERIFY_TX_SIG_GAS_COST;
use namada_core::types::address::Address;
use namada_core::types::hash::Hash;
use namada_core::types::key::common;
use namada_core::types::key::{common, PublicKey};
use namada_core::types::storage::{
BlockHash, BlockHeight, Epoch, Header, Key, TxIndex,
};
Expand Down
47 changes: 46 additions & 1 deletion shared/src/vm/host_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ use std::f64::MIN;
use std::num::TryFromIntError;

use borsh::{BorshDeserialize, BorshSerialize};
use namada_core::ledger::gas::{GasMetering, TxGasMeter};
use namada_core::ledger::gas::{
GasMetering, TxGasMeter, VERIFY_TX_SIG_GAS_COST,
};
use namada_core::types::internal::KeyVal;
use namada_core::types::key::common;
use thiserror::Error;

#[cfg(feature = "wasm-runtime")]
Expand Down Expand Up @@ -1763,6 +1766,48 @@ where
Ok(epoch.0)
}

/// Verify a transaction signature
/// TODO: this is just a warkaround to track gas for multiple singature verifications. When the runtime gas meter is implemented this funcion can be removed
pub fn vp_verify_tx_section_signature<MEM, DB, H, EVAL, CA>(
env: &VpVmEnv<MEM, DB, H, EVAL, CA>,
pk_ptr: u64,
pk_len: u64,
hash_ptr: u64,
hash_len: u64,
) -> vp_host_fns::EnvResult<i64>
where
MEM: VmMemory,
DB: storage::DB + for<'iter> storage::DBIter<'iter>,
H: StorageHasher,
EVAL: VpEvaluator,
CA: WasmCacheAccess,
{
let (pk, gas) = env
.memory
.read_bytes(pk_ptr, pk_len as _)
.map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?;

let gas_meter = unsafe { env.ctx.gas_meter.get() };
vp_host_fns::add_gas(gas_meter, gas)?;

let pk = common::PublicKey::try_from_slice(&pk)
.map_err(vp_host_fns::RuntimeError::EncodingError)?;

let (hash, gas) = env
.memory
.read_bytes(hash_ptr, hash_len as _)
.map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?;

vp_host_fns::add_gas(gas_meter, gas)?;
let hash = namada_core::types::hash::Hash::try_from_slice(&hash)
.map_err(vp_host_fns::RuntimeError::EncodingError)?;

vp_host_fns::add_gas(gas_meter, VERIFY_TX_SIG_GAS_COST)?;
let tx = unsafe { env.ctx.tx.get() };

Ok(HostEnvResult::from(tx.verify_signature(&pk, &hash).is_ok()).to_i64())
}

/// Verify a ShieldedTransaction.
pub fn vp_verify_masp<MEM, DB, H, EVAL, CA>(
env: &VpVmEnv<MEM, DB, H, EVAL, CA>,
Expand Down
56 changes: 29 additions & 27 deletions shared/src/vm/wasm/host_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,31 +98,33 @@ where
CA: WasmCacheAccess,
{
wasmer::imports! {
// default namespace
"env" => {
"memory" => initial_memory,
"gas" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_charge_gas),
"namada_vp_read_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_pre),
"namada_vp_read_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_post),
"namada_vp_read_temp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_temp),
"namada_vp_result_buffer" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_result_buffer),
"namada_vp_has_key_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_pre),
"namada_vp_has_key_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_post),
"namada_vp_iter_prefix_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre),
"namada_vp_iter_prefix_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre),
"namada_vp_iter_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_next),
"namada_vp_get_chain_id" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_chain_id),
"namada_vp_get_tx_index" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_index),
"namada_vp_get_block_height" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_height),
"namada_vp_get_block_header" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_header),
"namada_vp_get_block_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_hash),
"namada_vp_get_tx_code_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_code_hash),
"namada_vp_get_block_epoch" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_epoch),
"namada_vp_verify_masp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_masp),
"namada_vp_eval" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_eval),
"namada_vp_get_native_token" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_native_token),
"namada_vp_has_valid_pow" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_valid_pow),
"namada_vp_log_string" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_log_string),
},
}
// default namespace
"env" => {
"memory" => initial_memory,
"gas" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_charge_gas),
"namada_vp_read_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_pre),
"namada_vp_read_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_post),
"namada_vp_read_temp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_read_temp),
"namada_vp_result_buffer" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_result_buffer),
"namada_vp_has_key_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_pre),
"namada_vp_has_key_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_post),
"namada_vp_iter_prefix_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre),
"namada_vp_iter_prefix_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre),
"namada_vp_iter_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_next),
"namada_vp_get_chain_id" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_chain_id),
"namada_vp_get_tx_index" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_index),
"namada_vp_get_block_height" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_height),
"namada_vp_get_block_header" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_header),
"namada_vp_get_block_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_hash),
"namada_vp_get_tx_code_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_code_hash),
"namada_vp_get_block_epoch" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_epoch),
"namada_vp_verify_tx_section_signature" => Function::new_native_with_env(wasm_store,
env.clone(), host_env::vp_verify_tx_section_signature),
"namada_vp_verify_masp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_masp),
"namada_vp_eval" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_eval),
"namada_vp_get_native_token" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_native_token),
"namada_vp_has_valid_pow" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_valid_pow),
"namada_vp_log_string" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_log_string),
},
}
}
8 changes: 8 additions & 0 deletions vm_env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ pub mod vp {
// Requires a node running with "Info" log level
pub fn namada_vp_log_string(str_ptr: u64, str_len: u64);

// Verify the signature of a tx section
pub fn namada_vp_verify_tx_section_signature(
pk_ptr: u64,
pk_len: u64,
hash_ptr: u64,
hash_len: u64,
) -> i64;

pub fn namada_vp_eval(
vp_code_hash_ptr: u64,
vp_code_hash_len: u64,
Expand Down
19 changes: 19 additions & 0 deletions vp_prelude/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,25 @@ impl<'view> VpEnv<'view> for Ctx {
Ok(HostEnvResult::is_success(result))
}

fn verify_tx_section_signature(
&self,
pk: &crate::key::common::PublicKey,
section_hash: &Hash,
) -> Result<bool, Error> {
let pk = pk.try_to_vec().unwrap();
let hash = section_hash.try_to_vec().unwrap();
let valid = unsafe {
namada_vp_verify_tx_section_signature(
pk.as_ptr() as _,
pk.len() as _,
hash.as_ptr() as _,
hash.len() as _,
)
};

Ok(HostEnvResult::is_success(valid))
}

fn get_tx_code_hash(&self) -> Result<Option<Hash>, Error> {
let result = Vec::with_capacity(HASH_LENGTH + 1);
unsafe {
Expand Down
56 changes: 36 additions & 20 deletions wasm/wasm_source/src/vp_implicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ fn validate_tx(
let valid_sig = Lazy::new(|| {
let pk = key::get(ctx, &addr);
match pk {
Ok(Some(pk)) => tx_data
.verify_signature(&pk, tx_data.data_sechash())
Ok(Some(pk)) => ctx
.verify_tx_section_signature(&pk, tx_data.data_sechash())
.is_ok(),
_ => false,
}
Expand Down Expand Up @@ -503,10 +503,14 @@ mod tests {
vp_env.all_touched_storage_keys();
let verifiers: BTreeSet<Address> = BTreeSet::default();
vp_host_env::set(vp_env);
assert!(
validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers)
.unwrap()
);
assert!(validate_tx(
&CTX,
signed_tx,
vp_owner,
keys_changed,
verifiers
)
.unwrap());
}

/// Test that a debit transfer without a valid signature is rejected.
Expand Down Expand Up @@ -610,10 +614,14 @@ mod tests {
vp_env.all_touched_storage_keys();
let verifiers: BTreeSet<Address> = BTreeSet::default();
vp_host_env::set(vp_env);
assert!(
validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers)
.unwrap()
);
assert!(validate_tx(
&CTX,
signed_tx,
vp_owner,
keys_changed,
verifiers
)
.unwrap());
}

/// Test that a transfer on with accounts other than self is accepted.
Expand Down Expand Up @@ -670,8 +678,8 @@ mod tests {

/// Generates a keypair, derive an implicit address from it and generate
/// a storage key inside its storage.
fn arb_account_storage_subspace_key()
-> impl Strategy<Value = (key::common::SecretKey, Address, Key)> {
fn arb_account_storage_subspace_key(
) -> impl Strategy<Value = (key::common::SecretKey, Address, Key)> {
// Generate a keypair
key::testing::arb_common_keypair().prop_flat_map(|sk| {
let pk = sk.ref_to();
Expand Down Expand Up @@ -850,10 +858,14 @@ mod tests {
vp_env.all_touched_storage_keys();
let verifiers: BTreeSet<Address> = BTreeSet::default();
vp_host_env::set(vp_env);
assert!(
!validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers)
.unwrap()
);
assert!(!validate_tx(
&CTX,
signed_tx,
vp_owner,
keys_changed,
verifiers
)
.unwrap());
}

#[test]
Expand Down Expand Up @@ -899,9 +911,13 @@ mod tests {
vp_env.all_touched_storage_keys();
let verifiers: BTreeSet<Address> = BTreeSet::default();
vp_host_env::set(vp_env);
assert!(
validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers)
.unwrap()
);
assert!(validate_tx(
&CTX,
signed_tx,
vp_owner,
keys_changed,
verifiers
)
.unwrap());
}
}
16 changes: 10 additions & 6 deletions wasm/wasm_source/src/vp_testnet_faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ fn validate_tx(
let valid_sig = Lazy::new(|| {
let pk = key::get(ctx, &addr);
match pk {
Ok(Some(pk)) => tx_data
.verify_signature(&pk, tx_data.data_sechash())
Ok(Some(pk)) => ctx
.verify_tx_section_signature(&pk, tx_data.data_sechash())
.is_ok(),
_ => false,
}
Expand Down Expand Up @@ -261,10 +261,14 @@ mod tests {
vp_env.all_touched_storage_keys();
let verifiers: BTreeSet<Address> = BTreeSet::default();
vp_host_env::set(vp_env);
assert!(
validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers)
.unwrap()
);
assert!(validate_tx(
&CTX,
signed_tx,
vp_owner,
keys_changed,
verifiers
)
.unwrap());
}

prop_compose! {
Expand Down
Loading

0 comments on commit 184929d

Please sign in to comment.