Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

feat(hyperdrive): rework merkle root submission #63

Merged
merged 4 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jobs:
# - name: Check and Lint Code
# run: cargo clippy -- -D warnings

- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --check

- name: Run cargo check for release
uses: actions-rs/cargo@v1
with:
Expand Down
2 changes: 1 addition & 1 deletion pallets/acurast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ pub type JobRegistrationFor<T> =
#[frame_support::pallet]
pub mod pallet {
use acurast_common::*;
use core::ops::AddAssign;
use frame_support::{
dispatch::DispatchResultWithPostInfo, ensure, pallet_prelude::*, traits::UnixTime,
Blake2_128Concat, PalletId,
};
use frame_system::pallet_prelude::*;
use sp_std::prelude::*;
use core::ops::AddAssign;

use crate::{traits::*, utils::*, JobRegistrationFor};

Expand Down
5 changes: 3 additions & 2 deletions pallets/hyperdrive/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ benchmarks_instance_pallet! {
submit_state_merkle_root {
// add the transmitters and submit before benchmarked extrinsic
let (caller, _) = update_state_transmitters_helper::<T, I>(1, true);
}: _(RawOrigin::Signed(caller.clone()), 5.into(), HASH.into())
}: _(RawOrigin::Signed(caller.clone()), 1.into(), HASH.into())
verify {
assert_event::<T, I>(Event::StateMerkleRootSubmitted{
block: 5.into(),
source: caller.clone(),
snapshot: 1.into(),
state_merkle_root: HASH.into()
}.into());
}
Expand Down
80 changes: 57 additions & 23 deletions pallets/hyperdrive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub mod pallet {
use frame_system::pallet_prelude::*;
use sp_arithmetic::traits::{CheckedRem, Zero};
use sp_runtime::traits::Hash;
use sp_std::collections::btree_set::BTreeSet;
use sp_std::prelude::*;
use sp_std::vec;

use types::*;

Expand Down Expand Up @@ -79,6 +82,7 @@ pub mod pallet {
+ MaxEncodedLen
+ TypeInfo
+ Zero
+ From<u8>
+ CheckedRem;
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
type TargetChainHashing: Hash<Output = Self::TargetChainHash> + TypeInfo;
Expand Down Expand Up @@ -106,11 +110,12 @@ pub mod pallet {
removed: Vec<T::AccountId>,
},
StateMerkleRootSubmitted {
block: T::TargetChainBlockNumber,
source: T::AccountId,
snapshot: T::TargetChainBlockNumber,
state_merkle_root: T::TargetChainHash,
},
StateMerkleRootAccepted {
block: T::TargetChainBlockNumber,
snapshot: T::TargetChainBlockNumber,
state_merkle_root: T::TargetChainHash,
},
}
Expand All @@ -129,6 +134,17 @@ pub mod pallet {
ValueQuery,
>;

#[pallet::type_value]
pub fn FirstSnapshot<T: Config<I>, I: 'static>() -> T::TargetChainBlockNumber {
1u8.into()
}

/// This storage field contains the latest validated snapshot number.
#[pallet::storage]
#[pallet::getter(fn latest_snapshot)]
pub type CurrentSnapshot<T: Config<I>, I: 'static = ()> =
StorageValue<_, T::TargetChainBlockNumber, ValueQuery, FirstSnapshot<T, I>>;

#[pallet::storage]
#[pallet::getter(fn state_merkle_root)]
pub type StateMerkleRootCount<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
Expand All @@ -137,15 +153,15 @@ pub mod pallet {
T::TargetChainBlockNumber,
Identity,
T::TargetChainHash,
u8,
BTreeSet<T::AccountId>,
>;

#[pallet::error]
pub enum Error<T, I = ()> {
/// A known transmitter submits outside the window of activity he is permissioned to.
SubmitOutsideTransmitterActivityWindow,
SubmitOutsideTransmissionRate,
CalculationOverflow,
UnexpectedSnapshot,
}

#[pallet::call]
Expand Down Expand Up @@ -206,10 +222,17 @@ pub mod pallet {
#[pallet::weight(< T as Config<I>>::WeightInfo::submit_state_merkle_root())]
pub fn submit_state_merkle_root(
origin: OriginFor<T>,
block: T::TargetChainBlockNumber,
snapshot: T::TargetChainBlockNumber,
state_merkle_root: T::TargetChainHash,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let expected_snapshot = Self::latest_snapshot();

// Ensure merkle roots are submitted sequentially
ensure!(
snapshot == expected_snapshot,
Error::<T, I>::UnexpectedSnapshot
);

let activity_window = <StateTransmitter<T, I>>::get(&who);
let current_block = <frame_system::Pallet<T>>::block_number();
Expand All @@ -219,32 +242,42 @@ pub mod pallet {
&& current_block < activity_window.end_block,
Error::<T, I>::SubmitOutsideTransmitterActivityWindow
);
ensure!(
block
.checked_rem(&T::TransmissionRate::get())
.ok_or(Error::<T, I>::CalculationOverflow)?
.is_zero(),
Error::<T, I>::SubmitOutsideTransmissionRate
);

// insert merkle root proposal since all checks passed
// allows for constant-time validity checks
let accepted =
StateMerkleRootCount::<T, I>::mutate(&block, &state_merkle_root, |count| {
let count_ = count.unwrap_or(0) + 1;
*count = Some(count_);
count_ >= <T as Config<I>>::TransmissionQuorum::get()
});
let accepted = StateMerkleRootCount::<T, I>::mutate(
&snapshot,
&state_merkle_root,
|submissions| {
// This can be improved once [let chains feature](https://github.com/rust-lang/rust/issues/53667) lands
if let Some(transmitters) = submissions {
if !transmitters.contains(&who) {
RomarQ marked this conversation as resolved.
Show resolved Hide resolved
transmitters.insert(who.clone());
}
} else {
let mut set = BTreeSet::<T::AccountId>::new();
set.insert(who.clone());
*submissions = Some(set);
}

let submissions_count = submissions
.as_ref()
.map_or(0usize, |transmitters| transmitters.len());
return submissions_count >= T::TransmissionQuorum::get().into();
},
);

// Emit event to inform that the state merkle root has been sumitted
Self::deposit_event(Event::StateMerkleRootSubmitted {
block,
source: who,
snapshot,
state_merkle_root,
});

if accepted {
CurrentSnapshot::<T, I>::set(expected_snapshot + T::TransmissionRate::get());
Self::deposit_event(Event::StateMerkleRootAccepted {
block,
snapshot,
state_merkle_root,
});
}
Expand All @@ -259,9 +292,10 @@ pub mod pallet {
block: T::TargetChainBlockNumber,
state_merkle_root: T::TargetChainHash,
) -> bool {
StateMerkleRootCount::<T, I>::get(&block, &state_merkle_root).map_or(false, |count| {
count >= <T as Config<I>>::TransmissionQuorum::get()
})
StateMerkleRootCount::<T, I>::get(&block, &state_merkle_root)
.map_or(false, |submissions| {
submissions.len() >= T::TransmissionQuorum::get().into()
})
}
}
}
68 changes: 31 additions & 37 deletions pallets/hyperdrive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,35 +149,28 @@ fn submit_outside_activity_window() {
assert_err!(
TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(alice_account_id()),
5,
1,
HASH
),
Error::<Test, ()>::SubmitOutsideTransmitterActivityWindow
);

System::set_block_number(10);
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(alice_account_id()),
10,
HASH
));

System::set_block_number(19);
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(alice_account_id()),
10,
HASH
));

