Integrate pallet_contracts gas with the weight system (#5712)

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-Authored-By: Sergei Pepyakin <sergei@parity.io>
This commit is contained in:
Alexander Theißen
2020-04-24 10:48:13 +02:00
committed by GitHub
parent e731817e24
commit 000c924b62
20 changed files with 386 additions and 680 deletions
@@ -26,49 +26,20 @@
//! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one.
//! Thus, before executing a contract it should be reinstrument with new schedule.
use crate::gas::{Gas, GasMeter, Token};
use crate::wasm::{prepare, runtime::Env, PrefabWasmModule};
use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Trait};
use sp_std::prelude::*;
use sp_runtime::traits::{Hash, Bounded};
use sp_runtime::traits::Hash;
use frame_support::StorageMap;
/// Gas metering token that used for charging storing code into the code storage.
///
/// Specifies the code length in bytes.
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
pub struct PutCodeToken(u32);
impl<T: Trait> Token<T> for PutCodeToken {
type Metadata = Schedule;
fn calculate_amount(&self, metadata: &Schedule) -> Gas {
metadata
.put_code_per_byte_cost
.checked_mul(self.0.into())
.unwrap_or_else(|| Bounded::max_value())
}
}
/// Put code in the storage. The hash of code is used as a key and is returned
/// as a result of this function.
///
/// This function instruments the given code and caches it in the storage.
pub fn save<T: Trait>(
original_code: Vec<u8>,
gas_meter: &mut GasMeter<T>,
schedule: &Schedule,
) -> Result<CodeHash<T>, &'static str> {
// The first time instrumentation is on the user. However, consequent reinstrumentation
// due to the schedule changes is on governance system.
if gas_meter
.charge(schedule, PutCodeToken(original_code.len() as u32))
.is_out_of_gas()
{
return Err("there is not enough gas for storing the code");
}
let prefab_module = prepare::prepare_contract::<Env>(&original_code, schedule)?;
let code_hash = T::Hashing::hash(&original_code);
+42 -34
View File
@@ -157,12 +157,14 @@ mod tests {
use crate::gas::{Gas, GasMeter};
use crate::tests::{Test, Call};
use crate::wasm::prepare::prepare_contract;
use crate::CodeHash;
use crate::{CodeHash, BalanceOf};
use wabt;
use hex_literal::hex;
use assert_matches::assert_matches;
use sp_runtime::DispatchError;
const GAS_LIMIT: Gas = 10_000_000_000;
#[derive(Debug, PartialEq, Eq)]
struct DispatchEntry(Call);
@@ -373,6 +375,9 @@ mod tests {
)
)
}
fn get_weight_price(&self) -> BalanceOf<Self::T> {
1312_u32.into()
}
}
impl Ext for &mut MockExt {
@@ -478,6 +483,9 @@ mod tests {
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
(**self).get_runtime_storage(key)
}
fn get_weight_price(&self) -> BalanceOf<Self::T> {
(**self).get_weight_price()
}
}
fn execute<E: Ext>(
@@ -544,7 +552,7 @@ mod tests {
CODE_TRANSFER,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -553,7 +561,7 @@ mod tests {
to: 7,
value: 153,
data: Vec::new(),
gas_left: 49978,
gas_left: 9989000000,
}]
);
}
@@ -604,7 +612,7 @@ mod tests {
CODE_CALL,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -613,7 +621,7 @@ mod tests {
to: 9,
value: 6,
data: vec![1, 2, 3, 4],
gas_left: 49971,
gas_left: 9985500000,
}]
);
}
@@ -666,7 +674,7 @@ mod tests {
CODE_INSTANTIATE,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -675,7 +683,7 @@ mod tests {
code_hash: [0x11; 32].into(),
endowment: 3,
data: vec![1, 2, 3, 4],
gas_left: 49947,
gas_left: 9973500000,
}]
);
}
@@ -709,14 +717,14 @@ mod tests {
CODE_TERMINATE,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
&mock_ext.terminations,
&[TerminationEntry {
beneficiary: 0x09,
gas_left: 49989,
gas_left: 9994500000,
}]
);
}
@@ -767,7 +775,7 @@ mod tests {
&CODE_TRANSFER_LIMITED_GAS,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -860,7 +868,7 @@ mod tests {
CODE_GET_STORAGE,
vec![],
mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() });
@@ -924,7 +932,7 @@ mod tests {
CODE_CALLER,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -986,7 +994,7 @@ mod tests {
CODE_ADDRESS,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -1041,7 +1049,7 @@ mod tests {
#[test]
fn balance() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_BALANCE,
vec![],
@@ -1101,7 +1109,7 @@ mod tests {
#[test]
fn gas_price() {
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_GAS_PRICE,
vec![],
@@ -1159,7 +1167,7 @@ mod tests {
#[test]
fn gas_left() {
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_GAS_LEFT,
@@ -1169,7 +1177,7 @@ mod tests {
).unwrap();
let gas_left = Gas::decode(&mut output.data.as_slice()).unwrap();
assert!(gas_left < 50_000, "gas_left must be less than initial");
assert!(gas_left < GAS_LIMIT, "gas_left must be less than initial");
assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final");
}
@@ -1224,7 +1232,7 @@ mod tests {
#[test]
fn value_transferred() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_VALUE_TRANSFERRED,
vec![],
@@ -1260,7 +1268,7 @@ mod tests {
CODE_DISPATCH_CALL,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -1300,7 +1308,7 @@ mod tests {
CODE_RETURN_FROM_START_FN,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] });
@@ -1357,7 +1365,7 @@ mod tests {
#[test]
fn now() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_TIMESTAMP_NOW,
vec![],
@@ -1416,7 +1424,7 @@ mod tests {
#[test]
fn minimum_balance() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_MINIMUM_BALANCE,
vec![],
@@ -1475,7 +1483,7 @@ mod tests {
#[test]
fn tombstone_deposit() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_TOMBSTONE_DEPOSIT,
vec![],
@@ -1543,7 +1551,7 @@ mod tests {
#[test]
fn random() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_RANDOM,
@@ -1588,7 +1596,7 @@ mod tests {
#[test]
fn deposit_event() {
let mut mock_ext = MockExt::default();
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_DEPOSIT_EVENT,
vec![],
@@ -1601,7 +1609,7 @@ mod tests {
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
]);
assert_eq!(gas_meter.gas_left(), 49934);
assert_eq!(gas_meter.gas_left(), 9967000000);
}
const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
@@ -1634,7 +1642,7 @@ mod tests {
#[test]
fn deposit_event_max_topics() {
// Checks that the runtime traps if there are more than `max_topic_events` topics.
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
assert_matches!(
execute(
@@ -1678,7 +1686,7 @@ mod tests {
#[test]
fn deposit_event_duplicates() {
// Checks that the runtime traps if there are duplicates.
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
assert_matches!(
execute(
@@ -1749,7 +1757,7 @@ mod tests {
CODE_BLOCK_NUMBER,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -1789,7 +1797,7 @@ mod tests {
CODE_SIMPLE_ASSERT,
input_data,
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output.data.len(), 0);
@@ -1805,7 +1813,7 @@ mod tests {
CODE_SIMPLE_ASSERT,
input_data,
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).err().unwrap();
assert_eq!(error.buffer.capacity(), 1_234);
@@ -1859,7 +1867,7 @@ mod tests {
CODE_RETURN_WITH_DATA,
hex!("00112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() });
@@ -1872,7 +1880,7 @@ mod tests {
CODE_RETURN_WITH_DATA,
hex!("112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
@@ -1958,7 +1966,7 @@ mod tests {
#[test]
fn get_runtime_storage() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let mock_ext = MockExt::default();
// "\01\02\03\04" - Some(0x14144020)
+11 -12
View File
@@ -16,11 +16,11 @@
//! Environment definition of the wasm smart-contract runtime.
use crate::{Schedule, Trait, CodeHash, ComputeDispatchFee, BalanceOf};
use crate::{Schedule, Trait, CodeHash, BalanceOf};
use crate::exec::{
Ext, ExecResult, ExecError, ExecReturnValue, StorageKey, TopicOf, STATUS_SUCCESS,
};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult, approx_gas_for_balance};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult};
use sp_sandbox;
use frame_system;
use sp_std::{prelude::*, mem, convert::TryInto};
@@ -32,6 +32,7 @@ use sp_io::hashing::{
blake2_128,
sha2_256,
};
use frame_support::weights::GetDispatchInfo;
/// The value returned from ext_call and ext_instantiate contract external functions if the call or
/// instantiation traps. This value is chosen as if the execution does not trap, the return value
@@ -153,8 +154,8 @@ pub enum RuntimeToken {
/// The given number of bytes is read from the sandbox memory and
/// is returned as the return data buffer of the call.
ReturnData(u32),
/// Dispatch fee calculated by `T::ComputeDispatchFee`.
ComputedDispatchFee(Gas),
/// Dispatched a call with the given weight.
DispatchWithWeight(Gas),
/// (topic_count, data_bytes): A buffer of the given size is posted as an event indexed with the
/// given number of topics.
DepositEvent(u32, u32),
@@ -195,7 +196,7 @@ impl<T: Trait> Token<T> for RuntimeToken {
data_and_topics_cost.checked_add(metadata.event_base_cost)
)
},
ComputedDispatchFee(gas) => Some(gas),
DispatchWithWeight(gas) => gas.checked_add(metadata.dispatch_base_cost),
};
value.unwrap_or_else(|| Bounded::max_value())
@@ -692,7 +693,7 @@ define_env!(Env, <E: Ext>,
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
ext_gas_price(ctx) => {
ctx.scratch_buf.clear();
ctx.gas_meter.gas_price().encode_to(&mut ctx.scratch_buf);
ctx.ext.get_weight_price().encode_to(&mut ctx.scratch_buf);
Ok(())
},
@@ -783,16 +784,14 @@ define_env!(Env, <E: Ext>,
let call: <<E as Ext>::T as Trait>::Call =
read_sandbox_memory_as(ctx, call_ptr, call_len)?;
// Charge gas for dispatching this call.
let fee = {
let balance_fee = <<E as Ext>::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call);
approx_gas_for_balance(ctx.gas_meter.gas_price(), balance_fee)
};
// We already deducted the len costs when reading from the sandbox.
// Bill on the actual weight of the dispatched call.
let info = call.get_dispatch_info();
charge_gas(
&mut ctx.gas_meter,
ctx.schedule,
&mut ctx.special_trap,
RuntimeToken::ComputedDispatchFee(fee)
RuntimeToken::DispatchWithWeight(info.weight)
)?;
ctx.ext.note_dispatch_call(call);