From 8b007d1c87dfe7a2c8597576f32b10d3589acb92 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Tue, 28 Mar 2023 12:46:58 -0700 Subject: [PATCH] Chain ID included in transaction ID's and predicate owners (#406) * use chain id for predicate root * include chain id in tx id * add consensus params to tx builder * add chain id to gm * merge master * update rust to 1.67.1 * deal with merge conflicts * fmt * fix missing 'with_params' * refactor tx calculation into common function --------- Co-authored-by: Green Baneling --- .github/workflows/ci.yml | 2 +- fuel-asm/src/args.rs | 6 +++ fuel-tx/src/builder.rs | 22 ++++++-- fuel-tx/src/tests/bytes.rs | 4 +- fuel-tx/src/tests/offset.rs | 2 +- fuel-tx/src/tests/valid_cases/input.rs | 19 +++---- fuel-tx/src/tests/valid_cases/transaction.rs | 22 ++++---- fuel-tx/src/transaction.rs | 4 +- .../src/transaction/consensus_parameters.rs | 30 +++++++++++ fuel-tx/src/transaction/id.rs | 44 ++++++++++------ fuel-tx/src/transaction/metadata.rs | 15 +++--- fuel-tx/src/transaction/types.rs | 12 +++++ fuel-tx/src/transaction/types/create.rs | 18 +++---- .../transaction/types/create/ser_de_tests.rs | 1 + fuel-tx/src/transaction/types/input.rs | 18 ++++--- fuel-tx/src/transaction/types/mint.rs | 17 ++++--- fuel-tx/src/transaction/types/script.rs | 18 +++---- fuel-tx/src/transaction/validity.rs | 26 ++++++---- fuel-vm/src/checked_transaction.rs | 10 ++-- fuel-vm/src/checked_transaction/builder.rs | 23 +++------ fuel-vm/src/checked_transaction/types.rs | 6 +-- fuel-vm/src/interpreter/executors/main.rs | 2 +- fuel-vm/src/interpreter/initialization.rs | 2 +- fuel-vm/src/interpreter/internal/tests.rs | 2 +- fuel-vm/src/interpreter/metadata.rs | 12 ++++- fuel-vm/src/interpreter/metadata/tests.rs | 31 ++++++++++- fuel-vm/src/predicate.rs | 3 +- fuel-vm/src/tests/blockchain.rs | 8 +-- fuel-vm/src/tests/code_coverage.rs | 3 +- fuel-vm/src/tests/crypto.rs | 9 ++-- fuel-vm/src/tests/metadata.rs | 51 ++++++++++++++++--- fuel-vm/src/tests/predicate.rs | 29 ++++++----- fuel-vm/src/tests/profile_gas.rs | 3 +- fuel-vm/src/tests/validation.rs | 3 +- fuel-vm/src/util.rs | 10 ++-- 35 files changed, 326 insertions(+), 161 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efd13b3423..92af7ea2c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ concurrency: env: CARGO_TERM_COLOR: always - RUST_VERSION: 1.67.0 + RUST_VERSION: 1.67.1 jobs: cargo: diff --git a/fuel-asm/src/args.rs b/fuel-asm/src/args.rs index c81843341b..b189b39a63 100644 --- a/fuel-asm/src/args.rs +++ b/fuel-asm/src/args.rs @@ -5,6 +5,7 @@ use fuel_types::{Immediate12, Immediate18}; const GM_IS_CALLER_EXTERNAL: u8 = 0x01; const GM_GET_CALLER: u8 = 0x02; const GM_GET_VERIFYING_PREDICATE: u8 = 0x03; +const GM_GET_CHAIN_ID: u8 = 0x04; /// Argument list for GM (get metadata) instruction #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::EnumIter)] @@ -22,6 +23,9 @@ pub enum GMArgs { /// Get index of current predicate. GetVerifyingPredicate = GM_GET_VERIFYING_PREDICATE, + + /// Get the Chain ID this VM is operating within + GetChainId = GM_GET_CHAIN_ID, } impl TryFrom for GMArgs { @@ -32,6 +36,7 @@ impl TryFrom for GMArgs { GM_IS_CALLER_EXTERNAL => Ok(Self::IsCallerExternal), GM_GET_CALLER => Ok(Self::GetCaller), GM_GET_VERIFYING_PREDICATE => Ok(Self::GetVerifyingPredicate), + GM_GET_CHAIN_ID => Ok(Self::GetChainId), _ => Err(PanicReason::InvalidMetadataIdentifier), } } @@ -428,6 +433,7 @@ fn encode_gm_args() { GMArgs::IsCallerExternal, GMArgs::GetCaller, GMArgs::GetVerifyingPredicate, + GMArgs::GetChainId, ]; args.into_iter().for_each(|a| { diff --git a/fuel-tx/src/builder.rs b/fuel-tx/src/builder.rs index bd9655e99b..c0df2e1aa9 100644 --- a/fuel-tx/src/builder.rs +++ b/fuel-tx/src/builder.rs @@ -1,6 +1,6 @@ use crate::transaction::field::{BytecodeLength, BytecodeWitnessIndex, Witnesses}; use crate::transaction::{field, Chargeable, Create, Executable, Script}; -use crate::{Cacheable, Input, Mint, Output, StorageSlot, Transaction, TxPointer, Witness}; +use crate::{Cacheable, ConsensusParameters, Input, Mint, Output, StorageSlot, Transaction, TxPointer, Witness}; #[cfg(feature = "std")] use crate::Signable; @@ -88,6 +88,7 @@ pub struct TransactionBuilder { should_prepare_script: bool, should_prepare_predicate: bool, + parameters: ConsensusParameters, // We take the key by reference so this lib won't have the responsibility to properly zeroize // the keys @@ -165,8 +166,18 @@ impl TransactionBuilder { should_prepare_script, should_prepare_predicate, sign_keys, + parameters: ConsensusParameters::DEFAULT, } } + + pub fn get_params(&self) -> &ConsensusParameters { + &self.parameters + } + + pub fn with_params(&mut self, parameters: ConsensusParameters) -> &mut Self { + self.parameters = parameters; + self + } } impl TransactionBuilder { @@ -264,6 +275,7 @@ impl TransactionBuilder { self } + #[cfg(feature = "std")] fn prepare_finalize(&mut self) { if self.should_prepare_predicate { @@ -281,9 +293,9 @@ impl TransactionBuilder { let mut tx = core::mem::take(&mut self.tx); - self.sign_keys.iter().for_each(|k| tx.sign_inputs(k)); + self.sign_keys.iter().for_each(|k| tx.sign_inputs(k, &self.parameters)); - tx.precompute(); + tx.precompute(&self.parameters); tx } @@ -294,7 +306,7 @@ impl TransactionBuilder { let mut tx = core::mem::take(&mut self.tx); - tx.precompute(); + tx.precompute(&self.parameters); tx } @@ -317,7 +329,7 @@ pub trait Finalizable { impl Finalizable for TransactionBuilder { fn finalize(&mut self) -> Mint { let mut tx = core::mem::take(&mut self.tx); - tx.precompute(); + tx.precompute(&self.parameters); tx } diff --git a/fuel-tx/src/tests/bytes.rs b/fuel-tx/src/tests/bytes.rs index 67ac285be8..b44930a7b1 100644 --- a/fuel-tx/src/tests/bytes.rs +++ b/fuel-tx/src/tests/bytes.rs @@ -720,7 +720,7 @@ fn create_input_data_offset() { ); let mut tx_p = tx.clone(); - tx_p.precompute(); + tx_p.precompute(&ConsensusParameters::DEFAULT); buffer.iter_mut().for_each(|b| *b = 0x00); let _ = tx.read(buffer.as_mut_slice()).expect("Failed to serialize input"); @@ -826,7 +826,7 @@ fn script_input_coin_data_offset() { ); let mut tx_p = tx.clone(); - tx_p.precompute(); + tx_p.precompute(&ConsensusParameters::DEFAULT); buffer.iter_mut().for_each(|b| *b = 0x00); diff --git a/fuel-tx/src/tests/offset.rs b/fuel-tx/src/tests/offset.rs index 98fe330c72..0ebac0de97 100644 --- a/fuel-tx/src/tests/offset.rs +++ b/fuel-tx/src/tests/offset.rs @@ -391,7 +391,7 @@ fn iow_offset() { let bytes = tx.to_bytes(); let mut tx_p = tx.clone(); - tx_p.precompute(); + tx_p.precompute(&ConsensusParameters::DEFAULT); tx.inputs().iter().enumerate().for_each(|(x, i)| { let offset = tx.inputs_offset_at(x).unwrap(); diff --git a/fuel-tx/src/tests/valid_cases/input.rs b/fuel-tx/src/tests/valid_cases/input.rs index 8bee5eac22..fad2dd3411 100644 --- a/fuel-tx/src/tests/valid_cases/input.rs +++ b/fuel-tx/src/tests/valid_cases/input.rs @@ -12,7 +12,7 @@ fn input_coin_message_signature() { let rng = &mut StdRng::seed_from_u64(8586); fn check_inputs(tx: Tx) -> Result<(), CheckError> { - let txhash = tx.id(); + let txhash = tx.id(&ConsensusParameters::DEFAULT); let outputs = tx.outputs(); let witnesses = tx.witnesses(); @@ -42,8 +42,9 @@ fn input_coin_message_signature() { f(&mut tx, &public); - tx.sign_inputs(&secret); - keys.iter().for_each(|sk| tx.sign_inputs(sk)); + tx.sign_inputs(&secret, &ConsensusParameters::DEFAULT); + keys.iter() + .for_each(|sk| tx.sign_inputs(sk, &ConsensusParameters::DEFAULT)); check_inputs(tx) } @@ -108,7 +109,7 @@ fn coin_predicate() { let txhash: Bytes32 = rng.gen(); let predicate = generate_nonempty_padded_bytes(rng); - let owner = (*Contract::root_from_code(&predicate)).into(); + let owner = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); Input::coin_predicate( rng.gen(), @@ -124,7 +125,7 @@ fn coin_predicate() { .unwrap(); let predicate = vec![]; - let owner = (*Contract::root_from_code(&predicate)).into(); + let owner = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); let err = Input::coin_predicate( rng.gen(), @@ -220,7 +221,7 @@ fn message_metadata() { let txhash: Bytes32 = rng.gen(); let predicate = generate_nonempty_padded_bytes(rng); - let recipient = (*Contract::root_from_code(&predicate)).into(); + let recipient = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); Input::message_data_predicate( rng.gen(), @@ -248,7 +249,7 @@ fn message_metadata() { assert_eq!(CheckError::InputWitnessIndexBounds { index: 0 }, err,); let mut predicate = generate_nonempty_padded_bytes(rng); - let recipient = (*Contract::root_from_code(&predicate)).into(); + let recipient = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); predicate[0] = predicate[0].wrapping_add(1); let err = Input::message_data_predicate( @@ -327,7 +328,7 @@ fn message_message_coin() { let txhash: Bytes32 = rng.gen(); let predicate = generate_nonempty_padded_bytes(rng); - let recipient = (*Contract::root_from_code(&predicate)).into(); + let recipient = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); Input::message_coin_predicate( rng.gen(), @@ -353,7 +354,7 @@ fn message_message_coin() { assert_eq!(CheckError::InputWitnessIndexBounds { index: 0 }, err,); let mut predicate = generate_nonempty_padded_bytes(rng); - let recipient = (*Contract::root_from_code(&predicate)).into(); + let recipient = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); predicate[0] = predicate[0].wrapping_add(1); let err = Input::message_coin_predicate( diff --git a/fuel-tx/src/tests/valid_cases/transaction.rs b/fuel-tx/src/tests/valid_cases/transaction.rs index a1cc5cc0c2..cf6a65015e 100644 --- a/fuel-tx/src/tests/valid_cases/transaction.rs +++ b/fuel-tx/src/tests/valid_cases/transaction.rs @@ -743,9 +743,9 @@ fn tx_id_bytecode_len() { vec![w_c], ); - let id_a = tx_a.id(); - let id_b = tx_b.id(); - let id_c = tx_c.id(); + let id_a = tx_a.id(&PARAMS); + let id_b = tx_b.id(&PARAMS); + let id_c = tx_c.id(&PARAMS); // bytecode with different length should produce different id assert_ne!(id_a, id_b); @@ -771,7 +771,7 @@ mod inputs { let predicate = (0..1000).map(|_| rng.gen()).collect_vec(); // The predicate is an owner of the coin - let owner: Address = (*Contract::root_from_code(&predicate)).into(); + let owner: Address = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); let tx = TransactionBuilder::create(generate_bytes(rng).into(), rng.gen(), vec![]) .gas_limit(PARAMS.max_gas_per_tx) @@ -787,9 +787,10 @@ mod inputs { predicate, vec![], )) + .with_params(PARAMS) .finalize(); - assert!(tx.check_predicate_owners()); + assert!(tx.check_predicate_owners(&ConsensusParameters::DEFAULT)); } #[test] @@ -812,9 +813,10 @@ mod inputs { predicate, vec![], )) + .with_params(PARAMS) .finalize(); - assert!(!tx.check_predicate_owners()); + assert!(!tx.check_predicate_owners(&ConsensusParameters::DEFAULT)); } #[test] @@ -823,7 +825,7 @@ mod inputs { let predicate = (0..1000).map(|_| rng.gen()).collect_vec(); // The predicate is an recipient(owner) of the message - let recipient: Address = (*Contract::root_from_code(&predicate)).into(); + let recipient: Address = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); let tx = TransactionBuilder::create(generate_bytes(rng).into(), rng.gen(), vec![]) .gas_limit(PARAMS.max_gas_per_tx) @@ -838,9 +840,10 @@ mod inputs { predicate, vec![], )) + .with_params(PARAMS) .finalize(); - assert!(tx.check_predicate_owners()); + assert!(tx.check_predicate_owners(&ConsensusParameters::DEFAULT)); } #[test] @@ -862,8 +865,9 @@ mod inputs { predicate, vec![], )) + .with_params(PARAMS) .finalize(); - assert!(!tx.check_predicate_owners()); + assert!(!tx.check_predicate_owners(&ConsensusParameters::DEFAULT)); } } diff --git a/fuel-tx/src/transaction.rs b/fuel-tx/src/transaction.rs index 4bb5abf15a..3b46d6a8dc 100644 --- a/fuel-tx/src/transaction.rs +++ b/fuel-tx/src/transaction.rs @@ -254,7 +254,7 @@ pub trait Executable: field::Inputs + field::Outputs + field::Witnesses { /// Checks that all owners of inputs in the predicates are valid. #[cfg(feature = "std")] - fn check_predicate_owners(&self) -> bool { + fn check_predicate_owners(&self, parameters: &ConsensusParameters) -> bool { self.inputs() .iter() .filter_map(|i| match i { @@ -265,7 +265,7 @@ pub trait Executable: field::Inputs + field::Outputs + field::Witnesses { _ => None, }) .fold(true, |result, (owner, predicate)| { - result && Input::is_predicate_owner_valid(owner, predicate) + result && Input::is_predicate_owner_valid(owner, predicate, parameters) }) } diff --git a/fuel-tx/src/transaction/consensus_parameters.rs b/fuel-tx/src/transaction/consensus_parameters.rs index 0ed14131e1..962e3e9af6 100644 --- a/fuel-tx/src/transaction/consensus_parameters.rs +++ b/fuel-tx/src/transaction/consensus_parameters.rs @@ -32,6 +32,8 @@ pub struct ConsensusParameters { pub gas_per_byte: u64, /// Maximum length of message data, in bytes. pub max_message_data_length: u64, + /// The unique identifier of this chain + pub chain_id: u64, } impl ConsensusParameters { @@ -50,6 +52,7 @@ impl ConsensusParameters { gas_price_factor: 1_000_000_000, gas_per_byte: 4, max_message_data_length: 1024 * 1024, + chain_id: 0, }; /// Transaction memory offset in VM runtime @@ -75,6 +78,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -92,6 +96,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -110,6 +115,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -127,6 +133,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -145,6 +152,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -162,6 +170,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -180,6 +189,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -197,6 +207,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -215,6 +226,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -232,6 +244,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -250,6 +263,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -267,6 +281,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -285,6 +300,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -302,6 +318,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -320,6 +337,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -337,6 +355,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -355,6 +374,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -372,6 +392,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -390,6 +411,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -407,6 +429,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -425,6 +448,7 @@ impl ConsensusParameters { max_predicate_data_length, gas_per_byte, max_message_data_length, + chain_id, .. } = self; @@ -442,6 +466,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -459,6 +484,7 @@ impl ConsensusParameters { max_predicate_data_length, gas_price_factor, max_message_data_length, + chain_id, .. } = self; @@ -476,6 +502,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } @@ -494,6 +521,7 @@ impl ConsensusParameters { max_predicate_data_length, gas_price_factor, gas_per_byte, + chain_id, .. } = self; @@ -511,6 +539,7 @@ impl ConsensusParameters { gas_price_factor, gas_per_byte, max_message_data_length, + chain_id, } } } @@ -540,4 +569,5 @@ pub mod default_parameters { pub const GAS_PRICE_FACTOR: u64 = ConsensusParameters::DEFAULT.gas_price_factor; pub const GAS_PER_BYTE: u64 = ConsensusParameters::DEFAULT.gas_per_byte; pub const MAX_MESSAGE_DATA_LENGTH: u64 = ConsensusParameters::DEFAULT.max_message_data_length; + pub const CHAIN_ID: u64 = ConsensusParameters::DEFAULT.chain_id; } diff --git a/fuel-tx/src/transaction/id.rs b/fuel-tx/src/transaction/id.rs index 6bcbb688f7..8e3c5ace7a 100644 --- a/fuel-tx/src/transaction/id.rs +++ b/fuel-tx/src/transaction/id.rs @@ -1,21 +1,21 @@ use crate::input::coin::CoinSigned; use crate::input::message::{MessageCoinSigned, MessageDataSigned}; -use crate::{field, Input, Transaction}; +use crate::{field, ConsensusParameters, Input, Transaction}; use fuel_crypto::{Message, PublicKey, SecretKey, Signature}; use fuel_types::Bytes32; /// Means that transaction has a unique identifier. pub trait UniqueIdentifier { /// The unique identifier of the transaction is based on its content. - fn id(&self) -> Bytes32; + fn id(&self, params: &ConsensusParameters) -> Bytes32; } impl UniqueIdentifier for Transaction { - fn id(&self) -> Bytes32 { + fn id(&self, params: &ConsensusParameters) -> Bytes32 { match self { - Transaction::Script(script) => script.id(), - Transaction::Create(create) => create.id(), - Self::Mint(mint) => mint.id(), + Transaction::Script(script) => script.id(params), + Transaction::Create(create) => create.id(params), + Self::Mint(mint) => mint.id(params), } } } @@ -25,7 +25,7 @@ impl UniqueIdentifier for Transaction { /// # Note: Autogenerated transactions are not signable. pub trait Signable: UniqueIdentifier { /// Signs inputs of the transaction. - fn sign_inputs(&mut self, secret: &SecretKey); + fn sign_inputs(&mut self, secret: &SecretKey, parameters: &ConsensusParameters); } impl Signable for T @@ -34,12 +34,12 @@ where { /// For all inputs of type `coin` or `message`, check if its `owner` equals the public /// counterpart of the provided key. Sign all matches. - fn sign_inputs(&mut self, secret: &SecretKey) { + fn sign_inputs(&mut self, secret: &SecretKey, parameters: &ConsensusParameters) { use itertools::Itertools; let pk = PublicKey::from(secret); let pk = Input::owner(&pk); - let id = self.id(); + let id = self.id(parameters); let message = Message::from_bytes_ref(&id); @@ -150,12 +150,18 @@ mod tests { let mut tx_p = tx.clone(); let mut tx_q = tx.clone(); - tx_q.precompute(); + tx_q.precompute(&ConsensusParameters::DEFAULT); f(&mut tx_p); - assert_eq!(tx.id(), tx_p.id()); - assert_eq!(tx.id(), tx_q.id()); + assert_eq!( + tx.id(&ConsensusParameters::DEFAULT), + tx_p.id(&ConsensusParameters::DEFAULT) + ); + assert_eq!( + tx.id(&ConsensusParameters::DEFAULT), + tx_q.id(&ConsensusParameters::DEFAULT) + ); } fn assert_id_ne(tx: &Tx, mut f: F) @@ -167,10 +173,16 @@ mod tests { f(&mut tx_p); let mut tx_q = tx_p.clone(); - tx_q.precompute(); - - assert_ne!(tx.id(), tx_p.id()); - assert_ne!(tx.id(), tx_q.id()); + tx_q.precompute(&ConsensusParameters::DEFAULT); + + assert_ne!( + tx.id(&ConsensusParameters::DEFAULT), + tx_p.id(&ConsensusParameters::DEFAULT) + ); + assert_ne!( + tx.id(&ConsensusParameters::DEFAULT), + tx_q.id(&ConsensusParameters::DEFAULT) + ); } macro_rules! assert_io_ne { diff --git a/fuel-tx/src/transaction/metadata.rs b/fuel-tx/src/transaction/metadata.rs index 78951fa49a..d9f4d1688b 100644 --- a/fuel-tx/src/transaction/metadata.rs +++ b/fuel-tx/src/transaction/metadata.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use fuel_types::Bytes32; +use crate::ConsensusParameters; #[cfg(feature = "std")] use crate::{field, UniqueIdentifier}; @@ -12,7 +13,7 @@ pub trait Cacheable { fn is_computed(&self) -> bool; /// Computes the cache for the entity. - fn precompute(&mut self); + fn precompute(&mut self, parameters: &ConsensusParameters); } #[cfg(feature = "std")] @@ -25,11 +26,11 @@ impl Cacheable for super::Transaction { } } - fn precompute(&mut self) { + fn precompute(&mut self, parameters: &ConsensusParameters) { match self { - Self::Script(script) => script.precompute(), - Self::Create(create) => create.precompute(), - Self::Mint(mint) => mint.precompute(), + Self::Script(script) => script.precompute(parameters), + Self::Create(create) => create.precompute(parameters), + Self::Mint(mint) => mint.precompute(parameters), } } } @@ -50,7 +51,7 @@ pub(crate) struct CommonMetadata { #[cfg(feature = "std")] impl CommonMetadata { /// Computes the `Metadata` for the `tx` transaction. - pub fn compute(tx: &Tx) -> Self + pub fn compute(tx: &Tx, params: &ConsensusParameters) -> Self where Tx: UniqueIdentifier, Tx: field::Inputs, @@ -61,7 +62,7 @@ impl CommonMetadata { use fuel_types::bytes::SizedBytes; use itertools::Itertools; - let id = tx.id(); + let id = tx.id(params); let inputs_predicate_offset_at = tx .inputs() diff --git a/fuel-tx/src/transaction/types.rs b/fuel-tx/src/transaction/types.rs index 5c4d335f1a..d38ab29fa7 100644 --- a/fuel-tx/src/transaction/types.rs +++ b/fuel-tx/src/transaction/types.rs @@ -7,10 +7,22 @@ mod storage; mod utxo_id; mod witness; +use crate::{ConsensusParameters, TxId}; pub use create::Create; +use fuel_crypto::Hasher; +use fuel_types::bytes::SerializableVec; pub use mint::Mint; pub use output::{Output, OutputRepr}; pub use script::Script; pub use storage::StorageSlot; pub use utxo_id::UtxoId; pub use witness::Witness; + +pub fn compute_transaction_id(params: &ConsensusParameters, tx: &mut T) -> TxId { + let mut hasher = Hasher::default(); + // chain ID + hasher.input(params.chain_id.to_be_bytes()); + // transaction bytes + hasher.input(tx.to_bytes().as_slice()); + hasher.finalize() +} diff --git a/fuel-tx/src/transaction/types/create.rs b/fuel-tx/src/transaction/types/create.rs index bb1c0dacbc..74a55bf542 100644 --- a/fuel-tx/src/transaction/types/create.rs +++ b/fuel-tx/src/transaction/types/create.rs @@ -1,4 +1,5 @@ use crate::transaction::{ + compute_transaction_id, field::{ BytecodeLength, BytecodeWitnessIndex, GasLimit, GasPrice, Inputs, Maturity, Outputs, Salt as SaltField, StorageSlots, Witnesses, @@ -20,9 +21,6 @@ use std::io; #[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(feature = "std")] -use fuel_types::bytes::SerializableVec; - #[cfg(all(test, feature = "std"))] mod ser_de_tests; @@ -62,7 +60,7 @@ mem_layout!( #[cfg(feature = "std")] impl crate::UniqueIdentifier for Create { - fn id(&self) -> fuel_types::Bytes32 { + fn id(&self, params: &ConsensusParameters) -> fuel_types::Bytes32 { if let Some(CommonMetadata { id, .. }) = self.metadata { return id; } @@ -74,7 +72,7 @@ impl crate::UniqueIdentifier for Create { clone.outputs_mut().iter_mut().for_each(Output::prepare_sign); clone.witnesses_mut().clear(); - fuel_crypto::Hasher::hash(clone.to_bytes().as_slice()) + compute_transaction_id(params, &mut clone) } } @@ -98,15 +96,15 @@ impl Chargeable for Create { impl FormatValidityChecks for Create { #[cfg(feature = "std")] - fn check_signatures(&self) -> Result<(), CheckError> { + fn check_signatures(&self, parameters: &ConsensusParameters) -> Result<(), CheckError> { use crate::UniqueIdentifier; - let id = self.id(); + let id = self.id(parameters); self.inputs() .iter() .enumerate() - .try_for_each(|(index, input)| input.check_signature(index, &id, &self.witnesses))?; + .try_for_each(|(index, input)| input.check_signature(index, &id, &self.witnesses, parameters))?; Ok(()) } @@ -190,9 +188,9 @@ impl crate::Cacheable for Create { self.metadata.is_some() } - fn precompute(&mut self) { + fn precompute(&mut self, parameters: &ConsensusParameters) { self.metadata = None; - self.metadata = Some(CommonMetadata::compute(self)); + self.metadata = Some(CommonMetadata::compute(self, parameters)); } } diff --git a/fuel-tx/src/transaction/types/create/ser_de_tests.rs b/fuel-tx/src/transaction/types/create/ser_de_tests.rs index 523c9a4dbc..b6065f7ba3 100644 --- a/fuel-tx/src/transaction/types/create/ser_de_tests.rs +++ b/fuel-tx/src/transaction/types/create/ser_de_tests.rs @@ -1,4 +1,5 @@ use fuel_types::bytes::Deserializable; +use fuel_types::bytes::SerializableVec; use fuel_types::Bytes32; use super::*; diff --git a/fuel-tx/src/transaction/types/input.rs b/fuel-tx/src/transaction/types/input.rs index 19805a0f0b..a68d4ea034 100644 --- a/fuel-tx/src/transaction/types/input.rs +++ b/fuel-tx/src/transaction/types/input.rs @@ -1,9 +1,9 @@ -use crate::{TxPointer, UtxoId}; +use crate::{ConsensusParameters, TxPointer, UtxoId}; use alloc::vec::Vec; use coin::*; use consts::*; use contract::*; -use fuel_crypto::PublicKey; +use fuel_crypto::{Hasher, PublicKey}; use fuel_types::bytes::{SizedBytes, WORD_SIZE}; use fuel_types::{bytes, BlockHeight, Nonce}; use fuel_types::{Address, AssetId, Bytes32, ContractId, MessageId, Word}; @@ -566,7 +566,7 @@ impl Input { compute_message_id(sender, recipient, nonce, amount, data) } - pub fn predicate_owner