System::set_block_number(20);
assert_err!(
TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(bob_account_id()),
5,
RuntimeOrigin::signed(alice_account_id()),
1,
HASH
),
Error::<Test, ()>::SubmitOutsideTransmitterActivityWindow
);

System::set_block_number(10);
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(alice_account_id()),
1,
HASH
));
});
}

Expand Down Expand Up @@ -206,7 +199,7 @@ fn submit_outside_transmission_rate() {
6,
HASH
),
Error::<Test, ()>::SubmitOutsideTransmitterActivityWindow
Error::<Test, ()>::UnexpectedSnapshot
);
});
}
Expand Down Expand Up @@ -240,30 +233,33 @@ fn submit_state_merkle_root() {

System::set_block_number(10);

// first submission for target chain block 10
// first submission for target chain snapshot 1
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(alice_account_id()),
10,
1,
HASH
));
// does not validate until quorum reached
assert_eq!(TezosHyperdrive::validate_state_merkle_root(10, HASH), false);
assert_eq!(TezosHyperdrive::validate_state_merkle_root(1, HASH), false);

// intermitted submission for different block is allowed!
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(bob_account_id()),
15,
HASH
));
// intermitted submission for different snapshot is not allowed!
assert_err!(
TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(bob_account_id()),
2,
HASH
),
Error::<Test, ()>::UnexpectedSnapshot
);

// second submission for target chain block 10
// second submission for target chain snapshot 1
assert_ok!(TezosHyperdrive::submit_state_merkle_root(
RuntimeOrigin::signed(bob_account_id()),
10,
1,
HASH
));
// does validate since quorum reached
assert_eq!(TezosHyperdrive::validate_state_merkle_root(10, HASH), true);
assert_eq!(TezosHyperdrive::validate_state_merkle_root(1, HASH), true);

assert_eq!(
events(),
Expand All @@ -289,19 +285,17 @@ fn submit_state_merkle_root() {
removed: vec![],
}),
RuntimeEvent::TezosHyperdrive(crate::Event::StateMerkleRootSubmitted {
block: 10,
state_merkle_root: HASH
}),
RuntimeEvent::TezosHyperdrive(crate::Event::StateMerkleRootSubmitted {
block: 15,
source: alice_account_id(),
snapshot: 1,
state_merkle_root: HASH
}),
RuntimeEvent::TezosHyperdrive(crate::Event::StateMerkleRootSubmitted {
block: 10,
source: bob_account_id(),
snapshot: 1,
state_merkle_root: HASH
}),
RuntimeEvent::TezosHyperdrive(crate::Event::StateMerkleRootAccepted {
block: 10,
snapshot: 1,
state_merkle_root: HASH
})
]
Expand Down
2 changes: 1 addition & 1 deletion pallets/marketplace/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use sp_runtime::BoundedVec;
use sp_std::prelude::*;

pub use pallet::Config;
use pallet_acurast::{Event as AcurastEvent, JobRegistrationFor, Script, MultiOrigin};
use pallet_acurast::{Event as AcurastEvent, JobRegistrationFor, MultiOrigin, Script};
use pallet_acurast::{Pallet as Acurast, Schedule};

pub use crate::stub::*;
Expand Down