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

Include size lookup of contract for computing LDC opcode cost #598

Merged
merged 35 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
80c0e23
Include size lookup of contract for computing cost
MitchTurner Oct 3, 2023
ce4d94d
Merge remote-tracking branch 'origin/master' into fix-ldc-cost
MitchTurner Oct 3, 2023
8023807
Remove comment, update CHANGELOG
MitchTurner Oct 3, 2023
2e24f6a
Fix merge mistakes
MitchTurner Oct 3, 2023
1f6e728
Fix ssp_not_sp test
MitchTurner Oct 5, 2023
cec3c68
Fix test, cleanup, remove unused code
MitchTurner Oct 5, 2023
5796561
Appease Clippy-sama
MitchTurner Oct 5, 2023
716e82c
Simplify and reorder code to fix test
MitchTurner Oct 5, 2023
6a07661
Remove comment and unnecissary conversion
MitchTurner Oct 5, 2023
429dbe1
Only take the bytes of the contract that ldc specifies with `$rC`
MitchTurner Oct 5, 2023
eaa027b
Merge branch 'master' into fix-ldc-cost
MitchTurner Oct 5, 2023
8053292
Add test for copy cost
MitchTurner Oct 6, 2023
da033b0
Appease Clippy-sama, fix nostd code
MitchTurner Oct 6, 2023
d9de157
Call out adherence to spec with comment
MitchTurner Oct 6, 2023
68afeae
Fix name, move comment
MitchTurner Oct 6, 2023
29c5cd8
Cleanup
MitchTurner Oct 6, 2023
fc1ee18
Refactor tests
MitchTurner Oct 6, 2023
d556e7a
Cleanup
MitchTurner Oct 6, 2023
a0008a6
Fix some of the opcodes in tests
MitchTurner Oct 7, 2023
b71151d
fmt
MitchTurner Oct 7, 2023
7257740
Revert behavior to cost for entire contrat, fix test, add tests, fix …
MitchTurner Oct 8, 2023
3016217
Fix sub_bytes code
MitchTurner Oct 8, 2023
2842fe8
Remove use of std vec
MitchTurner Oct 8, 2023
fc837b0
Add offset test
MitchTurner Oct 8, 2023
6bba267
Fix names and reg values, fix bug in interpreter code
MitchTurner Oct 9, 2023
5ade899
Add cost right after gettting contract bytes
MitchTurner Oct 9, 2023
51cf8fe
Merge remote-tracking branch 'origin/master' into fix-ldc-cost
MitchTurner Oct 9, 2023
f605bb2
Appease Clippy-sama
MitchTurner Oct 9, 2023
0b360d6
Move charging for the size of the contract at the middle of the load …
xgreenx Oct 10, 2023
be21ac2
Fix padding in tests
MitchTurner Oct 10, 2023
109af4b
Fix param names
MitchTurner Oct 10, 2023
ab3d0f2
Simplify contract code
MitchTurner Oct 10, 2023
36c0639
Merge branch 'master' into fix-ldc-cost
MitchTurner Oct 10, 2023
000237a
Fix comments
MitchTurner Oct 11, 2023
dd971a3
Merge branch 'fix-ldc-cost' of github.com:FuelLabs/fuel-vm into fix-l…
MitchTurner Oct 11, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

#### Breaking

