Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SMO charge from contract balance in internal context #407

Merged
merged 6 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions fuel-vm/src/interpreter/blockchain.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -242,6 +242,8 @@ where
receipts: &mut self.receipts,
tx: &mut self.tx,
balances: &mut self.balances,
storage: &mut self.storage,
current_contract: self.frames.last().map(|frame| frame.to()).copied(),
fp: fp.as_ref(),
pc,
recipient_mem_address: a,
Expand Down Expand Up @@ -667,13 +669,19 @@ pub(crate) fn timestamp(

inc_pc(pc)
}
struct MessageOutputCtx<'vm, Tx> {
struct MessageOutputCtx<'vm, Tx, S>
where
S: ContractsAssetsStorage + ?Sized,
<S as StorageInspect<ContractsAssets>>::Error: Into<std::io::Error>,
{
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,
current_contract: Option<ContractId>,
fp: Reg<'vm, FP>,
pc: RegMut<'vm, PC>,
/// A
Expand All @@ -686,7 +694,11 @@ struct MessageOutputCtx<'vm, Tx> {
amount_coins_to_send: Word,
}

impl<Tx> MessageOutputCtx<'_, Tx> {
impl<Tx, S> MessageOutputCtx<'_, Tx, S>
where
S: ContractsAssetsStorage + ?Sized,
<S as StorageInspect<ContractsAssets>>::Error: Into<std::io::Error>,
{
pub(crate) fn message_output(self) -> Result<(), RuntimeError>
where
Tx: ExecutableTransaction,
Expand All @@ -707,7 +719,16 @@ impl<Tx> 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.current_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);
Expand Down
90 changes: 80 additions & 10 deletions fuel-vm/src/interpreter/blockchain/smo_tests.rs
Original file line number Diff line number Diff line change
@@ -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::*;

Expand All @@ -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<u8>)>,
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 {
Expand All @@ -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,
}
}
}
Expand All @@ -46,7 +54,18 @@ impl Default for Input {
amount_coins_to_send: 0,
..Default::default()
} => matches Ok(Output { .. })
; "sanity test"
; "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 (internal)"
)]
#[test_case(
Input {
Expand Down Expand Up @@ -97,30 +116,72 @@ impl Default for Input {
msg_data_ptr: 0,
msg_data_len: 10,
amount_coins_to_send: 30,
balance: [(AssetId::zeroed(), 29)].into_iter().collect(),
initial_balance: 29,
..Default::default()
} => Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance))
; "amount coins to send > balance"
; "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,
initial_balance: 29,
internal: true,
..Default::default()
} => Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance))
; "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,
Dentosal marked this conversation as resolved.
Show resolved Hide resolved
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<Output, RuntimeError> {
let asset = AssetId::zeroed();

let mut memory: Memory<MEM_SIZE> = 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 {
Expand All @@ -130,12 +191,21 @@ fn test_smo(
receipts: &mut receipts,
tx: &mut tx,
balances: &mut balances,
storage: &mut storage,
current_contract: if internal { Some(ContractId::default()) } else { None },
fp: Reg::new(&fp),
pc: RegMut::new(&mut pc),
recipient_mem_address,
msg_data_len,
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(),
})
}