diff --git a/CHANGELOG.md b/CHANGELOG.md index f556cbb52e..c1fcdcfe00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Description of the upcoming release here. -### Breaking +### Fixed + +#### Breaking - [#502](https://github.com/FuelLabs/fuel-vm/pull/502) The algorithm used by the binary Merkle tree for generating Merkle proofs has been updated to remove the leaf data from the proof set. This change allows BMT proofs to conform to the format expected by the Solidity contracts used for verifying proofs. +- [#503](https://github.com/FuelLabs/fuel-vm/pull/503): Use correct amount of gas in call + receipts when limited by cgas. Before this change, the `Receipt::Call` could show an incorrect value for the gas limit. + +- [#504](https://github.com/FuelLabs/fuel-vm/pull/504): The `CROO` and `CSIZ` opcodes require + the existence of corresponding `ContractId` in the transaction's + inputs(the same behavior as for the `CROO` opcode). + +- [#504](https://github.com/FuelLabs/fuel-vm/pull/504): The size of the contract + was incorrectly padded. It affects the end of the call frame in the memory, + making it not 8 bytes align. Also, it affects the cost of the contract + call(in some cases, we charged less in some more). + +- [#504](https://github.com/FuelLabs/fuel-vm/pull/504): The charging for `DependentCost` + was done incorrectly, devaluing the `dep_per_unit` part. After the fixing of + this, the execution should become much more expensive. + ## [Version 0.34.1] Mainly new opcodes prices and small performance improvements in the `BinaryMerkleTree`. diff --git a/fuel-vm/src/call.rs b/fuel-vm/src/call.rs index 2a36cf944d..d28fb08b36 100644 --- a/fuel-vm/src/call.rs +++ b/fuel-vm/src/call.rs @@ -258,7 +258,8 @@ impl CallFrame { /// Padding to the next word boundary. pub fn code_size_padding(&self) -> Word { - self.code_size() % WORD_SIZE as Word + const WORD_SIZE: Word = crate::consts::WORD_SIZE as Word; + (WORD_SIZE - self.code_size() % WORD_SIZE) % WORD_SIZE } /// Total code size including padding. diff --git a/fuel-vm/src/interpreter.rs b/fuel-vm/src/interpreter.rs index 130efb2a25..e53f224125 100644 --- a/fuel-vm/src/interpreter.rs +++ b/fuel-vm/src/interpreter.rs @@ -461,3 +461,27 @@ impl CheckedMetadata for CreateCheckedMetadata { self.gas_used_by_predicates = gas_used; } } + +pub(crate) struct InputContracts<'vm, I> { + tx_input_contracts: I, + panic_context: &'vm mut PanicContext, +} + +impl<'vm, I: Iterator> InputContracts<'vm, I> { + pub fn new(tx_input_contracts: I, panic_context: &'vm mut PanicContext) -> Self { + Self { + tx_input_contracts, + panic_context, + } + } + + /// Checks that the contract is declared in the transaction inputs. + pub fn check(&mut self, contract: &ContractId) -> Result<(), PanicReason> { + if !self.tx_input_contracts.any(|input| input == contract) { + *self.panic_context = PanicContext::ContractId(*contract); + Err(PanicReason::ContractNotInInputs) + } else { + Ok(()) + } + } +} diff --git a/fuel-vm/src/interpreter/blockchain.rs b/fuel-vm/src/interpreter/blockchain.rs index bcb48919a9..36f599576c 100644 --- a/fuel-vm/src/interpreter/blockchain.rs +++ b/fuel-vm/src/interpreter/blockchain.rs @@ -53,7 +53,7 @@ use crate::{ gas::DependentCost, interpreter::{ receipts::ReceiptsCtx, - PanicContext, + InputContracts, }, prelude::Profiler, storage::{ @@ -63,7 +63,6 @@ use crate::{ InterpreterStorage, }, }; - use fuel_asm::PanicReason; use fuel_storage::{ StorageInspect, @@ -128,8 +127,10 @@ where memory: &mut self.memory, storage: &mut self.storage, contract_max_size: self.params.contract_max_size, - input_contracts: self.tx.input_contracts(), - panic_context: &mut self.panic_context, + input_contracts: InputContracts::new( + self.tx.input_contracts(), + &mut self.panic_context, + ), ssp, sp, fp: fp.as_ref(), @@ -173,8 +174,10 @@ where let owner = self.ownership_registers(); let input = CodeCopyCtx { memory: &mut self.memory, - input_contracts: self.tx.input_contracts(), - panic_context: &mut self.panic_context, + input_contracts: InputContracts::new( + self.tx.input_contracts(), + &mut self.panic_context, + ), storage: &mut self.storage, owner, pc: self.registers.pc_mut(), @@ -213,14 +216,17 @@ where pub(crate) fn code_root(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> { let owner = self.ownership_registers(); - code_root( - &self.storage, - &mut self.memory, + CodeRootCtx { + memory: &mut self.memory, + input_contracts: InputContracts::new( + self.tx.input_contracts(), + &mut self.panic_context, + ), + storage: &self.storage, owner, - self.registers.pc_mut(), - a, - b, - ) + pc: self.registers.pc_mut(), + } + .code_root(a, b) } pub(crate) fn code_size( @@ -243,6 +249,10 @@ where storage: &mut self.storage, gas_cost: self.gas_costs.csiz, profiler: &mut self.profiler, + input_contracts: InputContracts::new( + self.tx.input_contracts(), + &mut self.panic_context, + ), current_contract, cgas, ggas, @@ -419,8 +429,7 @@ where struct LoadContractCodeCtx<'vm, S, I> { contract_max_size: u64, memory: &'vm mut [u8; MEM_SIZE], - input_contracts: I, - panic_context: &'vm mut PanicContext, + input_contracts: InputContracts<'vm, I>, storage: &'vm S, ssp: RegMut<'vm, SSP>, sp: RegMut<'vm, SP>, @@ -484,11 +493,7 @@ impl<'vm, S, I> LoadContractCodeCtx<'vm, S, I> { // Safety: Memory bounds are checked and consistent let contract_id = ContractId::from_bytes_ref(contract_id); - // the contract must be declared in the transaction inputs - if !self.input_contracts.any(|id| id == contract_id) { - *self.panic_context = PanicContext::ContractId(*contract_id); - return Err(PanicReason::ContractNotInInputs.into()) - }; + self.input_contracts.check(contract_id)?; // fetch the storage contract let contract = super::contract::contract(self.storage, contract_id)?; @@ -603,8 +608,7 @@ where struct CodeCopyCtx<'vm, S, I> { memory: &'vm mut [u8; MEM_SIZE], - input_contracts: I, - panic_context: &'vm mut PanicContext, + input_contracts: InputContracts<'vm, I>, storage: &'vm S, owner: OwnershipRegisters, pc: RegMut<'vm, PC>, @@ -637,10 +641,7 @@ impl<'vm, S, I> CodeCopyCtx<'vm, S, I> { let contract = ContractId::from_bytes_ref(contract.read(self.memory)); - if !self.input_contracts.any(|input| input == contract) { - *self.panic_context = PanicContext::ContractId(*contract); - return Err(PanicReason::ContractNotInInputs.into()) - } + self.input_contracts.check(contract)?; let contract = super::contract::contract(self.storage, contract)?.into_owned(); @@ -698,43 +699,50 @@ pub(crate) fn coinbase( inc_pc(pc) } -pub(crate) fn code_root( - storage: &S, - memory: &mut [u8; MEM_SIZE], +struct CodeRootCtx<'vm, S, I> { + memory: &'vm mut [u8; MEM_SIZE], + input_contracts: InputContracts<'vm, I>, + storage: &'vm S, owner: OwnershipRegisters, - pc: RegMut, - a: Word, - b: Word, -) -> Result<(), RuntimeError> -where - S: InterpreterStorage, -{ - let ax = checked_add_word(a, Bytes32::LEN as Word)?; - let contract_id = CheckedMemConstLen::<{ ContractId::LEN }>::new(b)?; + pc: RegMut<'vm, PC>, +} - if ax > VM_MAX_RAM { - return Err(PanicReason::MemoryOverflow.into()) - } +impl<'vm, S, I: Iterator> CodeRootCtx<'vm, S, I> { + pub(crate) fn code_root(mut self, a: Word, b: Word) -> Result<(), RuntimeError> + where + S: InterpreterStorage, + { + let ax = checked_add_word(a, Bytes32::LEN as Word)?; + let contract_id = CheckedMemConstLen::<{ ContractId::LEN }>::new(b)?; - let contract_id = ContractId::from_bytes_ref(contract_id.read(memory)); + if ax > VM_MAX_RAM { + return Err(PanicReason::MemoryOverflow.into()) + } - let (_, root) = storage - .storage_contract_root(contract_id) - .transpose() - .ok_or(PanicReason::ContractNotFound)? - .map_err(RuntimeError::from_io)? - .into_owned(); + let contract_id = ContractId::from_bytes_ref(contract_id.read(self.memory)); - try_mem_write(a, root.as_ref(), owner, memory)?; + self.input_contracts.check(contract_id)?; - inc_pc(pc) + let (_, root) = self + .storage + .storage_contract_root(contract_id) + .transpose() + .ok_or(PanicReason::ContractNotFound)? + .map_err(RuntimeError::from_io)? + .into_owned(); + + try_mem_write(a, root.as_ref(), self.owner, self.memory)?; + + inc_pc(self.pc) + } } -struct CodeSizeCtx<'vm, S> { +struct CodeSizeCtx<'vm, S, I> { storage: &'vm S, memory: &'vm mut [u8; MEM_SIZE], gas_cost: DependentCost, profiler: &'vm mut Profiler, + input_contracts: InputContracts<'vm, I>, current_contract: Option, cgas: RegMut<'vm, CGAS>, ggas: RegMut<'vm, GGAS>, @@ -742,8 +750,12 @@ struct CodeSizeCtx<'vm, S> { is: Reg<'vm, IS>, } -impl<'vm, S> CodeSizeCtx<'vm, S> { - pub(crate) fn code_size(self, result: &mut Word, b: Word) -> Result<(), RuntimeError> +impl<'vm, S, I: Iterator> CodeSizeCtx<'vm, S, I> { + pub(crate) fn code_size( + mut self, + result: &mut Word, + b: Word, + ) -> Result<(), RuntimeError> where S: StorageSize, >::Error: Into, @@ -752,6 +764,8 @@ impl<'vm, S> CodeSizeCtx<'vm, S> { let contract_id = ContractId::from_bytes_ref(contract_id.read(self.memory)); + self.input_contracts.check(contract_id)?; + let len = contract_size(self.storage, contract_id)?; let profiler = ProfileGas { pc: self.pc.as_ref(), diff --git a/fuel-vm/src/interpreter/blockchain/code_tests.rs b/fuel-vm/src/interpreter/blockchain/code_tests.rs index 3801015aab..ecbe5dd4f5 100644 --- a/fuel-vm/src/interpreter/blockchain/code_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/code_tests.rs @@ -1,9 +1,11 @@ +use super::*; use crate::{ - interpreter::memory::Memory, + interpreter::{ + memory::Memory, + PanicContext, + }, storage::MemoryStorage, }; - -use super::*; use fuel_tx::Contract; #[test] @@ -29,13 +31,13 @@ fn test_load_contract() -> Result<(), RuntimeError> { .storage_contract_insert(&contract_id, &Contract::from(vec![5u8; 400])) .unwrap(); + let mut panic_context = PanicContext::None; let input_contracts = vec![contract_id]; let input = LoadContractCodeCtx { contract_max_size: 100, storage: &storage, memory: &mut memory, - panic_context: &mut PanicContext::None, - input_contracts: input_contracts.iter(), + input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context), ssp: RegMut::new(&mut ssp), sp: RegMut::new(&mut sp), fp: Reg::new(&fp), @@ -69,11 +71,11 @@ fn test_code_copy() -> Result<(), RuntimeError> { .unwrap(); let input_contracts = vec![contract_id]; + let mut panic_context = PanicContext::None; let input = CodeCopyCtx { storage: &storage, memory: &mut memory, - panic_context: &mut PanicContext::None, - input_contracts: input_contracts.iter(), + input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context), pc: RegMut::new(&mut pc), owner: OwnershipRegisters { sp: 1000, diff --git a/fuel-vm/src/interpreter/blockchain/other_tests.rs b/fuel-vm/src/interpreter/blockchain/other_tests.rs index 97516bcc5e..facb397c6b 100644 --- a/fuel-vm/src/interpreter/blockchain/other_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/other_tests.rs @@ -2,8 +2,10 @@ use crate::{ interpreter::memory::Memory, storage::MemoryStorage, }; +use std::iter; use super::*; +use crate::interpreter::PanicContext; use fuel_storage::StorageAsMut; use fuel_types::Salt; use test_case::test_case; @@ -167,9 +169,10 @@ fn test_coinbase() { #[test] fn test_code_root() { + let contract_id = ContractId::new([3u8; ContractId::LEN]); let mut storage = MemoryStorage::new(Default::default(), Default::default()); let mut memory: Memory = vec![1u8; MEM_SIZE].try_into().unwrap(); - memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]); + memory[0..ContractId::LEN].copy_from_slice(contract_id.as_slice()); let owner = OwnershipRegisters { sp: 1000, ssp: 1, @@ -180,8 +183,17 @@ fn test_code_root() { }, }; let mut pc = 4; - code_root(&storage, &mut memory, owner, RegMut::new(&mut pc), 20, 0) - .expect_err("Contract is not found"); + let input_contracts = vec![contract_id]; + let mut panic_context = PanicContext::None; + CodeRootCtx { + memory: &mut memory, + input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context), + storage: &storage, + owner, + pc: RegMut::new(&mut pc), + } + .code_root(20, 0) + .expect_err("Contract is not found"); assert_eq!(pc, 4); storage @@ -200,16 +212,44 @@ fn test_code_root() { block_height: Default::default(), }, }; - code_root(&storage, &mut memory, owner, RegMut::new(&mut pc), 20, 0).unwrap(); + CodeRootCtx { + memory: &mut memory, + input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context), + storage: &storage, + owner, + pc: RegMut::new(&mut pc), + } + .code_root(20, 0) + .unwrap(); assert_eq!(pc, 8); assert_eq!(memory[20..20 + 32], [6u8; 32]); + + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1, + hp: 2000, + prev_hp: 3000, + context: Context::Script { + block_height: Default::default(), + }, + }; + CodeRootCtx { + memory: &mut memory, + input_contracts: InputContracts::new(iter::empty(), &mut panic_context), + storage: &storage, + owner, + pc: RegMut::new(&mut pc), + } + .code_root(20, 0) + .expect_err("Contract is not in inputs"); } #[test] fn test_code_size() { + let contract_id = ContractId::new([3u8; ContractId::LEN]); let mut storage = MemoryStorage::new(Default::default(), Default::default()); let mut memory: Memory = vec![1u8; MEM_SIZE].try_into().unwrap(); - memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]); + memory[0..ContractId::LEN].copy_from_slice(contract_id.as_slice()); StorageAsMut::storage::(&mut storage) .write(&ContractId::from([3u8; 32]), vec![1u8; 100]) .unwrap(); @@ -217,6 +257,8 @@ fn test_code_size() { let is = 0; let mut cgas = 0; let mut ggas = 0; + let input_contract = vec![contract_id]; + let mut panic_context = PanicContext::None; let input = CodeSizeCtx { storage: &mut storage, memory: &mut memory, @@ -225,6 +267,7 @@ fn test_code_size() { dep_per_unit: 0, }, profiler: &mut Profiler::default(), + input_contracts: InputContracts::new(input_contract.iter(), &mut panic_context), current_contract: None, cgas: RegMut::new(&mut cgas), ggas: RegMut::new(&mut ggas), @@ -244,6 +287,7 @@ fn test_code_size() { base: 0, dep_per_unit: 0, }, + input_contracts: InputContracts::new(input_contract.iter(), &mut panic_context), profiler: &mut Profiler::default(), current_contract: None, cgas: RegMut::new(&mut cgas), @@ -255,6 +299,26 @@ fn test_code_size() { input.code_size(&mut result, 0).unwrap(); assert_eq!(pc, 8); assert_eq!(result, 100); + + let input = CodeSizeCtx { + storage: &mut storage, + memory: &mut memory, + gas_cost: DependentCost { + base: 0, + dep_per_unit: 0, + }, + input_contracts: InputContracts::new(iter::empty(), &mut panic_context), + profiler: &mut Profiler::default(), + current_contract: None, + cgas: RegMut::new(&mut cgas), + ggas: RegMut::new(&mut ggas), + pc: RegMut::new(&mut pc), + is: Reg::new(&is), + }; + let mut result = 0; + input + .code_size(&mut result, 0) + .expect_err("The contract is not in the input"); } #[test] diff --git a/fuel-vm/src/interpreter/contract.rs b/fuel-vm/src/interpreter/contract.rs index 778c6648af..0a175a056b 100644 --- a/fuel-vm/src/interpreter/contract.rs +++ b/fuel-vm/src/interpreter/contract.rs @@ -21,6 +21,7 @@ use crate::{ error::RuntimeError, interpreter::{ receipts::ReceiptsCtx, + InputContracts, PanicContext, }, storage::{ @@ -30,7 +31,6 @@ use crate::{ InterpreterStorage, }, }; - use fuel_asm::{ PanicReason, RegisterId, @@ -73,8 +73,10 @@ where storage: &self.storage, memory: &mut self.memory, pc, - panic_context: &mut self.panic_context, - input_contracts: self.tx.input_contracts(), + input_contracts: InputContracts::new( + self.tx.input_contracts(), + &mut self.panic_context, + ), }; input.contract_balance(result, b, c) } @@ -153,8 +155,7 @@ struct ContractBalanceCtx<'vm, S, I> { storage: &'vm S, memory: &'vm mut [u8; MEM_SIZE], pc: RegMut<'vm, PC>, - input_contracts: I, - panic_context: &'vm mut PanicContext, + input_contracts: InputContracts<'vm, I>, } impl<'vm, S, I> ContractBalanceCtx<'vm, S, I> { @@ -175,10 +176,7 @@ impl<'vm, S, I> ContractBalanceCtx<'vm, S, I> { let asset_id = AssetId::from_bytes_ref(asset_id.read(self.memory)); let contract = ContractId::from_bytes_ref(contract.read(self.memory)); - if !self.input_contracts.any(|input| contract == input) { - *self.panic_context = PanicContext::ContractId(*contract); - return Err(PanicReason::ContractNotInInputs.into()) - } + self.input_contracts.check(contract)?; let balance = balance(self.storage, contract, asset_id)?; @@ -199,6 +197,7 @@ struct TransferCtx<'vm, S, Tx> { is: Reg<'vm, IS>, pc: RegMut<'vm, PC>, } + impl<'vm, S, Tx> TransferCtx<'vm, S, Tx> { pub(crate) fn transfer( self, @@ -232,14 +231,8 @@ impl<'vm, S, Tx> TransferCtx<'vm, S, Tx> { let asset_id = AssetId::try_from(&self.memory[c as usize..cx as usize]) .expect("Unreachable! Checked memory range"); - if !self - .tx - .input_contracts() - .any(|contract| &destination == contract) - { - *panic_context = PanicContext::ContractId(destination); - return Err(PanicReason::ContractNotInInputs.into()) - } + InputContracts::new(self.tx.input_contracts(), panic_context) + .check(&destination)?; if amount == 0 { return Err(PanicReason::NotEnoughBalance.into()) diff --git a/fuel-vm/src/interpreter/contract/tests.rs b/fuel-vm/src/interpreter/contract/tests.rs index 018feb4abd..5b59e52480 100644 --- a/fuel-vm/src/interpreter/contract/tests.rs +++ b/fuel-vm/src/interpreter/contract/tests.rs @@ -32,12 +32,15 @@ fn test_contract_balance(b: Word, c: Word) -> Result<(), RuntimeError> { .unwrap(); let mut pc = 4; + let mut panic_context = PanicContext::None; let input = ContractBalanceCtx { storage: &mut storage, memory: &mut memory, pc: RegMut::new(&mut pc), - input_contracts: [&contract_id].into_iter(), - panic_context: &mut PanicContext::None, + input_contracts: InputContracts::new( + [&contract_id].into_iter(), + &mut panic_context, + ), }; let mut result = 0; diff --git a/fuel-vm/src/interpreter/flow.rs b/fuel-vm/src/interpreter/flow.rs index 16acb5c500..091b4a3807 100644 --- a/fuel-vm/src/interpreter/flow.rs +++ b/fuel-vm/src/interpreter/flow.rs @@ -38,6 +38,7 @@ use crate::{ gas::DependentCost, interpreter::{ receipts::ReceiptsCtx, + InputContracts, PanicContext, }, profiler::Profiler, @@ -48,7 +49,6 @@ use crate::{ InterpreterStorage, }, }; - use fuel_asm::{ Instruction, PanicInstruction, @@ -391,7 +391,7 @@ where current_contract(&self.context, self.registers.fp(), self.memory.as_ref())? .copied(); let memory = PrepareCallMemory::try_from((self.memory.as_mut(), ¶ms))?; - let input_contracts = self.tx.input_contracts().copied().collect(); + let input_contracts = self.tx.input_contracts().copied().collect::>(); PrepareCallCtx { params, @@ -401,8 +401,10 @@ where gas_cost: self.gas_costs.call, runtime_balances: &mut self.balances, storage: &mut self.storage, - input_contracts, - panic_context: &mut self.panic_context, + input_contracts: InputContracts::new( + input_contracts.iter(), + &mut self.panic_context, + ), receipts: &mut self.receipts, script: self.tx.as_script_mut(), consensus: &self.params, @@ -466,7 +468,7 @@ struct PrepareCallMemory<'a> { asset_id: CheckedMemValue, } -struct PrepareCallCtx<'vm, S> { +struct PrepareCallCtx<'vm, S, I> { params: PrepareCallParams, registers: PrepareCallRegisters<'vm>, memory: PrepareCallMemory<'vm>, @@ -474,8 +476,7 @@ struct PrepareCallCtx<'vm, S> { gas_cost: DependentCost, runtime_balances: &'vm mut RuntimeBalances, storage: &'vm mut S, - input_contracts: Vec, - panic_context: &'vm mut PanicContext, + input_contracts: InputContracts<'vm, I>, receipts: &'vm mut ReceiptsCtx, script: Option<&'vm mut Script>, consensus: &'vm ConsensusParameters, @@ -484,7 +485,10 @@ struct PrepareCallCtx<'vm, S> { profiler: &'vm mut Profiler, } -impl<'vm, S> PrepareCallCtx<'vm, S> { +impl<'vm, S, I> PrepareCallCtx<'vm, S, I> +where + I: Iterator, +{ fn prepare_call(mut self) -> Result<(), RuntimeError> where S: StorageSize @@ -535,14 +539,7 @@ impl<'vm, S> PrepareCallCtx<'vm, S> { )?; } - if !self - .input_contracts - .iter() - .any(|contract| call.to() == contract) - { - *self.panic_context = PanicContext::ContractId(*call.to()); - return Err(PanicReason::ContractNotInInputs.into()) - } + self.input_contracts.check(call.to())?; // credit contract asset_id balance balance_increase( diff --git a/fuel-vm/src/interpreter/flow/tests.rs b/fuel-vm/src/interpreter/flow/tests.rs index ce7c8417d6..74d456a723 100644 --- a/fuel-vm/src/interpreter/flow/tests.rs +++ b/fuel-vm/src/interpreter/flow/tests.rs @@ -106,7 +106,7 @@ impl Default for Output { frames: vec![CallFrame::new( Default::default(), Default::default(), - make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 10), (GGAS, 10)]), + make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 0)]), 10, 0, 0, @@ -147,11 +147,11 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 21, ggas: 21 }, + reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Script{ block_height: Default::default() }, ..Default::default() } => using check_output(Ok(Output{ - reg: RegInput{hp: 1000, sp: 712, ssp: 712, fp: 100, pc: 700, is: 700, bal: 0, cgas: 0, ggas: 10 }, + reg: RegInput{hp: 1000, sp: 716, ssp: 716, fp: 100, pc: 700, is: 700, bal: 0, cgas: 0, ggas: 0 }, ..Default::default() })); "basic call working" )] @@ -163,7 +163,7 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 2000, amount_of_gas_to_forward: 30, }, - reg: RegInput{hp: 1000, sp: 200, ssp: 200, fp: 0, pc: 0, is: 0, bal: 0, cgas: 201, ggas: 201 }, + reg: RegInput{hp: 1000, sp: 200, ssp: 200, fp: 0, pc: 0, is: 0, bal: 0, cgas: 1080, ggas: 1150 }, context: Context::Script{ block_height: Default::default() }, input_contracts: vec![ContractId::from([1u8; 32])], memory: mem(&[(2000, vec![2; 32]), (2032, Call::new(ContractId::from([1u8; 32]), 4, 5).into())]), @@ -172,12 +172,12 @@ fn mem(set: &[(usize, Vec)]) -> Memory { script: Some(Default::default()), ..Default::default() } => using check_output({ - let frame = CallFrame::new(ContractId::from([1u8; 32]), AssetId::from([2u8; 32]), make_reg(&[(HP, 1000), (SP, 200), (SSP, 200), (CGAS, 151), (GGAS, 181)]), 100, 4, 5); + let frame = CallFrame::new(ContractId::from([1u8; 32]), AssetId::from([2u8; 32]), make_reg(&[(HP, 1000), (SP, 200), (SSP, 200), (CGAS, 0), (GGAS, 100)]), 100, 4, 5); let mut receipt = Receipt::call(ContractId::zeroed(), ContractId::from([1u8; 32]), 20, AssetId::from([2u8; 32]), 30, 4, 5, 800, 800); let mut script = Script::default(); *script.receipts_root_mut() = crypto::ephemeral_merkle_root([receipt.to_bytes()].into_iter()); Ok(Output{ - reg: RegInput{hp: 1000, sp: 904, ssp: 904, fp: 200, pc: 800, is: 800, bal: 20, cgas: 30, ggas: 181 }, + reg: RegInput{hp: 1000, sp: 904, ssp: 904, fp: 200, pc: 800, is: 800, bal: 20, cgas: 30, ggas: 100 }, receipts: vec![receipt].into(), frames: vec![frame.clone()], memory: CheckMem::Check(vec![(200, frame.into()), (2000, vec![2; 32]), (2032, Call::new(ContractId::from([1u8; 32]), 4, 5).into())]), @@ -194,12 +194,12 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 11, ggas: 11 }, + reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Script{ block_height: Default::default() }, balance: [(AssetId::default(), 30)].into_iter().collect(), ..Default::default() } => using check_output(Ok(Output{ - reg: RegInput{hp: 1000, sp: 712, ssp: 712, fp: 100, pc: 700, is: 700, bal: 20, cgas: 0, ggas: 0 }, + reg: RegInput{hp: 1000, sp: 716, ssp: 716, fp: 100, pc: 700, is: 700, bal: 20, cgas: 0, ggas: 0 }, receipts: vec![Receipt::call(Default::default(), Default::default(), 20, Default::default(), 0, 0, 0, 700, 700)].into(), frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 0)]), 10, 0, 0)], ..Default::default() @@ -213,14 +213,14 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 10, }, - reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 40, ggas: 80 }, + reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 180, ggas: 200 }, context: Context::Script{ block_height: Default::default() }, balance: [(AssetId::default(), 30)].into_iter().collect(), ..Default::default() } => using check_output(Ok(Output{ - reg: RegInput{hp: 1000, sp: 712, ssp: 712, fp: 100, pc: 700, is: 700, bal: 20, cgas: 10, ggas: 69 }, + reg: RegInput{hp: 1000, sp: 716, ssp: 716, fp: 100, pc: 700, is: 700, bal: 20, cgas: 10, ggas: 30 }, receipts: vec![Receipt::call(Default::default(), Default::default(), 20, Default::default(), 10, 0, 0, 700, 700)].into(), - frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 19), (GGAS, 69)]), 10, 0, 0)], + frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 30)]), 10, 0, 0)], ..Default::default() })); "forwards gas" )] @@ -232,14 +232,14 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 100, }, - reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 40, ggas: 80 }, + reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 180, ggas: 200 }, context: Context::Script{ block_height: Default::default() }, balance: [(AssetId::default(), 30)].into_iter().collect(), ..Default::default() } => using check_output(Ok(Output{ - reg: RegInput{hp: 1000, sp: 712, ssp: 712, fp: 100, pc: 700, is: 700, bal: 20, cgas: 29, ggas: 69 }, - receipts: vec![Receipt::call(Default::default(), Default::default(), 20, Default::default(), 29, 0, 0, 700, 700)].into(), - frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 69)]), 10, 0, 0)], + reg: RegInput{hp: 1000, sp: 716, ssp: 716, fp: 100, pc: 700, is: 700, bal: 20, cgas: 10, ggas: 30 }, + receipts: vec![Receipt::call(Default::default(), Default::default(), 20, Default::default(), 10, 0, 0, 700, 700)].into(), + frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 30)]), 10, 0, 0)], ..Default::default() })); "the receipt shows forwarded gas correctly when limited by available gas" )] @@ -251,12 +251,12 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 11, ggas: 11 }, + reg: RegInput{hp: 1000, sp: 100, ssp: 100, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Call{ block_height: Default::default() }, storage_balance: [(AssetId::default(), 30)].into_iter().collect(), ..Default::default() } => using check_output(Ok(Output{ - reg: RegInput{hp: 1000, sp: 712, ssp: 712, fp: 100, pc: 700, is: 700, bal: 20, cgas: 0, ggas: 0 }, + reg: RegInput{hp: 1000, sp: 716, ssp: 716, fp: 100, pc: 700, is: 700, bal: 20, cgas: 0, ggas: 0 }, receipts: vec![Receipt::call(Default::default(), Default::default(), 20, Default::default(), 0, 0, 0, 700, 700)].into(), frames: vec![CallFrame::new(Default::default(), Default::default(), make_reg(&[(HP, 1000), (SP, 100), (SSP, 100), (CGAS, 0), (GGAS, 0)]), 10, 0, 0)], ..Default::default() @@ -270,7 +270,7 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 11, ggas: 11 }, + reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Script{ block_height: Default::default() }, ..Default::default() } => using check_output(Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance))); "Tries to forward more coins than the contract has" @@ -283,7 +283,7 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 11, ggas: 11 }, + reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Script{ block_height: Default::default() }, ..Default::default() } => using check_output(Err(RuntimeError::Recoverable(PanicReason::MemoryOverflow))); "call_params_mem_address overflow" @@ -309,7 +309,7 @@ fn mem(set: &[(usize, Vec)]) -> Memory { asset_id_mem_address: 0, amount_of_gas_to_forward: 0, }, - reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 11, ggas: 11 }, + reg: RegInput{hp: 1000, sp: 0, ssp: 0, fp: 0, pc: 0, is: 0, bal: 0, cgas: 170, ggas: 170 }, context: Context::Call{ block_height: Default::default() }, balance: [(AssetId::default(), 30)].into_iter().collect(), ..Default::default() @@ -367,8 +367,7 @@ fn test_prepare_call(input: Input) -> Result { gas_cost, runtime_balances: &mut runtime_balances, storage: &mut storage, - input_contracts, - panic_context: &mut panic_context, + input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context), receipts: &mut receipts, script: script.as_mut(), consensus: &consensus, diff --git a/fuel-vm/src/interpreter/gas.rs b/fuel-vm/src/interpreter/gas.rs index 2945df4d06..ee0457851a 100644 --- a/fuel-vm/src/interpreter/gas.rs +++ b/fuel-vm/src/interpreter/gas.rs @@ -97,7 +97,7 @@ fn dependent_gas_charge_inner( ) -> Result { let cost = gas_cost .base - .saturating_add(arg.saturating_div(gas_cost.dep_per_unit)); + .saturating_add(arg.saturating_mul(gas_cost.dep_per_unit)); gas_charge_inner(cgas, ggas, cost).map(|_| cost) } diff --git a/fuel-vm/src/interpreter/gas/tests.rs b/fuel-vm/src/interpreter/gas/tests.rs index d0304e7160..61dec9820e 100644 --- a/fuel-vm/src/interpreter/gas/tests.rs +++ b/fuel-vm/src/interpreter/gas/tests.rs @@ -66,21 +66,21 @@ struct DepGasChargeInput { )] #[test_case( DepGasChargeInput{ - input: GasChargeInput{cgas: 1, ggas: 1, dependent_factor: 1}, + input: GasChargeInput{cgas: 3, ggas: 3, dependent_factor: 1}, gas_cost: DependentCost{base: 1, dep_per_unit: 2} } => Ok(GasChargeOutput{ cgas: 0, ggas: 0}); "just base with gas" )] #[test_case( DepGasChargeInput{ - input: GasChargeInput{cgas: 3, ggas: 3, dependent_factor: 8}, + input: GasChargeInput{cgas: 33, ggas: 33, dependent_factor: 8}, gas_cost: DependentCost{base: 1, dep_per_unit: 4} } => Ok(GasChargeOutput{ cgas: 0, ggas: 0}); "base with gas and a unit" )] #[test_case( DepGasChargeInput{ - input: GasChargeInput{cgas: 3, ggas: 3, dependent_factor: 5}, + input: GasChargeInput{cgas: 30, ggas: 30, dependent_factor: 5}, gas_cost: DependentCost{base: 0, dep_per_unit: 4} - } => Ok(GasChargeOutput{ cgas: 2, ggas: 2}); "base with gas and a unit and left over" + } => Ok(GasChargeOutput{ cgas: 10, ggas: 10}); "base with gas and a unit and left over" )] #[test_case( DepGasChargeInput{ diff --git a/fuel-vm/src/tests/metadata.rs b/fuel-vm/src/tests/metadata.rs index aadee07c53..5f9c82cc94 100644 --- a/fuel-vm/src/tests/metadata.rs +++ b/fuel-vm/src/tests/metadata.rs @@ -258,7 +258,7 @@ fn get_transaction_fields() { let mut client = MemoryClient::default(); let gas_price = 1; - let gas_limit = 1_000_000; + let gas_limit = 100_000_000; let maturity = 50.into(); let height = 122.into(); let input = 10_000_000;