From 2bf080675754450aa24febf1314bc53228bb2cbf Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Tue, 21 Mar 2023 15:02:26 +0200 Subject: [PATCH 1/5] Make SMO instruction take data ptr as an argument --- .../src/interpreter/blockchain/smo_tests.rs | 18 +++++++++--------- fuel-vm/src/tests/blockchain.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fuel-vm/src/interpreter/blockchain/smo_tests.rs b/fuel-vm/src/interpreter/blockchain/smo_tests.rs index 4b0f9064f4..df97116308 100644 --- a/fuel-vm/src/interpreter/blockchain/smo_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/smo_tests.rs @@ -41,7 +41,7 @@ impl Default for Input { #[test_case( Input { recipient_mem_address: 400, - msg_data_ptr: 0, + msg_data_ptr: 432, msg_data_len: 1, amount_coins_to_send: 0, ..Default::default() @@ -51,7 +51,7 @@ impl Default for Input { #[test_case( Input { recipient_mem_address: 0, - msg_data_ptr: 0, + msg_data_ptr: 32, msg_data_len: 0, amount_coins_to_send: 0, ..Default::default() @@ -60,8 +60,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: 0, - msg_data_ptr: Word::MAX, + recipient_mem_address: Word::MAX, + msg_data_ptr: 32, msg_data_len: 1, amount_coins_to_send: 0, max_message_data_length: Word::MAX, @@ -71,8 +71,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: 0, - msg_data_ptr: VM_MAX_RAM - 64, + recipient_mem_address: VM_MAX_RAM - 64, + msg_data_ptr: 32, msg_data_len: 100, amount_coins_to_send: 0, max_message_data_length: Word::MAX, @@ -82,8 +82,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: 0, - msg_data_ptr: 0, + recipient_mem_address: 400, + msg_data_ptr: 32, msg_data_len: 101, amount_coins_to_send: 0, max_message_data_length: 100, @@ -94,7 +94,7 @@ impl Default for Input { #[test_case( Input { recipient_mem_address: 400, - msg_data_ptr: 0, + msg_data_ptr: 432, msg_data_len: 10, amount_coins_to_send: 30, balance: [(AssetId::zeroed(), 29)].into_iter().collect(), diff --git a/fuel-vm/src/tests/blockchain.rs b/fuel-vm/src/tests/blockchain.rs index 0111d17f2a..8e5297f7a3 100644 --- a/fuel-vm/src/tests/blockchain.rs +++ b/fuel-vm/src/tests/blockchain.rs @@ -1331,7 +1331,7 @@ fn smo_instruction_works() { op::movi(0x10, msg_data[1].into()), // second message byte op::sb(RegId::HP, 0x10, 1), // store above to the message buffer op::movi(0x10, 0), // set the txid as recipient - op::movi(0x12, 2), // one byte of data + op::movi(0x12, 2), // two bytess of data op::movi(0x13, message_output_amount as Immediate24), // expected output amount op::smo(0x10,RegId::HP,0x12,0x13), op::ret(RegId::ONE) From 1379b4fc7e38694db4d3e596c277af531e2f0e9d Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Wed, 22 Mar 2023 13:49:41 +0200 Subject: [PATCH 2/5] Make SMO charge from contract balance in internal contexts --- fuel-vm/src/interpreter/blockchain.rs | 30 ++++- .../src/interpreter/blockchain/smo_tests.rs | 104 +++++++++++++++--- fuel-vm/src/tests/blockchain.rs | 2 +- 3 files changed, 114 insertions(+), 22 deletions(-) diff --git a/fuel-vm/src/interpreter/blockchain.rs b/fuel-vm/src/interpreter/blockchain.rs index 9030c8b69c..397fb03ada 100644 --- a/fuel-vm/src/interpreter/blockchain.rs +++ b/fuel-vm/src/interpreter/blockchain.rs @@ -1,4 +1,4 @@ -use super::contract::{balance, contract_size}; +use super::contract::{balance, balance_decrease, contract_size}; use super::gas::{dependent_gas_charge, ProfileGas}; use super::internal::{ append_receipt, base_asset_balance_sub, current_contract, inc_pc, internal_contract, internal_contract_bounds, @@ -242,6 +242,8 @@ where receipts: &mut self.receipts, tx: &mut self.tx, balances: &mut self.balances, + storage: &mut self.storage, + contract: self.frames.last().map(|frame| frame.to()).copied(), fp: fp.as_ref(), pc, recipient_mem_address: a, @@ -667,13 +669,20 @@ pub(crate) fn timestamp( inc_pc(pc) } -struct MessageOutputCtx<'vm, Tx> { +struct MessageOutputCtx<'vm, Tx, S> +where + S: ContractsAssetsStorage + ?Sized, + >::Error: Into, +{ max_message_data_length: u64, memory: &'vm mut [u8; MEM_SIZE], tx_offset: usize, receipts: &'vm mut ReceiptsCtx, tx: &'vm mut Tx, balances: &'vm mut RuntimeBalances, + storage: &'vm mut S, + /// The context of execution, None for external contexts + contract: Option, fp: Reg<'vm, FP>, pc: RegMut<'vm, PC>, /// A @@ -686,7 +695,11 @@ struct MessageOutputCtx<'vm, Tx> { amount_coins_to_send: Word, } -impl MessageOutputCtx<'_, Tx> { +impl MessageOutputCtx<'_, Tx, S> +where + S: ContractsAssetsStorage + ?Sized, + >::Error: Into, +{ pub(crate) fn message_output(self) -> Result<(), RuntimeError> where Tx: ExecutableTransaction, @@ -707,7 +720,16 @@ impl MessageOutputCtx<'_, Tx> { // validations passed, perform the mutations - base_asset_balance_sub(self.balances, self.memory, self.amount_coins_to_send)?; + if let Some(source_contract) = self.contract { + balance_decrease( + self.storage, + &source_contract, + &AssetId::BASE, + self.amount_coins_to_send, + )?; + } else { + base_asset_balance_sub(self.balances, self.memory, self.amount_coins_to_send)?; + } let sender = CheckedMemConstLen::<{ Address::LEN }>::new(*self.fp)?; let txid = tx_id(self.memory); diff --git a/fuel-vm/src/interpreter/blockchain/smo_tests.rs b/fuel-vm/src/interpreter/blockchain/smo_tests.rs index df97116308..3a6f839639 100644 --- a/fuel-vm/src/interpreter/blockchain/smo_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/smo_tests.rs @@ -1,4 +1,5 @@ -use crate::interpreter::memory::Memory; +use crate::interpreter::contract::balance as contract_balance; +use crate::{interpreter::memory::Memory, storage::MemoryStorage}; use super::*; @@ -14,14 +15,20 @@ struct Input { msg_data_len: Word, /// D amount_coins_to_send: Word, + internal: bool, max_message_data_length: Word, memory: Vec<(usize, Vec)>, - balance: Vec<(AssetId, Word)>, + /// Initial balance of the zeroed AssedId, same for both default contract and external context + initial_balance: Word, } #[derive(Debug, PartialEq, Eq)] struct Output { receipts: ReceiptsCtx, + /// Resulting internal (contract) balance of zeroed AssetId with zeroed ContractId + internal_balance: Word, + /// Resulting external balance of zeroed AssetId + external_balance: Word, } impl Default for Input { @@ -31,9 +38,10 @@ impl Default for Input { msg_data_ptr: Default::default(), msg_data_len: Default::default(), amount_coins_to_send: Default::default(), + internal: false, memory: vec![(400, Address::from([1u8; 32]).to_vec())], max_message_data_length: 100, - balance: Default::default(), + initial_balance: 0, } } } @@ -41,17 +49,28 @@ impl Default for Input { #[test_case( Input { recipient_mem_address: 400, - msg_data_ptr: 432, + msg_data_ptr: 0, + msg_data_len: 1, + amount_coins_to_send: 0, + ..Default::default() + } => matches Ok(Output { .. }) + ; "sanity test (external)" +)] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 0, msg_data_len: 1, amount_coins_to_send: 0, + internal: true, ..Default::default() } => matches Ok(Output { .. }) - ; "sanity test" + ; "sanity test (internal)" )] #[test_case( Input { recipient_mem_address: 0, - msg_data_ptr: 32, + msg_data_ptr: 0, msg_data_len: 0, amount_coins_to_send: 0, ..Default::default() @@ -60,8 +79,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: Word::MAX, - msg_data_ptr: 32, + recipient_mem_address: 0, + msg_data_ptr: Word::MAX, msg_data_len: 1, amount_coins_to_send: 0, max_message_data_length: Word::MAX, @@ -71,8 +90,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: VM_MAX_RAM - 64, - msg_data_ptr: 32, + recipient_mem_address: 0, + msg_data_ptr: VM_MAX_RAM - 64, msg_data_len: 100, amount_coins_to_send: 0, max_message_data_length: Word::MAX, @@ -94,33 +113,75 @@ impl Default for Input { #[test_case( Input { recipient_mem_address: 400, - msg_data_ptr: 432, + msg_data_ptr: 0, + msg_data_len: 10, + amount_coins_to_send: 30, + initial_balance: 29, + ..Default::default() + } => Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance)) + ; "amount coins to send > balance from external context" +)] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 0, msg_data_len: 10, amount_coins_to_send: 30, - balance: [(AssetId::zeroed(), 29)].into_iter().collect(), + initial_balance: 29, + internal: true, ..Default::default() } => Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance)) - ; "amount coins to send > balance" + ; "amount coins to send > balance from internal context" +)] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 432, + msg_data_len: 10, + amount_coins_to_send: 20, + initial_balance: 29, + ..Default::default() + } => matches Ok(Output { external_balance: 9, internal_balance: 29, .. }) + ; "coins sent succesfully from external context" +)] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 432, + msg_data_len: 10, + amount_coins_to_send: 20, + initial_balance: 29, + internal: true, + ..Default::default() + } => matches Ok(Output { external_balance: 29, internal_balance: 9, .. }) + ; "coins sent succesfully from internal context" )] -// TODO: Test the above on an internal context fn test_smo( Input { recipient_mem_address, msg_data_len, msg_data_ptr, amount_coins_to_send, + internal, memory: mem, max_message_data_length, - balance, + initial_balance, }: Input, ) -> Result { + let asset = AssetId::zeroed(); + let mut memory: Memory = vec![0; MEM_SIZE].try_into().unwrap(); for (offset, bytes) in mem { memory[offset..offset + bytes.len()].copy_from_slice(bytes.as_slice()); } let mut receipts = Default::default(); let mut tx = Create::default(); - let mut balances = RuntimeBalances::try_from_iter(balance).expect("Should be valid balance"); + let mut storage = MemoryStorage::new(0, Address::default()); + storage + .merkle_contract_asset_id_balance_insert(&ContractId::default(), &asset, initial_balance) + .unwrap(); + let mut balances = + RuntimeBalances::try_from_iter([(asset, initial_balance)].into_iter()).expect("Should be valid balance"); let fp = 0; let mut pc = 0; let input = MessageOutputCtx { @@ -130,6 +191,8 @@ fn test_smo( receipts: &mut receipts, tx: &mut tx, balances: &mut balances, + storage: &mut storage, + contract: if internal { Some(ContractId::default()) } else { None }, fp: Reg::new(&fp), pc: RegMut::new(&mut pc), recipient_mem_address, @@ -137,5 +200,12 @@ fn test_smo( msg_data_ptr, amount_coins_to_send, }; - input.message_output().map(|_| Output { receipts }) + + input.message_output()?; + + Ok(Output { + receipts, + internal_balance: contract_balance(&storage, &ContractId::default(), &asset).unwrap(), + external_balance: balances.balance(&asset).unwrap(), + }) } diff --git a/fuel-vm/src/tests/blockchain.rs b/fuel-vm/src/tests/blockchain.rs index 8e5297f7a3..0111d17f2a 100644 --- a/fuel-vm/src/tests/blockchain.rs +++ b/fuel-vm/src/tests/blockchain.rs @@ -1331,7 +1331,7 @@ fn smo_instruction_works() { op::movi(0x10, msg_data[1].into()), // second message byte op::sb(RegId::HP, 0x10, 1), // store above to the message buffer op::movi(0x10, 0), // set the txid as recipient - op::movi(0x12, 2), // two bytess of data + op::movi(0x12, 2), // one byte of data op::movi(0x13, message_output_amount as Immediate24), // expected output amount op::smo(0x10,RegId::HP,0x12,0x13), op::ret(RegId::ONE) From 9258ead7ecc79a97e0f726ed24536f9d59cd51dd Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Wed, 22 Mar 2023 13:58:15 +0200 Subject: [PATCH 3/5] Rename contract to current_contract --- fuel-vm/src/interpreter/blockchain.rs | 7 +++---- fuel-vm/src/interpreter/blockchain/smo_tests.rs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fuel-vm/src/interpreter/blockchain.rs b/fuel-vm/src/interpreter/blockchain.rs index 397fb03ada..f3caf79278 100644 --- a/fuel-vm/src/interpreter/blockchain.rs +++ b/fuel-vm/src/interpreter/blockchain.rs @@ -243,7 +243,7 @@ where tx: &mut self.tx, balances: &mut self.balances, storage: &mut self.storage, - contract: self.frames.last().map(|frame| frame.to()).copied(), + current_contract: self.frames.last().map(|frame| frame.to()).copied(), fp: fp.as_ref(), pc, recipient_mem_address: a, @@ -681,8 +681,7 @@ where tx: &'vm mut Tx, balances: &'vm mut RuntimeBalances, storage: &'vm mut S, - /// The context of execution, None for external contexts - contract: Option, + current_contract: Option, fp: Reg<'vm, FP>, pc: RegMut<'vm, PC>, /// A @@ -720,7 +719,7 @@ where // validations passed, perform the mutations - if let Some(source_contract) = self.contract { + if let Some(source_contract) = self.current_contract { balance_decrease( self.storage, &source_contract, diff --git a/fuel-vm/src/interpreter/blockchain/smo_tests.rs b/fuel-vm/src/interpreter/blockchain/smo_tests.rs index 3a6f839639..1447ab4323 100644 --- a/fuel-vm/src/interpreter/blockchain/smo_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/smo_tests.rs @@ -192,7 +192,7 @@ fn test_smo( tx: &mut tx, balances: &mut balances, storage: &mut storage, - contract: if internal { Some(ContractId::default()) } else { None }, + current_contract: if internal { Some(ContractId::default()) } else { None }, fp: Reg::new(&fp), pc: RegMut::new(&mut pc), recipient_mem_address, From 5327af9cb20c7cd8d5e24bf9c54c7ca0e82eb090 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Wed, 22 Mar 2023 14:07:48 +0200 Subject: [PATCH 4/5] Revert useless changes to some existing tests --- fuel-vm/src/interpreter/blockchain/smo_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuel-vm/src/interpreter/blockchain/smo_tests.rs b/fuel-vm/src/interpreter/blockchain/smo_tests.rs index 1447ab4323..dc01e05f81 100644 --- a/fuel-vm/src/interpreter/blockchain/smo_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/smo_tests.rs @@ -101,8 +101,8 @@ impl Default for Input { )] #[test_case( Input { - recipient_mem_address: 400, - msg_data_ptr: 32, + recipient_mem_address: 0, + msg_data_ptr: 0, msg_data_len: 101, amount_coins_to_send: 0, max_message_data_length: 100, From 17dd12b6e89bb14bd7b559b03e04699716e8b5b3 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Thu, 23 Mar 2023 02:17:58 +0200 Subject: [PATCH 5/5] Add test cases for spending full balance --- .../src/interpreter/blockchain/smo_tests.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fuel-vm/src/interpreter/blockchain/smo_tests.rs b/fuel-vm/src/interpreter/blockchain/smo_tests.rs index dc01e05f81..fa7f71d685 100644 --- a/fuel-vm/src/interpreter/blockchain/smo_tests.rs +++ b/fuel-vm/src/interpreter/blockchain/smo_tests.rs @@ -156,6 +156,29 @@ impl Default for Input { } => matches Ok(Output { external_balance: 29, internal_balance: 9, .. }) ; "coins sent succesfully from internal context" )] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 432, + msg_data_len: 10, + amount_coins_to_send: 20, + initial_balance: 20, + ..Default::default() + } => matches Ok(Output { external_balance: 0, internal_balance: 20, .. }) + ; "spend all coins succesfully from external context" +)] +#[test_case( + Input { + recipient_mem_address: 400, + msg_data_ptr: 432, + msg_data_len: 10, + amount_coins_to_send: 20, + initial_balance: 20, + internal: true, + ..Default::default() + } => matches Ok(Output { external_balance: 20, internal_balance: 0, .. }) + ; "spend all coins succesfully from internal context" +)] fn test_smo( Input { recipient_mem_address,