Skip to content

Commit

Permalink
Rolldown on_idle_hook & configurable dispute period (#843)
Browse files Browse the repository at this point in the history
- introduces configurable dispute periods per l1. Intial values can be
provided in chain spec. And modified later on using dedicated extrinsic
`set_dispute_period` (**root only**)
- adds benchmarks for functions used in hooks, to simplify weight
calculations
- accounts for huge read/writes based on substrate diagram (2mb)
- refactoring of the logic of updates scheduler/executed:
* `on_initialize_hook` - schedules requests to be executed as well as
moves execution counter to the next element in the queue if the previous
one was already executed.
* `on_idle_hook` - Executes some requests from current block based on
available weight.


Behavior change:
**Now update that is scheduled to be executed at block `N` may start
being executed in the same block**
**Stops executing of given update as soon as maintanance mode is
triggered** https://mangatafinance.atlassian.net/browse/GASP-1632
  • Loading branch information
mateuszaaa authored Nov 29, 2024
2 parents 07f2897 + 1569ee0 commit 0a07b8c
Show file tree
Hide file tree
Showing 12 changed files with 795 additions and 489 deletions.
215 changes: 170 additions & 45 deletions pallets/rolldown/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,17 @@ mod benchmarks {
.build();
let update_hash = update.abi_encode_hash();

let metadata = UpdateMetadata {
min_id: 0u128,
max_id: 0u128,
update_size: 0u128,
sequencer: SEQUENCER_ACCOUNT,
update_hash,
};

<frame_system::Pallet<T>>::set_block_number(1u32.into());
let dispute_period_end = <frame_system::Pallet<T>>::block_number().saturated_into::<u128>() +
Rolldown::<T>::get_dispute_period();
Rolldown::<T>::get_dispute_period(l1_chain).unwrap();
assert!(
PendingSequencerUpdates::<T>::get(dispute_period_end, l1_chain).is_none(),
"BEFORE PendingSequencerUpdates {:?} dispute_period_end {:?} l1_chain should be uninit",
Expand Down Expand Up @@ -408,7 +416,7 @@ mod benchmarks {

<frame_system::Pallet<T>>::set_block_number(1u32.into());
let dispute_period_end = <frame_system::Pallet<T>>::block_number().saturated_into::<u128>() +
Rolldown::<T>::get_dispute_period();
Rolldown::<T>::get_dispute_period(l1_chain).unwrap();
assert!(
PendingSequencerUpdates::<T>::get(dispute_period_end, l1_chain).is_none(),
"BEFORE PendingSequencerUpdates {:?} dispute_period_end {:?} l1_chain should be uninit",
Expand Down Expand Up @@ -468,6 +476,7 @@ mod benchmarks {
Ok(())
}

//FIX: check and possibly allign this benchmark
#[benchmark]
fn cancel_requests_from_l1() -> Result<(), BenchmarkError> {
setup_sequencer::<T>(SEQUENCER_ACCOUNT.into(), None, true, false)?;
Expand All @@ -476,16 +485,24 @@ mod benchmarks {
get_chain_and_address_for_asset_id::<T>(TOKEN_ID.into())?;
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();

let requests = vec![L1UpdateRequest::Deposit(Default::default())];
let update = L1UpdateBuilder::default()
.with_requests(vec![L1UpdateRequest::Deposit(Default::default())])
.build();

let update_hash = H256::default();
let sequencer_account: T::AccountId = SEQUENCER_ACCOUNT.into();
PendingSequencerUpdates::<T>::insert(
DUMMY_REQUEST_ID,
l1_chain,
(sequencer_account, update, H256::default()),
);
let metadata = UpdateMetadata {
min_id: 1u128,
max_id: 1u128,
update_size: 1u128,
sequencer: sequencer_account.clone(),
update_hash: H256::zero(),
};

PendingSequencerUpdates::<T>::insert(DUMMY_REQUEST_ID, l1_chain, metadata);

PendingSequencerUpdateContent::<T>::insert(update_hash, update);

assert!(
L2Requests::<T>::get(l1_chain, RequestId::from((Origin::L2, FIRST_REQUEST_ID)))
Expand All @@ -510,19 +527,24 @@ mod benchmarks {
Ok(())
}

//FIX: check and possibly allign this benchmark
#[benchmark]
fn force_cancel_requests_from_l1() -> Result<(), BenchmarkError> {
setup_sequencer::<T>(SEQUENCER_ACCOUNT.into(), None, true, true)?;
let (l1_aset_chain, l1_asset_address) =
get_chain_and_address_for_asset_id::<T>(TOKEN_ID.into())?;
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();

let metadata = UpdateMetadata::<T::AccountId> {
min_id: 1u128,
max_id: 1u128,
update_size: 1u128,
sequencer: T::AddressConverter::convert(SEQUENCER_ACCOUNT),
update_hash: H256::zero(),
};

let sequencer_account: T::AccountId = SEQUENCER_ACCOUNT.into();
PendingSequencerUpdates::<T>::insert(
DUMMY_REQUEST_ID,
l1_chain,
(sequencer_account, messages::L1Update::default(), H256::default()),
);
PendingSequencerUpdates::<T>::insert(DUMMY_REQUEST_ID, l1_chain, metadata);

assert!(
T::SequencerStakingProvider::is_selected_sequencer(l1_chain, &SEQUENCER_ACCOUNT.into()),
Expand Down Expand Up @@ -885,51 +907,154 @@ mod benchmarks {
}

#[benchmark]
fn schedule_requests(x: Linear<2, 200>) -> Result<(), BenchmarkError> {
fn load_next_update_from_execution_queue() -> Result<(), BenchmarkError> {
let current_execution_id = 1u128;
let next_execution_id = 2u128;
let scheduled_at: BlockNumberFor<T> = 19u32.into();
let (l1_aset_chain, _) = get_chain_and_address_for_asset_id::<T>(TOKEN_ID.into())?;
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();

UpdatesExecutionQueue::<T>::remove(current_execution_id);
UpdatesExecutionQueue::<T>::insert(
next_execution_id,
(scheduled_at, l1_chain, H256::zero(), 10u128),
);
UpdatesExecutionQueueNextId::<T>::put(current_execution_id);

#[block]
{
Rolldown::<T>::load_next_update_from_execution_queue();
}

assert_eq!(UpdatesExecutionQueueNextId::<T>::get(), next_execution_id);

Ok(())
}

#[benchmark]
fn schedule_request_for_execution_if_dispute_period_has_passsed() -> Result<(), BenchmarkError>
{
setup_account::<T>(USER_ACCOUNT.into())?;
setup_and_do_withdrawal::<T>(USER_ACCOUNT.into())?;

let block_for_automatic_batch =
(<T as Config>::MerkleRootAutomaticBatchSize::get() + 1u128).saturated_into::<u32>();
let chain: <T as Config>::ChainId = crate::messages::Chain::Ethereum.into();
let update_hash =
H256::from(hex!("1111111111111111111111111111111111111111111111111111111111111111"));

let sequencer_account: T::AccountId = SEQUENCER_ACCOUNT.into();
let metadata = UpdateMetadata {
min_id: 1u128,
max_id: 1u128,
update_size: 1u128,
sequencer: sequencer_account.clone(),
update_hash,
};

PendingSequencerUpdates::<T>::insert(1u128, chain, metadata);
assert_eq!(LastScheduledUpdateIdInExecutionQueue::<T>::get(), 0u128);
assert_eq!(UpdatesExecutionQueue::<T>::get(FIRST_SCHEDULED_UPDATE_ID), None);

#[block]
{
Rolldown::<T>::schedule_request_for_execution_if_dispute_period_has_passsed(
block_for_automatic_batch.into(),
);
}

assert_eq!(LastScheduledUpdateIdInExecutionQueue::<T>::get(), 1u128);
assert_eq!(
UpdatesExecutionQueue::<T>::get(FIRST_SCHEDULED_UPDATE_ID),
Some((block_for_automatic_batch.into(), chain, update_hash, 1u128))
);

Ok(())
}

#[benchmark]
fn maybe_create_batch() -> Result<(), BenchmarkError> {
// trigger batch creating because of automatic batch size
setup_sequencer::<T>(SEQUENCER_ACCOUNT.into(), None, true, true)?;

let (l1_aset_chain, l1_asset_address) =
get_chain_and_address_for_asset_id::<T>(TOKEN_ID.into())?;
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();
let automatic_batch_size = Pallet::<T>::automatic_batch_size();

let x_deposits: usize = (x as usize) / 2;
let x_cancel_resolution: usize = (x as usize) - x_deposits;
let mut update = L1UpdateBuilder::default()
.with_requests(
[
vec![L1UpdateRequest::Deposit(Default::default()); x_deposits],
vec![
L1UpdateRequest::CancelResolution(Default::default());
x_cancel_resolution
],
]
.concat(),
)
.build();
let last_batch_id = 1u128;
let latest_element_in_previous_batch = 123u128;
let last_batch_range = (1u128, latest_element_in_previous_batch);
let latest_element_now = latest_element_in_previous_batch + automatic_batch_size;

assert!(
MaxAcceptedRequestIdOnl2::<T>::get(l1_chain).is_zero(),
"BEFORE MaxAcceptedRequestIdOnl2 {:?} chain should be zero",
l1_chain
assert!(T::SequencerStakingProvider::is_selected_sequencer(
l1_chain,
&SEQUENCER_ACCOUNT.into()
));

L2OriginRequestId::<T>::mutate(|map| {
map.insert(l1_chain, latest_element_now.saturating_add(1u128));
});

L2RequestsBatchLast::<T>::mutate(|map| {
map.insert(l1_chain, (20u32.into(), 1u128, (1u128, latest_element_in_previous_batch)));
});

assert_eq!(L2RequestsBatch::<T>::get((l1_chain, 2u128)), None);

<frame_system::Pallet<T>>::set_block_number(20u32.into());

#[block]
{
Rolldown::<T>::maybe_create_batch(21u32.into());
}

assert_eq!(
L2RequestsBatch::<T>::get((l1_chain, 2u128)),
Some((
21u32.into(),
(latest_element_in_previous_batch + 1, latest_element_now),
SEQUENCER_ACCOUNT.into()
))
);
assert!(
UpdatesExecutionQueue::<T>::get(FIRST_SCHEDULED_UPDATE_ID).is_none(),
"BEFORE UpdatesExecutionQueue {:?} scheduled update id should be none",
FIRST_SCHEDULED_UPDATE_ID

Ok(())
}

#[benchmark]
fn execute_requests_from_execute_queue() -> Result<(), BenchmarkError> {
let (l1_aset_chain, l1_asset_address) =
get_chain_and_address_for_asset_id::<T>(TOKEN_ID.into())?;
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();

<frame_system::Pallet<T>>::set_block_number(20u32.into());
let execution_id = 123u128;
let scheduled_at: BlockNumberFor<T> = 19u32.into();
let l1_chain: <T as Config>::ChainId = l1_aset_chain.into();
let update_hash =
H256::from(hex!("1111111111111111111111111111111111111111111111111111111111111111"));

UpdatesExecutionQueueNextId::<T>::put(execution_id);
UpdatesExecutionQueue::<T>::insert(
execution_id,
(scheduled_at, l1_chain, update_hash, 10u128),
);
LastProcessedRequestOnL2::<T>::insert(l1_chain, 0u128);

let update = L1UpdateBuilder::default()
.with_requests(vec![L1UpdateRequest::Deposit(Default::default()); 10_000])
.build();
PendingSequencerUpdateContent::<T>::insert(update_hash, update);

#[block]
{
Rolldown::<T>::schedule_requests(BlockNumberFor::<T>::default(), l1_chain, update);
Rolldown::<T>::execute_requests_from_execute_queue();
}

assert!(
!MaxAcceptedRequestIdOnl2::<T>::get(l1_chain).is_zero(),
"AFTER MaxAcceptedRequestIdOnl2 {:?} chain should NOT be zero",
l1_chain
);
assert!(
UpdatesExecutionQueue::<T>::get(FIRST_SCHEDULED_UPDATE_ID).is_some(),
"AFTER UpdatesExecutionQueue {:?} scheduled update id should be some",
FIRST_SCHEDULED_UPDATE_ID
UpdatesExecutionQueue::<T>::get(execution_id).expect("update partially executed");
assert_eq!(
LastProcessedRequestOnL2::<T>::get(l1_chain),
<T as Config>::RequestsPerBlock::get()
);
Ok(())
}
Expand Down
Loading

0 comments on commit 0a07b8c

Please sign in to comment.