-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
"contract" op code sanity checks #1453
Changes from 34 commits
8df83c3
333e74a
8fd9633
864fe3b
1fe61c6
39be18e
b79b577
0def296
773b8e1
dc2aa9d
7e4c311
31fa121
919127f
e58abf7
e43b05b
1b89fb7
dd62a49
0769262
7cc0428
c51a092
48ad913
90db502
b8195ba
ef78901
faa299c
e8b041d
88bab84
6d952b7
72319ee
6ee19be
8fd7b89
d811014
b14aecb
f26ec0d
f14aa0d
a3f7060
afc9488
a66e197
a3936c1
b40f617
37bfa17
e24af3e
7d24aad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ use rand::SeedableRng; | |
|
||
use ethnum::U256; | ||
use fuel_core_benches::*; | ||
use fuel_core_chain_config::ContractConfig; | ||
use fuel_core_types::{ | ||
fuel_asm::{ | ||
op, | ||
|
@@ -41,8 +42,20 @@ use fuel_core_types::{ | |
secp256r1, | ||
*, | ||
}, | ||
fuel_tx::UniqueIdentifier, | ||
fuel_types::AssetId, | ||
fuel_tx::{ | ||
ContractIdExt, | ||
Input, | ||
Output, | ||
TxPointer, | ||
UniqueIdentifier, | ||
UtxoId, | ||
}, | ||
fuel_types::{ | ||
AssetId, | ||
Bytes32, | ||
ContractId, | ||
}, | ||
fuel_vm::checked_transaction::EstimatePredicates, | ||
}; | ||
|
||
mod utils; | ||
|
@@ -58,6 +71,10 @@ use utils::{ | |
#[global_allocator] | ||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; | ||
|
||
const STATE_SIZE: u64 = 10_000_000; | ||
const TARGET_BLOCK_GAS_LIMIT: u64 = 100_000; | ||
const BASE: u64 = 10_000; | ||
|
||
fn run( | ||
id: &str, | ||
group: &mut BenchmarkGroup<WallTime>, | ||
|
@@ -70,8 +87,6 @@ fn run( | |
.build() | ||
.unwrap(); | ||
let _drop = rt.enter(); | ||
const TARGET_BLOCK_GAS_LIMIT: u64 = 100_000; | ||
const BASE: u64 = 10_000; | ||
|
||
let database = Database::rocksdb(); | ||
let mut config = Config::local_node(); | ||
|
@@ -92,8 +107,8 @@ fn run( | |
|
||
b.to_async(&rt).iter(|| { | ||
let shared = service.shared.clone(); | ||
|
||
let tx = fuel_core_types::fuel_tx::TransactionBuilder::script( | ||
// Infinite loop | ||
script.clone().into_iter().collect(), | ||
script_data.clone(), | ||
) | ||
|
@@ -102,7 +117,7 @@ fn run( | |
.add_unsigned_coin_input( | ||
SecretKey::random(&mut rng), | ||
rng.gen(), | ||
u64::MAX, | ||
u64::MAX / 2, | ||
AssetId::BASE, | ||
Default::default(), | ||
Default::default(), | ||
|
@@ -138,6 +153,207 @@ fn run( | |
}); | ||
} | ||
|
||
/// Sets up a service with a full database. Returns the service with the associated Runtime. | ||
/// The size of the database can be overridden with the `STATE_SIZE` environment variable. | ||
fn service_with_contract_id( | ||
contract_id: ContractId, | ||
) -> (fuel_core::service::FuelService, tokio::runtime::Runtime) { | ||
let rt = tokio::runtime::Builder::new_current_thread() | ||
.enable_all() | ||
.build() | ||
.unwrap(); | ||
let _drop = rt.enter(); | ||
let mut database = Database::rocksdb(); | ||
let mut config = Config::local_node(); | ||
config | ||
.chain_conf | ||
.consensus_parameters | ||
.tx_params | ||
.max_gas_per_tx = TARGET_BLOCK_GAS_LIMIT; | ||
config.chain_conf.initial_state.as_mut().unwrap().contracts = | ||
Some(vec![ContractConfig { | ||
contract_id, | ||
code: vec![], | ||
salt: Default::default(), | ||
state: None, | ||
balances: None, | ||
tx_id: None, | ||
output_index: None, | ||
tx_pointer_block_height: None, | ||
tx_pointer_tx_idx: None, | ||
}]); | ||
|
||
config | ||
.chain_conf | ||
.consensus_parameters | ||
.predicate_params | ||
.max_gas_per_predicate = TARGET_BLOCK_GAS_LIMIT; | ||
config.chain_conf.block_gas_limit = TARGET_BLOCK_GAS_LIMIT; | ||
config.utxo_validation = false; | ||
config.block_production = Trigger::Instant; | ||
|
||
// Override state size if the env var is set | ||
let state_size = std::env::var_os("STATE_SIZE") | ||
.map(|value| { | ||
let value = value.to_str().unwrap(); | ||
let value = value.parse::<u64>().unwrap(); | ||
println!("Overriding state size with {}", value); | ||
value | ||
}) | ||
.unwrap_or(STATE_SIZE); | ||
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. [nit] Accepting |
||
|
||
database | ||
.init_contract_state( | ||
&contract_id, | ||
(0..state_size).map(|k| { | ||
let mut key = Bytes32::zeroed(); | ||
key.as_mut()[..8].copy_from_slice(&k.to_be_bytes()); | ||
(key, key) | ||
}), | ||
) | ||
.unwrap(); | ||
database | ||
.init_contract_balances( | ||
&contract_id, | ||
(0..state_size).map(|k| { | ||
let key = k / 2; | ||
let mut sub_id = Bytes32::zeroed(); | ||
sub_id.as_mut()[..8].copy_from_slice(&key.to_be_bytes()); | ||
|
||
let asset = if k % 2 == 0 { | ||
VmBench::CONTRACT.asset_id(&sub_id) | ||
} else { | ||
AssetId::new(*sub_id) | ||
}; | ||
(asset, key + 1_000) | ||
}), | ||
) | ||
.unwrap(); | ||
|
||
let service = fuel_core::service::FuelService::new(database, config.clone()) | ||
.expect("Unable to start a FuelService"); | ||
service.start().expect("Unable to start the service"); | ||
(service, rt) | ||
} | ||
|
||
/// Runs benchmark for `script` with prepared `service` and specified contract (by 'contract_id') which should be | ||
/// included in service | ||
#[allow(clippy::too_many_arguments)] | ||
fn run_with_service( | ||
id: &str, | ||
group: &mut BenchmarkGroup<WallTime>, | ||
script: Vec<Instruction>, | ||
script_data: Vec<u8>, | ||
service: &fuel_core::service::FuelService, | ||
contract_id: ContractId, | ||
rt: &tokio::runtime::Runtime, | ||
rng: &mut rand::rngs::StdRng, | ||
) { | ||
run_with_service_with_extra_inputs( | ||
id, | ||
group, | ||
script, | ||
script_data, | ||
service, | ||
contract_id, | ||
rt, | ||
rng, | ||
vec![], | ||
vec![], | ||
); | ||
} | ||
|
||
/// Runs benchmark for `script` with prepared `service` and specified contract (by `contract_id`) which should be | ||
/// included in service. | ||
/// Also include additional inputs and outputs in transaction | ||
#[allow(clippy::too_many_arguments)] | ||
fn run_with_service_with_extra_inputs( | ||
id: &str, | ||
group: &mut BenchmarkGroup<WallTime>, | ||
script: Vec<Instruction>, | ||
script_data: Vec<u8>, | ||
service: &fuel_core::service::FuelService, | ||
contract_id: ContractId, | ||
rt: &tokio::runtime::Runtime, | ||
rng: &mut rand::rngs::StdRng, | ||
extra_inputs: Vec<Input>, | ||
extra_outputs: Vec<Output>, | ||
) { | ||
group.bench_function(id, |b| { | ||
|
||
b.to_async(rt).iter(|| { | ||
let shared = service.shared.clone(); | ||
|
||
|
||
let mut tx_builder = fuel_core_types::fuel_tx::TransactionBuilder::script( | ||
script.clone().into_iter().collect(), | ||
script_data.clone(), | ||
); | ||
tx_builder | ||
.gas_limit(TARGET_BLOCK_GAS_LIMIT - BASE) | ||
.gas_price(1) | ||
.add_unsigned_coin_input( | ||
SecretKey::random(rng), | ||
rng.gen(), | ||
u32::MAX as u64, | ||
AssetId::BASE, | ||
Default::default(), | ||
Default::default(), | ||
); | ||
let input_count = tx_builder.inputs().len(); | ||
|
||
let contract_input = Input::contract( | ||
UtxoId::default(), | ||
Bytes32::zeroed(), | ||
Bytes32::zeroed(), | ||
TxPointer::default(), | ||
contract_id, | ||
); | ||
let contract_output = Output::contract(input_count as u8, Bytes32::zeroed(), Bytes32::zeroed()); | ||
|
||
tx_builder | ||
.add_input(contract_input) | ||
.add_output(contract_output); | ||
|
||
for input in &extra_inputs { | ||
tx_builder.add_input(input.clone()); | ||
} | ||
|
||
for output in &extra_outputs { | ||
tx_builder.add_output(*output); | ||
} | ||
let mut tx = tx_builder.finalize_as_transaction(); | ||
tx.estimate_predicates(&shared.config.chain_conf.consensus_parameters.clone().into()).unwrap(); | ||
async move { | ||
let tx_id = tx.id(&shared.config.chain_conf.consensus_parameters.chain_id); | ||
|
||
let mut sub = shared.block_importer.block_importer.subscribe(); | ||
shared | ||
.txpool | ||
.insert(vec![std::sync::Arc::new(tx)]) | ||
.await | ||
.into_iter() | ||
.next() | ||
.expect("Should be at least 1 element") | ||
.expect("Should include transaction successfully"); | ||
let res = sub.recv().await.expect("Should produce a block"); | ||
assert_eq!(res.tx_status.len(), 2, "res.tx_status: {:?}", res.tx_status); | ||
assert_eq!(res.sealed_block.entity.transactions().len(), 2); | ||
assert_eq!(res.tx_status[0].id, tx_id); | ||
|
||
let fuel_core_types::services::executor::TransactionExecutionResult::Failed { | ||
reason, | ||
.. | ||
} = &res.tx_status[0].result | ||
else { | ||
panic!("The execution should fails with out of gas") | ||
}; | ||
assert!(reason.contains("OutOfGas")); | ||
} | ||
}) | ||
}); | ||
} | ||
|
||
fn block_target_gas(c: &mut Criterion) { | ||
let mut group = c.benchmark_group("block target estimation"); | ||
|
||
|
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.
Note: Mitch and I talked about this pattern, where the service and runtime are coupled together. It is not ideal and we can investigate ways to clean this up, either as part of this PR, or in a follow up issue.