(predicate: P) -> Address + pub fn predicate_owner

(predicate: P, params: &ConsensusParameters) -> Address where P: AsRef<[u8]>, { @@ -574,15 +574,21 @@ impl Input { let root = Contract::root_from_code(predicate); - (*root).into() + let mut hasher = Hasher::default(); + + hasher.input(ContractId::SEED); + hasher.input(params.chain_id.to_be_bytes()); + hasher.input(root); + + (*hasher.digest()).into() } #[cfg(feature = "std")] - pub fn is_predicate_owner_valid

(owner: &Address, predicate: P) -> bool + pub fn is_predicate_owner_valid

(owner: &Address, predicate: P, params: &ConsensusParameters) -> bool where P: AsRef<[u8]>, { - owner == &Self::predicate_owner(predicate) + owner == &Self::predicate_owner(predicate, params) } /// Prepare the output for VM predicate execution diff --git a/fuel-tx/src/transaction/types/mint.rs b/fuel-tx/src/transaction/types/mint.rs index c118257578..93bdd468b7 100644 --- a/fuel-tx/src/transaction/types/mint.rs +++ b/fuel-tx/src/transaction/types/mint.rs @@ -1,4 +1,5 @@ use crate::transaction::{ + compute_transaction_id, field::{Outputs, TxPointer as TxPointerField}, validity::FormatValidityChecks, }; @@ -17,7 +18,7 @@ use std::io; use alloc::vec::Vec; #[cfg(feature = "std")] -use fuel_types::bytes::{self, Deserializable, SerializableVec}; +use fuel_types::bytes::{self, Deserializable}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct MintMetadata { @@ -28,7 +29,7 @@ pub(crate) struct MintMetadata { #[cfg(feature = "std")] impl MintMetadata { - fn compute(tx: &Tx) -> Self + fn compute(tx: &Tx, parameters: &ConsensusParameters) -> Self where Tx: crate::UniqueIdentifier, Tx: Outputs, @@ -36,7 +37,7 @@ impl MintMetadata { { use itertools::Itertools; - let id = tx.id(); + let id = tx.id(parameters); let mut offset = tx.outputs_offset(); @@ -88,19 +89,19 @@ mem_layout!( #[cfg(feature = "std")] impl crate::UniqueIdentifier for Mint { - fn id(&self) -> Bytes32 { + fn id(&self, params: &ConsensusParameters) -> Bytes32 { if let Some(MintMetadata { id, .. }) = self.metadata { return id; } let mut clone = self.clone(); - fuel_crypto::Hasher::hash(clone.to_bytes().as_slice()) + compute_transaction_id(params, &mut clone) } } impl FormatValidityChecks for Mint { #[cfg(feature = "std")] - fn check_signatures(&self) -> Result<(), CheckError> { + fn check_signatures(&self, _: &ConsensusParameters) -> Result<(), CheckError> { Ok(()) } @@ -139,9 +140,9 @@ impl crate::Cacheable for Mint { self.metadata.is_some() } - fn precompute(&mut self) { + fn precompute(&mut self, parameters: &ConsensusParameters) { self.metadata = None; - self.metadata = Some(MintMetadata::compute(self)); + self.metadata = Some(MintMetadata::compute(self, parameters)); } } diff --git a/fuel-tx/src/transaction/types/script.rs b/fuel-tx/src/transaction/types/script.rs index 2ce06e6899..75df6bac64 100644 --- a/fuel-tx/src/transaction/types/script.rs +++ b/fuel-tx/src/transaction/types/script.rs @@ -1,4 +1,5 @@ use crate::transaction::{ + compute_transaction_id, field::{ GasLimit, GasPrice, Inputs, Maturity, Outputs, ReceiptsRoot, Script as ScriptField, ScriptData, Witnesses, }, @@ -20,9 +21,6 @@ use std::io; #[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(feature = "std")] -use fuel_types::bytes::SerializableVec; - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct ScriptMetadata { pub common: CommonMetadata, @@ -85,7 +83,7 @@ impl Default for Script { #[cfg(feature = "std")] impl crate::UniqueIdentifier for Script { - fn id(&self) -> Bytes32 { + fn id(&self, params: &ConsensusParameters) -> Bytes32 { if let Some(ScriptMetadata { common: CommonMetadata { id, .. }, .. @@ -102,7 +100,7 @@ impl crate::UniqueIdentifier for Script { clone.outputs_mut().iter_mut().for_each(Output::prepare_sign); clone.witnesses_mut().clear(); - fuel_crypto::Hasher::hash(clone.to_bytes().as_slice()) + compute_transaction_id(params, &mut clone) } } @@ -126,15 +124,15 @@ impl Chargeable for Script { impl FormatValidityChecks for Script { #[cfg(feature = "std")] - fn check_signatures(&self) -> Result<(), CheckError> { + fn check_signatures(&self, parameters: &ConsensusParameters) -> Result<(), CheckError> { use crate::UniqueIdentifier; - let id = self.id(); + let id = self.id(parameters); self.inputs() .iter() .enumerate() - .try_for_each(|(index, input)| input.check_signature(index, &id, &self.witnesses))?; + .try_for_each(|(index, input)| input.check_signature(index, &id, &self.witnesses, parameters))?; Ok(()) } @@ -172,10 +170,10 @@ impl crate::Cacheable for Script { self.metadata.is_some() } - fn precompute(&mut self) { + fn precompute(&mut self, parameters: &ConsensusParameters) { self.metadata = None; self.metadata = Some(ScriptMetadata { - common: CommonMetadata::compute(self), + common: CommonMetadata::compute(self, parameters), script_data_offset: self.script_data_offset(), }); } diff --git a/fuel-tx/src/transaction/validity.rs b/fuel-tx/src/transaction/validity.rs index f343291f58..d371aba182 100644 --- a/fuel-tx/src/transaction/validity.rs +++ b/fuel-tx/src/transaction/validity.rs @@ -29,13 +29,19 @@ impl Input { parameters: &ConsensusParameters, ) -> Result<(), CheckError> { self.check_without_signature(index, outputs, witnesses, parameters)?; - self.check_signature(index, txhash, witnesses)?; + self.check_signature(index, txhash, witnesses, parameters)?; Ok(()) } #[cfg(feature = "std")] - pub fn check_signature(&self, index: usize, txhash: &Bytes32, witnesses: &[Witness]) -> Result<(), CheckError> { + pub fn check_signature( + &self, + index: usize, + txhash: &Bytes32, + witnesses: &[Witness], + parameters: &ConsensusParameters, + ) -> Result<(), CheckError> { match self { Self::CoinSigned(CoinSigned { witness_index, owner, .. @@ -83,7 +89,9 @@ impl Input { recipient: owner, predicate, .. - }) if !Input::is_predicate_owner_valid(owner, predicate) => Err(CheckError::InputPredicateOwner { index }), + }) if !Input::is_predicate_owner_valid(owner, predicate, parameters) => { + Err(CheckError::InputPredicateOwner { index }) + } _ => Ok(()), } @@ -183,14 +191,14 @@ pub trait FormatValidityChecks { /// of fields according to rules in the specification and validity of signatures. fn check(&self, block_height: BlockHeight, parameters: &ConsensusParameters) -> Result<(), CheckError> { self.check_without_signatures(block_height, parameters)?; - self.check_signatures()?; + self.check_signatures(parameters)?; Ok(()) } #[cfg(feature = "std")] /// Validates that all required signatures are set in the transaction and that they are valid. - fn check_signatures(&self) -> Result<(), CheckError>; + fn check_signatures(&self, parameters: &ConsensusParameters) -> Result<(), CheckError>; /// Validates the transactions according to rules from the specification: /// https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/tx_format/transaction.md#transaction @@ -203,11 +211,11 @@ pub trait FormatValidityChecks { impl FormatValidityChecks for Transaction { #[cfg(feature = "std")] - fn check_signatures(&self) -> Result<(), CheckError> { + fn check_signatures(&self, parameters: &ConsensusParameters) -> Result<(), CheckError> { match self { - Transaction::Script(script) => script.check_signatures(), - Transaction::Create(create) => create.check_signatures(), - Transaction::Mint(mint) => mint.check_signatures(), + Transaction::Script(script) => script.check_signatures(parameters), + Transaction::Create(create) => create.check_signatures(parameters), + Transaction::Mint(mint) => mint.check_signatures(parameters), } } diff --git a/fuel-vm/src/checked_transaction.rs b/fuel-vm/src/checked_transaction.rs index 91707e8a69..7609fe05f9 100644 --- a/fuel-vm/src/checked_transaction.rs +++ b/fuel-vm/src/checked_transaction.rs @@ -91,9 +91,9 @@ impl Checked { } /// Performs check of signatures, if not yet done. - pub fn check_signatures(mut self) -> Result { + pub fn check_signatures(mut self, parameters: &ConsensusParameters) -> Result { if !self.checks_bitmask.contains(Checks::Signatures) { - self.transaction.check_signatures()?; + self.transaction.check_signatures(parameters)?; self.checks_bitmask.insert(Checks::Signatures); } Ok(self) @@ -157,7 +157,7 @@ pub trait IntoChecked: FormatValidityChecks + Sized { Checked: CheckPredicates, { self.into_checked_basic(block_height, params)? - .check_signatures()? + .check_signatures(params)? .check_predicates(params, gas_costs) } @@ -794,7 +794,7 @@ mod tests { .into_checked_basic(block_height, ¶ms) .unwrap() // Sets Checks::Signatures - .check_signatures() + .check_signatures(¶ms) .unwrap(); assert!(checked.checks().contains(Checks::Basic | Checks::Signatures)); @@ -873,7 +873,7 @@ mod tests { fn predicate_tx(rng: &mut StdRng, gas_price: u64, gas_limit: u64, fee_input_amount: u64) -> Script { let asset = AssetId::default(); let predicate = vec![op::ret(1)].into_iter().collect::>(); - let owner = Input::predicate_owner(&predicate); + let owner = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); TransactionBuilder::script(vec![], vec![]) .gas_price(gas_price) .gas_limit(gas_limit) diff --git a/fuel-vm/src/checked_transaction/builder.rs b/fuel-vm/src/checked_transaction/builder.rs index 0a305323d5..07b5d8ca7b 100644 --- a/fuel-vm/src/checked_transaction/builder.rs +++ b/fuel-vm/src/checked_transaction/builder.rs @@ -3,7 +3,6 @@ use super::{Checked, IntoChecked}; use crate::checked_transaction::CheckPredicates; use crate::prelude::*; -use fuel_tx::ConsensusParameters; use fuel_types::BlockHeight; /// Extension trait for [`fuel_tx::TransactionBuilder`] adding finalization methods @@ -12,15 +11,10 @@ where Tx: IntoChecked, { /// Finalize the builder into a [`Checked`] of the correct type - fn finalize_checked( - &mut self, - height: BlockHeight, - params: &ConsensusParameters, - gas_costs: &GasCosts, - ) -> Checked; + fn finalize_checked(&mut self, height: BlockHeight, gas_costs: &GasCosts) -> Checked; /// Finalize the builder into a [`Checked`] of the correct type, with basic checks only - fn finalize_checked_basic(&mut self, height: BlockHeight, params: &ConsensusParameters) -> Checked; + fn finalize_checked_basic(&mut self, height: BlockHeight) -> Checked; } impl TransactionBuilderExt for TransactionBuilder @@ -28,20 +22,15 @@ where Self: Finalizable, Checked: CheckPredicates, { - fn finalize_checked( - &mut self, - height: BlockHeight, - params: &ConsensusParameters, - gas_costs: &GasCosts, - ) -> Checked { + fn finalize_checked(&mut self, height: BlockHeight, gas_costs: &GasCosts) -> Checked { self.finalize() - .into_checked(height, params, gas_costs) + .into_checked(height, self.get_params(), gas_costs) .expect("failed to check tx") } - fn finalize_checked_basic(&mut self, height: BlockHeight, params: &ConsensusParameters) -> Checked { + fn finalize_checked_basic(&mut self, height: BlockHeight) -> Checked { self.finalize() - .into_checked_basic(height, params) + .into_checked_basic(height, self.get_params()) .expect("failed to check tx") } } diff --git a/fuel-vm/src/checked_transaction/types.rs b/fuel-vm/src/checked_transaction/types.rs index 2a1a6f3c78..54209b82fa 100644 --- a/fuel-vm/src/checked_transaction/types.rs +++ b/fuel-vm/src/checked_transaction/types.rs @@ -77,7 +77,7 @@ pub mod create { block_height: BlockHeight, params: &ConsensusParameters, ) -> Result, CheckError> { - self.precompute(); + self.precompute(params); self.check_without_signatures(block_height, params)?; // validate fees and compute free balances @@ -117,7 +117,7 @@ pub mod mint { block_height: BlockHeight, params: &ConsensusParameters, ) -> Result, CheckError> { - self.precompute(); + self.precompute(params); self.check_without_signatures(block_height, params)?; Ok(Checked::basic(self, ())) @@ -159,7 +159,7 @@ pub mod script { block_height: BlockHeight, params: &ConsensusParameters, ) -> Result, CheckError> { - self.precompute(); + self.precompute(params); self.check_without_signatures(block_height, params)?; // validate fees and compute free balances diff --git a/fuel-vm/src/interpreter/executors/main.rs b/fuel-vm/src/interpreter/executors/main.rs index 866ba5596f..ced4fb99a7 100644 --- a/fuel-vm/src/interpreter/executors/main.rs +++ b/fuel-vm/src/interpreter/executors/main.rs @@ -50,7 +50,7 @@ impl Interpreter { Tx: ExecutableTransaction, ::Metadata: CheckedMetadata, { - if !checked.transaction().check_predicate_owners() { + if !checked.transaction().check_predicate_owners(¶ms) { return Err(PredicateVerificationFailed::InvalidOwner); } diff --git a/fuel-vm/src/interpreter/initialization.rs b/fuel-vm/src/interpreter/initialization.rs index 0428ffe8c0..ed93fa4749 100644 --- a/fuel-vm/src/interpreter/initialization.rs +++ b/fuel-vm/src/interpreter/initialization.rs @@ -38,7 +38,7 @@ where // Set heap area self.registers[RegId::HP] = VM_MAX_RAM; - self.push_stack(self.transaction().id().as_ref()) + self.push_stack(self.transaction().id(&self.params).as_ref()) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; RuntimeBalances::try_from(initial_balances)?.to_vm(self); diff --git a/fuel-vm/src/interpreter/internal/tests.rs b/fuel-vm/src/interpreter/internal/tests.rs index f0733fbac0..08cac62760 100644 --- a/fuel-vm/src/interpreter/internal/tests.rs +++ b/fuel-vm/src/interpreter/internal/tests.rs @@ -35,7 +35,7 @@ fn external_balance() { .gas_limit(gas_limit) .gas_limit(100) .maturity(maturity) - .finalize_checked(height, &Default::default(), &Default::default()); + .finalize_checked(height, &Default::default()); vm.init_script(tx).expect("Failed to init VM!"); diff --git a/fuel-vm/src/interpreter/metadata.rs b/fuel-vm/src/interpreter/metadata.rs index 03c240025a..e8e947e16a 100644 --- a/fuel-vm/src/interpreter/metadata.rs +++ b/fuel-vm/src/interpreter/metadata.rs @@ -10,7 +10,7 @@ use fuel_asm::{GMArgs, GTFArgs, PanicReason, RegId}; use fuel_tx::field::{ BytecodeLength, BytecodeWitnessIndex, ReceiptsRoot, Salt, Script as ScriptField, ScriptData, StorageSlots, }; -use fuel_tx::{Input, InputRepr, Output, OutputRepr, UtxoId}; +use fuel_tx::{ConsensusParameters, Input, InputRepr, Output, OutputRepr, UtxoId}; use fuel_types::{Immediate12, Immediate18, RegisterId, Word}; #[cfg(test)] @@ -23,7 +23,7 @@ where pub(crate) fn metadata(&mut self, ra: RegisterId, imm: Immediate18) -> Result<(), RuntimeError> { let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers); let result = &mut w[WriteRegKey::try_from(ra)?]; - metadata(&self.context, &self.frames, pc, result, imm) + metadata(&self.context, &self.params, &self.frames, pc, result, imm) } pub(crate) fn get_transaction_field( @@ -45,6 +45,7 @@ where pub(crate) fn metadata( context: &Context, + params: &ConsensusParameters, frames: &[CallFrame], pc: RegMut, result: &mut Word, @@ -62,6 +63,10 @@ pub(crate) fn metadata( .ok_or(PanicReason::TransactionValidity)?; } + GMArgs::GetChainId => { + *result = params.chain_id; + } + _ => return Err(PanicReason::ExpectedInternalContext.into()), } } else { @@ -79,6 +84,9 @@ pub(crate) fn metadata( *result = parent; } + GMArgs::GetChainId => { + *result = params.chain_id; + } _ => return Err(PanicReason::ExpectedInternalContext.into()), } } diff --git a/fuel-vm/src/interpreter/metadata/tests.rs b/fuel-vm/src/interpreter/metadata/tests.rs index 302ea1f924..2ba7b4171e 100644 --- a/fuel-vm/src/interpreter/metadata/tests.rs +++ b/fuel-vm/src/interpreter/metadata/tests.rs @@ -1,4 +1,6 @@ use fuel_tx::Script; +use fuel_types::BlockHeight; +use test_case::test_case; use super::*; @@ -11,7 +13,15 @@ fn test_metadata() { let mut pc = 4; let mut result = 1; let imm = 0x03; - metadata(&context, &frames, RegMut::new(&mut pc), &mut result, imm).unwrap(); + metadata( + &context, + &ConsensusParameters::DEFAULT, + &frames, + RegMut::new(&mut pc), + &mut result, + imm, + ) + .unwrap(); assert_eq!(pc, 8); assert_eq!(result, 0); } @@ -31,3 +41,22 @@ fn test_get_transaction_field() { assert_eq!(pc, 8); assert_eq!(result, 0); } + +#[test_case(Context::Predicate { program: Default::default() }, 2 => (); "can fetch inside predicate")] +#[test_case(Context::Script { block_height: BlockHeight::default() }, 3 => (); "can fetch inside script")] +#[test_case(Context::Call { block_height: BlockHeight::default() }, 4 => (); "can fetch inside call")] +fn get_chain_id(context: Context, chain_id: u64) { + let mut frames = vec![]; + let mut pc = 4; + let mut result = 1; + let imm = GMArgs::GetChainId as Immediate18; + let mut params = ConsensusParameters::DEFAULT; + params.chain_id = chain_id; + + if context.is_internal() { + frames.push(CallFrame::default()); + } + metadata(&context, ¶ms, &frames, RegMut::new(&mut pc), &mut result, imm).unwrap(); + + assert_eq!(result, chain_id); +} diff --git a/fuel-vm/src/predicate.rs b/fuel-vm/src/predicate.rs index fe8317098e..6aa3cf650c 100644 --- a/fuel-vm/src/predicate.rs +++ b/fuel-vm/src/predicate.rs @@ -98,8 +98,9 @@ fn from_tx_works() { for i in inputs { let tx = TransactionBuilder::script(vec![], vec![]) + .with_params(params) .add_input(i) - .finalize_checked_basic(height, ¶ms); + .finalize_checked_basic(height); // assert invalid idx wont panic let idx = 1; diff --git a/fuel-vm/src/tests/blockchain.rs b/fuel-vm/src/tests/blockchain.rs index ff84727d50..1de1795280 100644 --- a/fuel-vm/src/tests/blockchain.rs +++ b/fuel-vm/src/tests/blockchain.rs @@ -1352,12 +1352,13 @@ fn smo_instruction_works() { amount: 0, asset_id: Default::default(), }) - .finalize_checked(block_height, params, client.gas_costs()); + .with_params(*params) + .finalize_checked(block_height, client.gas_costs()); let non_retryable_free_balance = tx.metadata().non_retryable_balances[&AssetId::BASE]; let retryable_balance: u64 = tx.metadata().retryable_balance.into(); - let txid = tx.transaction().id(); + let txid = tx.transaction().id(params); let receipts = client.transact(tx); let success = receipts.iter().any(|r| { @@ -1476,7 +1477,8 @@ fn timestamp_works() { .gas_price(gas_price) .gas_limit(gas_limit) .maturity(maturity) - .finalize_checked(block_height, ¶ms, client.gas_costs()); + .with_params(params) + .finalize_checked(block_height, client.gas_costs()); let receipts = client.transact(tx); let result = receipts.iter().any(|r| { diff --git a/fuel-vm/src/tests/code_coverage.rs b/fuel-vm/src/tests/code_coverage.rs index 9b49dd3016..c997705380 100644 --- a/fuel-vm/src/tests/code_coverage.rs +++ b/fuel-vm/src/tests/code_coverage.rs @@ -43,7 +43,8 @@ fn code_coverage() { .gas_price(gas_price) .gas_limit(gas_limit) .maturity(maturity) - .finalize_checked(height, ¶ms, &GasCosts::default()); + .with_params(params) + .finalize_checked(height, &GasCosts::default()); #[derive(Clone, Default)] struct ProfilingOutput { diff --git a/fuel-vm/src/tests/crypto.rs b/fuel-vm/src/tests/crypto.rs index 9b7545fa5e..8c229820dd 100644 --- a/fuel-vm/src/tests/crypto.rs +++ b/fuel-vm/src/tests/crypto.rs @@ -56,7 +56,8 @@ fn ecrecover() { .gas_price(gas_price) .gas_limit(gas_limit) .maturity(maturity) - .finalize_checked(height, ¶ms, &gas_costs); + .with_params(params) + .finalize_checked(height, &gas_costs); let receipts = client.transact(tx); let success = receipts.iter().any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); @@ -173,7 +174,8 @@ fn sha256() { .gas_price(gas_price) .gas_limit(gas_limit) .maturity(maturity) - .finalize_checked(height, ¶ms, &gas_costs); + .with_params(params) + .finalize_checked(height, &gas_costs); let receipts = client.transact(tx); let success = receipts.iter().any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); @@ -265,7 +267,8 @@ fn keccak256() { .gas_price(gas_price) .gas_limit(gas_limit) .maturity(maturity) - .finalize_checked(height, ¶ms, client.gas_costs()); + .with_params(params) + .finalize_checked(height, client.gas_costs()); let receipts = client.transact(tx); let success = receipts.iter().any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); diff --git a/fuel-vm/src/tests/metadata.rs b/fuel-vm/src/tests/metadata.rs index 9f7f19912c..c6a2a08a60 100644 --- a/fuel-vm/src/tests/metadata.rs +++ b/fuel-vm/src/tests/metadata.rs @@ -2,9 +2,9 @@ use fuel_asm::{op, GMArgs, GTFArgs, RegId}; use fuel_crypto::Hasher; use fuel_tx::{ field::{Inputs, Outputs, ReceiptsRoot, Script as ScriptField, Witnesses}, - Script, TransactionBuilder, + Finalizable, Receipt, Script, TransactionBuilder, }; -use fuel_types::bytes; +use fuel_types::{bytes, BlockHeight}; use fuel_vm::consts::*; use rand::{rngs::StdRng, Rng, SeedableRng}; @@ -175,6 +175,40 @@ fn metadata() { assert_eq!(&contract_call, digest); } +#[test] +fn get_metadata_chain_id() { + let rng = &mut StdRng::seed_from_u64(2322u64); + let gas_limit = 1_000_000; + let height = BlockHeight::default(); + let params = ConsensusParameters { + chain_id: rng.gen(), + ..Default::default() + }; + let gas_costs = GasCosts::default(); + + let mut client = MemoryClient::new(Default::default(), params, gas_costs.clone()); + + #[rustfmt::skip] + let get_chain_id = vec![ + op::gm_args(0x10, GMArgs::GetChainId), + op::ret(0x10), + ]; + + let script = TransactionBuilder::script(get_chain_id.into_iter().collect(), vec![]) + .gas_limit(gas_limit) + .finalize() + .into_checked(height, ¶ms, &gas_costs) + .unwrap(); + + let receipts = client.transact(script); + + if let Receipt::Return { val, .. } = receipts[0].clone() { + assert_eq!(val, params.chain_id); + } else { + panic!("expected return receipt, instead of {:?}", receipts[0]) + } +} + #[test] fn get_transaction_fields() { let rng = &mut StdRng::seed_from_u64(2322u64); @@ -198,7 +232,8 @@ fn get_transaction_fields() { let tx = TransactionBuilder::create(contract, salt, storage_slots) .add_output(Output::contract_created(contract_id, state_root)) - .finalize_checked(height, ¶ms, client.gas_costs()); + .with_params(params) + .finalize_checked(height, client.gas_costs()); client.deploy(tx); @@ -207,7 +242,7 @@ fn get_transaction_fields() { rng.fill(predicate_data.as_mut_slice()); - let owner = (*Contract::root_from_code(&predicate)).into(); + let owner = Input::predicate_owner(&predicate, &ConsensusParameters::DEFAULT); let input_coin_predicate = Input::coin_predicate( rng.gen(), owner, @@ -232,7 +267,7 @@ fn get_transaction_fields() { rng.fill(m_data.as_mut_slice()); rng.fill(m_predicate_data.as_mut_slice()); - let owner = Input::predicate_owner(&m_predicate); + let owner = Input::predicate_owner(&m_predicate, ¶ms); let message_predicate = Input::message_data_predicate( rng.gen(), owner, @@ -267,7 +302,8 @@ fn get_transaction_fields() { .add_input(message_predicate) .add_unsigned_coin_input(rng.gen(), rng.gen(), asset_amt, asset, rng.gen(), maturity) .add_output(Output::coin(rng.gen(), asset_amt, asset)) - .finalize_checked(height, ¶ms, client.gas_costs()); + .with_params(params) + .finalize_checked(height, client.gas_costs()); let inputs = tx.as_ref().inputs(); let outputs = tx.as_ref().outputs(); @@ -725,7 +761,8 @@ fn get_transaction_fields() { .maturity(maturity) .gas_price(gas_price) .gas_limit(gas_limit) - .finalize_checked_basic(height, ¶ms); + .with_params(params) + .finalize_checked_basic(height); let receipts = client.transact(tx); let success = receipts.iter().any(|r| matches!(r, Receipt::Log{ ra, .. } if ra == &1)); diff --git a/fuel-vm/src/tests/predicate.rs b/fuel-vm/src/tests/predicate.rs index 235088c342..d8ed32acd2 100644 --- a/fuel-vm/src/tests/predicate.rs +++ b/fuel-vm/src/tests/predicate.rs @@ -1,5 +1,5 @@ use fuel_asm::{op, GMArgs, GTFArgs, Instruction, RegId}; -use fuel_tx::TransactionBuilder; +use fuel_tx::{ConsensusParameters, TransactionBuilder}; use rand::{rngs::StdRng, Rng, SeedableRng}; use fuel_vm::prelude::*; @@ -27,7 +27,7 @@ where let height = Default::default(); let params = ConsensusParameters::default(); - let owner = Input::predicate_owner(&predicate); + let owner = Input::predicate_owner(&predicate, ¶ms); let input = Input::coin_predicate( utxo_id, owner, @@ -54,7 +54,7 @@ where builder.add_input(input); - let tx = builder.finalize_checked_basic(height, ¶ms); + let tx = builder.with_params(params).finalize_checked_basic(height); Interpreter::::check_predicates(tx, Default::default(), Default::default()).is_ok() } @@ -143,7 +143,7 @@ fn execute_gas_metered_predicates(predicates: Vec>) -> Result>) -> Result::check_predicates(tx, Default::default(), Default::default()) .map(|r| r.gas_used()) .map_err(|_| ()) @@ -220,7 +220,8 @@ fn gas_used_by_predicates_is_deducted_from_script_gas() { ); let tx_without_predicate = builder - .finalize_checked_basic(Default::default(), ¶ms) + .with_params(params) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()) .expect("Predicate check failed even if we don't have any predicates"); @@ -235,7 +236,7 @@ fn gas_used_by_predicates_is_deducted_from_script_gas() { .into_iter() .flat_map(|op| u32::from(op).to_be_bytes()) .collect(); - let owner = Input::predicate_owner(&predicate); + let owner = Input::predicate_owner(&predicate, ¶ms); let input = Input::coin_predicate( rng.gen(), owner, @@ -250,7 +251,7 @@ fn gas_used_by_predicates_is_deducted_from_script_gas() { builder.add_input(input); let tx_with_predicate = builder - .finalize_checked_basic(Default::default(), &ConsensusParameters::default()) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()) .expect("Predicate check failed"); @@ -293,7 +294,7 @@ fn gas_used_by_predicates_causes_out_of_gas_during_script() { ); let tx_without_predicate = builder - .finalize_checked_basic(Default::default(), &ConsensusParameters::default()) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()) .expect("Predicate check failed even if we don't have any predicates"); @@ -311,7 +312,7 @@ fn gas_used_by_predicates_causes_out_of_gas_during_script() { .into_iter() .flat_map(|op| u32::from(op).to_be_bytes()) .collect(); - let owner = Input::predicate_owner(&predicate); + let owner = Input::predicate_owner(&predicate, ¶ms); let input = Input::coin_predicate( rng.gen(), owner, @@ -326,7 +327,7 @@ fn gas_used_by_predicates_causes_out_of_gas_during_script() { builder.add_input(input); let tx_with_predicate = builder - .finalize_checked_basic(Default::default(), &ConsensusParameters::default()) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()) .expect("Predicate check failed"); @@ -369,7 +370,7 @@ fn gas_used_by_predicates_more_than_limit() { ); let tx_without_predicate = builder - .finalize_checked_basic(Default::default(), &ConsensusParameters::default()) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()) .expect("Predicate check failed even if we don't have any predicates"); @@ -393,7 +394,7 @@ fn gas_used_by_predicates_more_than_limit() { .into_iter() .flat_map(|op| u32::from(op).to_be_bytes()) .collect(); - let owner = Input::predicate_owner(&predicate); + let owner = Input::predicate_owner(&predicate, ¶ms); let input = Input::coin_predicate( rng.gen(), owner, @@ -408,7 +409,7 @@ fn gas_used_by_predicates_more_than_limit() { builder.add_input(input); let tx_with_predicate = builder - .finalize_checked_basic(Default::default(), &ConsensusParameters::default()) + .finalize_checked_basic(Default::default()) .check_predicates(¶ms, &GasCosts::default()); assert_eq!(tx_with_predicate.unwrap_err(), CheckError::PredicateExhaustedGas); diff --git a/fuel-vm/src/tests/profile_gas.rs b/fuel-vm/src/tests/profile_gas.rs index b4e616474d..e24384cdf1 100644 --- a/fuel-vm/src/tests/profile_gas.rs +++ b/fuel-vm/src/tests/profile_gas.rs @@ -40,7 +40,8 @@ fn profile_gas() { .gas_limit(gas_limit) .gas_price(gas_price) .maturity(maturity) - .finalize_checked(height, ¶ms, &GasCosts::default()); + .with_params(params) + .finalize_checked(height, &GasCosts::default()); let output = GasProfiler::default(); diff --git a/fuel-vm/src/tests/validation.rs b/fuel-vm/src/tests/validation.rs index 950c87087d..f7975b65c3 100644 --- a/fuel-vm/src/tests/validation.rs +++ b/fuel-vm/src/tests/validation.rs @@ -25,7 +25,8 @@ fn transaction_can_be_executed_after_maturity() { ) .gas_limit(100) .maturity(MATURITY) - .finalize_checked(BLOCK_HEIGHT, ¶ms, &gas_costs); + .with_params(params) + .finalize_checked(BLOCK_HEIGHT, &gas_costs); let result = TestBuilder::new(2322u64).block_height(BLOCK_HEIGHT).execute_tx(tx); assert!(result.is_ok()); diff --git a/fuel-vm/src/util.rs b/fuel-vm/src/util.rs index d12d481719..c408315af2 100644 --- a/fuel-vm/src/util.rs +++ b/fuel-vm/src/util.rs @@ -231,8 +231,8 @@ pub mod test_helpers { } pub fn build(&mut self) -> Checked