- [#598](https://github.com/FuelLabs/fuel-vm/pull/598): Update cost model for `ldc` opcode to take into account contract size.
- [#604](https://github.com/FuelLabs/fuel-vm/pull/604): Removed `ChainId` from `PredicateId` calculation. It changes the generated address of the predicates and may break tests or logic that uses hard-coded predicate IDs.
- [#594](https://github.com/FuelLabs/fuel-vm/pull/594): Add new predicate input validation tests. Also improves error propagation so that predicate error message better reflects the reason for invalidity.
- [#596](https://github.com/FuelLabs/fuel-vm/pull/596): Remove `core::ops::{Add, Sub}` impls from `BlockHeight`. Use `succ` and `pred` to access adjacent blocks, or perform arithmetic directly on the wrapped integer instead.
Expand Down
69 changes: 56 additions & 13 deletions fuel-vm/src/interpreter/blockchain.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use alloc::vec::Vec;

use super::{
contract::{
balance,
Expand Down Expand Up @@ -57,6 +55,7 @@ use crate::{
InterpreterStorage,
},
};
use alloc::vec::Vec;
use fuel_asm::PanicReason;
use fuel_storage::StorageSize;
use fuel_tx::{
Expand Down Expand Up @@ -89,46 +88,63 @@ where
Tx: ExecutableTransaction,
S: InterpreterStorage,
{
/// Loads contract ID pointed by `a`, and then for that contract,
/// copies `c` bytes from it starting from offset `b` into the stack.
/// ```txt
/// Loads contract ID pointed by `contract_id_addr`, and then for that contract,
/// copies `length_unpadded` bytes from it starting from offset `contract_offset` into
/// the stack. ```txt
/// contract_id = mem[$rA, 32]
/// contract_code = contracts[contract_id]
/// mem[$ssp, $rC] = contract_code[$rB, $rC]
/// ```
pub(crate) fn load_contract_code(
&mut self,
a: Word,
b: Word,
c: Word,
contract_id_addr: Word,
contract_offset: Word,
length_unpadded: Word,
) -> IoResult<(), S::DataError> {
let mut gas_cost = self.gas_costs().ldc;
// Charge only for the `base` execution.
// We will charge for the contracts size in the `load_contract_code`.
self.gas_charge(gas_cost.base)?;
gas_cost.base = 0;
let contract_max_size = self.contract_max_size();
let current_contract =
current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?
.copied();
let (
SystemRegisters {
cgas,
ggas,
ssp,
sp,
hp,
fp,
pc,
is,
..
},
_,
) = split_registers(&mut self.registers);
let input = LoadContractCodeCtx {
memory: &mut self.memory,
profiler: &mut self.profiler,
storage: &mut self.storage,
contract_max_size,
input_contracts: InputContracts::new(
self.tx.input_contracts(),
&mut self.panic_context,
),
gas_cost,
current_contract,
cgas,
ggas,
ssp,
sp,
fp: fp.as_ref(),
hp: hp.as_ref(),
pc,
is: is.as_ref(),
};
input.load_contract_code(a, b, c)
input.load_contract_code(contract_id_addr, contract_offset, length_unpadded)
}

pub(crate) fn burn(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> {
Expand Down Expand Up @@ -241,7 +257,11 @@ where
ra: RegisterId,
b: Word,
) -> IoResult<(), S::DataError> {
let gas_cost = self.gas_costs().csiz;
let mut gas_cost = self.gas_costs().csiz;
// Charge only for the `base` execution.
// We will charge for the contracts size in the `code_size`.
self.gas_charge(gas_cost.base)?;
gas_cost.base = 0;
let current_contract =
current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?
.copied();
Expand Down Expand Up @@ -441,13 +461,19 @@ where
struct LoadContractCodeCtx<'vm, S, I> {
contract_max_size: u64,
memory: &'vm mut [u8; MEM_SIZE],
profiler: &'vm mut Profiler,
input_contracts: InputContracts<'vm, I>,
storage: &'vm S,
current_contract: Option<ContractId>,
gas_cost: DependentCost,
cgas: RegMut<'vm, CGAS>,
ggas: RegMut<'vm, GGAS>,
ssp: RegMut<'vm, SSP>,
sp: RegMut<'vm, SP>,
fp: Reg<'vm, FP>,
hp: Reg<'vm, HP>,
pc: RegMut<'vm, PC>,
is: Reg<'vm, IS>,
}

impl<'vm, S, I> LoadContractCodeCtx<'vm, S, I>
Expand All @@ -461,6 +487,7 @@ where
/// contract_code = contracts[contract_id]
/// mem[$ssp, $rC] = contract_code[$rB, $rC]
/// ```
/// Returns the total length of the contract code that was loaded from storage.
pub(crate) fn load_contract_code(
mut self,
contract_id_addr: Word,
Expand Down Expand Up @@ -500,7 +527,21 @@ where

// Fetch the storage contract
let contract = super::contract::contract(self.storage, &contract_id)?;
let contract = contract.as_ref().as_ref();
let contract_bytes = contract.as_ref().as_ref();
let contract_len = contract_bytes.len();
let profiler = ProfileGas {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to charge the user here before we do the main work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it. This would be quite the architectural change, I think, but if it's how it needs to work, then I'll make it work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

K. I split the function in half to return the bytes and then copy them separately. I don't love the solution becuase the functions are doing too much, but it's probably better :)

pc: self.pc.as_ref(),
is: self.is,
current_contract: self.current_contract,
profiler: self.profiler,
};
dependent_gas_charge(
self.cgas,
self.ggas,
profiler,
self.gas_cost,
contract_len as u64,
)?;

// Mark stack space as allocated
let new_stack = dst_range.words().end;
Expand All @@ -510,7 +551,7 @@ where
// Copy the code. Ownership checks are not used as the stack is adjusted above.
copy_from_slice_zero_fill_noownerchecks(
self.memory,
contract,
contract_bytes,
dst_range.start,
contract_offset,
length,
Expand Down Expand Up @@ -539,7 +580,9 @@ where
.copy_from_slice(&new_code_size.to_be_bytes());
}

Ok(inc_pc(self.pc)?)
inc_pc(self.pc)?;

Ok(())
}
}

Expand Down
26 changes: 25 additions & 1 deletion fuel-vm/src/interpreter/blockchain/code_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,28 @@ fn test_load_contract() -> IoResult<(), Infallible> {
let mut memory: Memory<MEM_SIZE> = vec![1u8; MEM_SIZE].try_into().unwrap();
let mut pc = 4;
let hp = 2000;
let mut cgas = 1000;
let mut ggas = 1000;
let mut ssp = 1000;
let mut sp = 1000;
let fp = 0;
let is = 0;

let contract_id = ContractId::from([4u8; 32]);

let contract_id_mem_address: Word = 32;
let offset = 20;
let num_bytes = 40;
const CONTRACT_SIZE: u64 = 400;

memory[contract_id_mem_address as usize
..contract_id_mem_address as usize + ContractId::LEN]
.copy_from_slice(contract_id.as_ref());
storage
.storage_contract_insert(&contract_id, &Contract::from(vec![5u8; 400]))
.storage_contract_insert(
&contract_id,
&Contract::from(vec![5u8; CONTRACT_SIZE as usize]),
)
.unwrap();

let mut panic_context = PanicContext::None;
Expand All @@ -41,15 +48,32 @@ fn test_load_contract() -> IoResult<(), Infallible> {
contract_max_size: 100,
storage: &storage,
memory: &mut memory,
profiler: &mut Profiler::default(),
input_contracts: InputContracts::new(input_contracts.iter(), &mut panic_context),
current_contract: None,
gas_cost: DependentCost {
base: 13,
dep_per_unit: 1,
},
cgas: RegMut::new(&mut cgas),
ggas: RegMut::new(&mut ggas),
ssp: RegMut::new(&mut ssp),
sp: RegMut::new(&mut sp),
fp: Reg::new(&fp),
hp: Reg::new(&hp),
pc: RegMut::new(&mut pc),
is: Reg::new(&is),
};
input.load_contract_code(contract_id_mem_address, offset, num_bytes)?;
assert_eq!(pc, 8);
assert_eq!(
cgas,
1000 - CONTRACT_SIZE /* price per byte */ - 13 // base price
);
assert_eq!(
ggas,
1000 - CONTRACT_SIZE /* price per byte */ - 13 // base price
);

Ok(())
}
Expand Down
4 changes: 3 additions & 1 deletion fuel-vm/src/interpreter/executors/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ where
}

Instruction::CALL(call) => {
// We charge for the gas inside of the `prepare_call` function.
let (a, b, c, d) = call.unpack();

// Enter call context
Expand All @@ -753,13 +754,14 @@ where
}

Instruction::CSIZ(csiz) => {
// We charge for the gas inside of the `code_size` function.
let (a, b) = csiz.unpack();
self.code_size(a.into(), r!(b))?;
}

Instruction::LDC(ldc) => {
// We charge for the gas inside of the `load_contract_code` function.
let (a, b, c) = ldc.unpack();
self.dependent_gas_charge(self.gas_costs().ldc, r!(c))?;
self.load_contract_code(r!(a), r!(b), r!(c))?;
}

Expand Down
6 changes: 5 additions & 1 deletion fuel-vm/src/interpreter/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,11 @@ where
asset_id_mem_address,
amount_of_gas_to_forward,
};
let gas_cost = self.gas_costs().call;
let mut gas_cost = self.gas_costs().call;
// Charge only for the `base` execution.
// We will charge for the frame size in the `prepare_call`.
self.gas_charge(gas_cost.base)?;
gas_cost.base = 0;
let current_contract =
current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?
.copied();
Expand Down
Loading
Loading