-
Notifications
You must be signed in to change notification settings - Fork 87
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
Charge for new storage bytes #634
Changes from 15 commits
2d944a3
e76b1fa
ab1033e
fc05023
28603d9
f54a751
6dbdaa8
a510192
bc13cec
906fcfd
0c0bd01
3088340
14f71c1
52c8b29
4743ad4
7863362
c593198
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -321,6 +321,7 @@ pub struct GasCostsValues { | |
// Non-opcode costs | ||
pub contract_root: DependentCost, | ||
pub state_root: DependentCost, | ||
pub new_storage_per_byte: Word, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is better to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it makes any sense to future-proof this for the dynamic storage value support. The whole API is going to change anyway. |
||
pub vm_initialization: Word, | ||
} | ||
|
||
|
@@ -477,6 +478,7 @@ impl GasCostsValues { | |
// Non-opcode costs | ||
contract_root: DependentCost::free(), | ||
state_root: DependentCost::free(), | ||
new_storage_per_byte: 0, | ||
vm_initialization: 0, | ||
} | ||
} | ||
|
@@ -594,6 +596,7 @@ impl GasCostsValues { | |
// Non-opcode costs | ||
contract_root: DependentCost::unit(), | ||
state_root: DependentCost::unit(), | ||
new_storage_per_byte: 1, | ||
vm_initialization: 1, | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ use super::{ | |
}, | ||
gas::{ | ||
dependent_gas_charge, | ||
gas_charge, | ||
ProfileGas, | ||
}, | ||
internal::{ | ||
|
@@ -171,8 +172,18 @@ where | |
|
||
pub(crate) fn mint(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> { | ||
let tx_offset = self.tx_offset(); | ||
let (SystemRegisters { fp, pc, is, .. }, _) = | ||
split_registers(&mut self.registers); | ||
let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte; | ||
let ( | ||
SystemRegisters { | ||
cgas, | ||
ggas, | ||
fp, | ||
pc, | ||
is, | ||
.. | ||
}, | ||
_, | ||
) = split_registers(&mut self.registers); | ||
MintCtx { | ||
storage: &mut self.storage, | ||
context: &self.context, | ||
|
@@ -182,6 +193,10 @@ where | |
tx_offset, | ||
memory: &mut self.memory, | ||
}, | ||
profiler: &mut self.profiler, | ||
new_storage_gas_per_byte, | ||
cgas, | ||
ggas, | ||
fp: fp.as_ref(), | ||
pc, | ||
is: is.as_ref(), | ||
|
@@ -318,8 +333,18 @@ where | |
rb: RegisterId, | ||
c: Word, | ||
) -> IoResult<(), S::DataError> { | ||
let (SystemRegisters { fp, pc, .. }, mut w) = | ||
split_registers(&mut self.registers); | ||
let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte; | ||
Dentosal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let ( | ||
SystemRegisters { | ||
cgas, | ||
ggas, | ||
is, | ||
fp, | ||
pc, | ||
.. | ||
}, | ||
mut w, | ||
) = split_registers(&mut self.registers); | ||
let (result, got_result) = w | ||
.get_mut_two(WriteRegKey::try_from(ra)?, WriteRegKey::try_from(rb)?) | ||
.ok_or(RuntimeError::Recoverable( | ||
|
@@ -336,6 +361,12 @@ where | |
storage, | ||
memory, | ||
context, | ||
profiler: &mut self.profiler, | ||
new_storage_gas_per_byte, | ||
current_contract: self.frames.last().map(|frame| frame.to()).copied(), | ||
cgas, | ||
ggas, | ||
is: is.as_ref(), | ||
fp: fp.as_ref(), | ||
pc, | ||
}, | ||
|
@@ -373,8 +404,18 @@ where | |
rb: RegisterId, | ||
c: Word, | ||
) -> IoResult<(), S::DataError> { | ||
let (SystemRegisters { fp, pc, .. }, mut w) = | ||
split_registers(&mut self.registers); | ||
let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte; | ||
let ( | ||
SystemRegisters { | ||
cgas, | ||
ggas, | ||
is, | ||
fp, | ||
pc, | ||
.. | ||
}, | ||
mut w, | ||
) = split_registers(&mut self.registers); | ||
let exists = &mut w[WriteRegKey::try_from(rb)?]; | ||
let Self { | ||
ref mut storage, | ||
|
@@ -387,6 +428,12 @@ where | |
storage, | ||
memory, | ||
context, | ||
profiler: &mut self.profiler, | ||
new_storage_gas_per_byte, | ||
current_contract: self.frames.last().map(|frame| frame.to()).copied(), | ||
cgas, | ||
ggas, | ||
is: is.as_ref(), | ||
fp: fp.as_ref(), | ||
pc, | ||
}, | ||
|
@@ -403,8 +450,14 @@ where | |
c: Word, | ||
d: Word, | ||
) -> IoResult<(), S::DataError> { | ||
let new_storage_per_byte = self.gas_costs().new_storage_per_byte; | ||
let contract_id = self.internal_contract().copied(); | ||
let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers); | ||
let ( | ||
SystemRegisters { | ||
is, cgas, ggas, pc, .. | ||
}, | ||
mut w, | ||
) = split_registers(&mut self.registers); | ||
let result = &mut w[WriteRegKey::try_from(rb)?]; | ||
|
||
let input = StateWriteQWord::new(a, c, d)?; | ||
|
@@ -414,7 +467,20 @@ where | |
.. | ||
} = self; | ||
|
||
state_write_qword(&contract_id?, storage, memory.as_mut(), pc, result, input) | ||
state_write_qword( | ||
&contract_id?, | ||
storage, | ||
memory.as_mut(), | ||
&mut self.profiler, | ||
new_storage_per_byte, | ||
self.frames.last().map(|frame| frame.to()).copied(), | ||
cgas, | ||
ggas, | ||
is.as_ref(), | ||
pc, | ||
result, | ||
input, | ||
) | ||
} | ||
|
||
pub(crate) fn timestamp( | ||
|
@@ -616,7 +682,8 @@ where | |
.checked_sub(a) | ||
.ok_or(PanicReason::NotEnoughBalance)?; | ||
|
||
self.storage | ||
let _ = self | ||
.storage | ||
.merkle_contract_asset_id_balance_insert(contract_id, &asset_id, balance) | ||
.map_err(RuntimeError::Storage)?; | ||
|
||
|
@@ -631,7 +698,11 @@ where | |
struct MintCtx<'vm, S> { | ||
storage: &'vm mut S, | ||
context: &'vm Context, | ||
profiler: &'vm mut Profiler, | ||
new_storage_gas_per_byte: Word, | ||
append: AppendReceipt<'vm>, | ||
cgas: RegMut<'vm, CGAS>, | ||
ggas: RegMut<'vm, GGAS>, | ||
fp: Reg<'vm, FP>, | ||
pc: RegMut<'vm, PC>, | ||
is: Reg<'vm, IS>, | ||
|
@@ -654,10 +725,27 @@ where | |
let balance = balance(self.storage, contract_id, &asset_id)?; | ||
let balance = balance.checked_add(a).ok_or(PanicReason::BalanceOverflow)?; | ||
|
||
self.storage | ||
let old_value = self | ||
.storage | ||
.merkle_contract_asset_id_balance_insert(contract_id, &asset_id, balance) | ||
.map_err(RuntimeError::Storage)?; | ||
|
||
if old_value.is_none() { | ||
// New data was written, charge gas for it | ||
let profiler = ProfileGas { | ||
pc: self.pc.as_ref(), | ||
is: self.is, | ||
current_contract: Some(*contract_id), | ||
profiler: self.profiler, | ||
}; | ||
gas_charge( | ||
self.cgas, | ||
self.ggas, | ||
profiler, | ||
((AssetId::LEN + WORD_SIZE) as u64) * self.new_storage_gas_per_byte, | ||
)?; | ||
} | ||
|
||
let receipt = Receipt::mint(*sub_id, *contract_id, a, *self.pc, *self.is); | ||
|
||
append_receipt(self.append, receipt)?; | ||
|
@@ -846,6 +934,12 @@ pub(crate) struct StateWordCtx<'vm, S> { | |
pub storage: &'vm mut S, | ||
pub memory: &'vm [u8; MEM_SIZE], | ||
pub context: &'vm Context, | ||
pub profiler: &'vm mut Profiler, | ||
pub new_storage_gas_per_byte: Word, | ||
pub current_contract: Option<ContractId>, | ||
pub cgas: RegMut<'vm, CGAS>, | ||
pub ggas: RegMut<'vm, GGAS>, | ||
pub is: Reg<'vm, IS>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are not needed for state read opcodes, so maybe it makes sense to split this struct into two? On the other hand, we're handing over over half of the VM anyway, so just passing the VM directly might make sense. |
||
pub fp: Reg<'vm, FP>, | ||
pub pc: RegMut<'vm, PC>, | ||
} | ||
|
@@ -857,6 +951,7 @@ pub(crate) fn state_read_word<S: InterpreterStorage>( | |
context, | ||
fp, | ||
pc, | ||
.. | ||
}: StateWordCtx<S>, | ||
result: &mut Word, | ||
got_result: &mut Word, | ||
|
@@ -890,11 +985,17 @@ pub(crate) fn state_write_word<S: InterpreterStorage>( | |
storage, | ||
memory, | ||
context, | ||
profiler, | ||
new_storage_gas_per_byte, | ||
current_contract, | ||
cgas, | ||
ggas, | ||
is, | ||
fp, | ||
pc, | ||
}: StateWordCtx<S>, | ||
a: Word, | ||
exists: &mut Word, | ||
created_new: &mut Word, | ||
c: Word, | ||
) -> IoResult<(), S::DataError> { | ||
let key = CheckedMemConstLen::<{ Bytes32::LEN }>::new(a)?; | ||
|
@@ -913,7 +1014,23 @@ pub(crate) fn state_write_word<S: InterpreterStorage>( | |
.merkle_contract_state_insert(contract, key, &value) | ||
.map_err(RuntimeError::Storage)?; | ||
|
||
*exists = result.is_some() as Word; | ||
*created_new = result.is_none() as Word; | ||
|
||
if result.is_none() { | ||
// New data was written, charge gas for it | ||
let profiler = ProfileGas { | ||
pc: pc.as_ref(), | ||
is, | ||
current_contract, | ||
profiler, | ||
}; | ||
gas_charge( | ||
cgas, | ||
ggas, | ||
profiler, | ||
(2 * Bytes32::LEN as u64) * new_storage_gas_per_byte, | ||
)?; | ||
} | ||
|
||
Ok(inc_pc(pc)?) | ||
} | ||
|
@@ -1123,10 +1240,17 @@ impl StateWriteQWord { | |
} | ||
} | ||
|
||
fn state_write_qword<S: InterpreterStorage>( | ||
#[allow(clippy::too_many_arguments)] | ||
fn state_write_qword<'vm, S: InterpreterStorage>( | ||
contract_id: &ContractId, | ||
storage: &mut S, | ||
memory: &[u8; MEM_SIZE], | ||
profiler: &'vm mut Profiler, | ||
new_storage_gas_per_byte: Word, | ||
current_contract: Option<ContractId>, | ||
cgas: RegMut<'vm, CGAS>, | ||
ggas: RegMut<'vm, GGAS>, | ||
is: Reg<'vm, IS>, | ||
pc: RegMut<PC>, | ||
result_register: &mut Word, | ||
input: StateWriteQWord, | ||
|
@@ -1139,11 +1263,27 @@ fn state_write_qword<S: InterpreterStorage>( | |
.flat_map(|chunk| Some(Bytes32::from(<[u8; 32]>::try_from(chunk).ok()?))) | ||
.collect(); | ||
|
||
let any_none = storage | ||
let unset_count = storage | ||
.merkle_contract_state_insert_range(contract_id, destination_key, &values) | ||
.map_err(RuntimeError::Storage)? | ||
.is_some(); | ||
*result_register = any_none as Word; | ||
.map_err(RuntimeError::Storage)?; | ||
*result_register = unset_count as Word; | ||
|
||
if unset_count > 0 { | ||
// New data was written, charge gas for it | ||
let profiler = ProfileGas { | ||
pc: pc.as_ref(), | ||
is, | ||
current_contract, | ||
profiler, | ||
}; | ||
gas_charge( | ||
cgas, | ||
ggas, | ||
profiler, | ||
// Overflow safety: unset_count * 32 can be at most VM_MAX_RAM | ||
(unset_count as u64) * (2 * Bytes32::LEN as u64) * new_storage_gas_per_byte, | ||
)?; | ||
} | ||
|
||
inc_pc(pc)?; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name still subject to bikeshedding