#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage,
dispatch::DispatchResult,
ensure,
traits::{Currency, Get},
weights::Weight,
StorageDoubleMap, StorageMap,
};
use frame_system::{self as system, ensure_signed};
use pallet_contracts::Gas;
use sp_core::crypto::UncheckedFrom;
use sp_runtime::{
traits::{Bounded, Hash, One, SaturatedConversion, Saturating, Zero},
DispatchError, RuntimeDebug,
};
use sp_std::{marker::PhantomData, prelude::*, vec::Vec};
pub use pallet_ovm::{Decision, Property, PropertyOf};
mod deserializer;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub use deserializer::Deserializer;
pub type DispatchResultT<T> = Result<T, DispatchError>;
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct Range<Balance> {
start: Balance,
end: Balance,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct StateUpdate<AccountId, Balance, BlockNumber> {
deposit_contract_address: AccountId,
range: Range<Balance>,
block_number: BlockNumber,
state_object: Property<AccountId>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct Checkpoint<AccountId> {
state_update: Property<AccountId>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct Exit<AccountId, BlockNumber, Balance, Hash> {
state_update: StateUpdate<AccountId, Balance, BlockNumber>,
inclusion_proof: InclusionProof<AccountId, Balance, Hash>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct InclusionProof<AccountId, Balance, Hash> {
address_inclusion_proof: AddressInclusionProof<AccountId, Balance, Hash>,
interval_inclusion_proof: IntervalInclusionProof<Balance, Hash>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct IntervalInclusionProof<Balance, Hash> {
leaf_index: Balance,
leaf_position: Balance,
siblings: Vec<IntervalTreeNode<Balance, Hash>>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct AddressInclusionProof<AccountId, Balance, Hash> {
leaf_index: AccountId,
leaf_position: Balance,
siblings: Vec<AddressTreeNode<AccountId, Hash>>,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct IntervalTreeNode<Balance, Hash> {
data: Hash,
start: Balance,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct AddressTreeNode<AccountId, Hash> {
data: Hash,
token_address: AccountId,
}
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct ExitDeposit<AccountId, Balance, BlockNumber> {
state_update: StateUpdate<AccountId, Balance, BlockNumber>,
checkpoint: Checkpoint<AccountId>,
}
pub type BalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
pub type CheckpointOf<T> = Checkpoint<<T as frame_system::Trait>::AccountId>;
pub type ExitDepositOf<T> = ExitDeposit<
<T as frame_system::Trait>::AccountId,
BalanceOf<T>,
<T as frame_system::Trait>::BlockNumber,
>;
pub type RangeOf<T> = Range<BalanceOf<T>>;
pub type ExitOf<T> = Exit<
<T as frame_system::Trait>::AccountId,
<T as frame_system::Trait>::BlockNumber,
BalanceOf<T>,
<T as frame_system::Trait>::Hash,
>;
pub type StateUpdateOf<T> = StateUpdate<
<T as frame_system::Trait>::AccountId,
BalanceOf<T>,
<T as frame_system::Trait>::BlockNumber,
>;
pub type InclusionProofOf<T> = InclusionProof<
<T as frame_system::Trait>::AccountId,
BalanceOf<T>,
<T as frame_system::Trait>::Hash,
>;
pub type IntervalInclusionProofOf<T> =
IntervalInclusionProof<BalanceOf<T>, <T as frame_system::Trait>::Hash>;
pub type IntervalTreeNodeOf<T> = IntervalTreeNode<BalanceOf<T>, <T as frame_system::Trait>::Hash>;
pub type AddressInclusionProofOf<T> = AddressInclusionProof<
<T as frame_system::Trait>::AccountId,
BalanceOf<T>,
<T as frame_system::Trait>::Hash,
>;
pub type AddressTreeNodeOf<T> =
AddressTreeNode<<T as frame_system::Trait>::AccountId, <T as frame_system::Trait>::Hash>;
pub trait PlappsAddressFor<Hash, AccountId> {
fn plapps_address_for(hash: &Hash, origin: &AccountId) -> AccountId;
}
pub struct SimpleAddressDeterminer<T: Trait>(PhantomData<T>);
impl<T: Trait> PlappsAddressFor<T::Hash, T::AccountId> for SimpleAddressDeterminer<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
fn plapps_address_for(hash: &T::Hash, origin: &T::AccountId) -> T::AccountId {
let mut buf = Vec::new();
buf.extend_from_slice(hash.as_ref());
buf.extend_from_slice(origin.as_ref());
UncheckedFrom::unchecked_from(T::Hashing::hash(&buf[..]))
}
}
pub trait Trait: pallet_ovm::Trait + pallet_contracts::Trait {
type Currency: Currency<Self::AccountId>;
type DeterminePlappsAddress: PlappsAddressFor<Self::Hash, Self::AccountId>;
type MaximumTokenAddress: Get<Self::AccountId>;
type PlasmaHashing: Hash<Output = Self::Hash>;
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
decl_storage! {
trait Store for Module<T: Trait> as Plasma {
AggregatorAddress get(fn aggregator_address): map hasher(twox_64_concat) T::AccountId => T::AccountId;
CurrentBlock get(fn current_block): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;
Blocks get(fn blocks): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::BlockNumber => T::Hash;
ERC20 get(fn erc20): map hasher(twox_64_concat) T::AccountId => T::AccountId;
StateUpdatePredicate get(fn state_update_predicate): map hasher(twox_64_concat) T::AccountId => T::AccountId;
ExitPredicate get(fn exit_predicate): map hasher(twox_64_concat) T::AccountId => T::AccountId;
ExitDepositPredicate get(fn exit_deposit_predicate): map hasher(twox_64_concat) T::AccountId => T::AccountId;
TotalDeposited get(fn total_deposited): map hasher(twox_64_concat) T::AccountId => BalanceOf<T>;
DepositedRanges get(fn deposited_ranges): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) BalanceOf<T> => RangeOf<T>;
Checkpoints get(fn checkpoints): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::Hash => bool;
Payout get(fn payout): map hasher(twox_64_concat) T::AccountId => T::AccountId;
}
}
decl_event!(
pub enum Event<T>
where
AccountId = <T as system::Trait>::AccountId,
Hash = <T as system::Trait>::Hash,
BlockNumber = <T as system::Trait>::BlockNumber,
Range = RangeOf<T>,
Checkpoint = CheckpointOf<T>,
{
Deploy(AccountId, AccountId),
BlockSubmitted(AccountId, BlockNumber, Hash),
CheckpointFinalized(AccountId, Hash, Checkpoint),
ExitFinalized(AccountId, Hash),
DepositedRangeExtended(AccountId, Range),
DepositedRangeRemoved(AccountId, Range),
}
);
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default;
const MaximumTokenAddress: <T as system::Trait>::AccountId = T::MaximumTokenAddress::get();
fn on_runtime_upgrade() -> Weight {
migrate::<T>();
T::MaximumBlockWeight::get()
}
#[weight = 100_000]
fn deploy(
origin,
aggregator_id: T::AccountId,
erc20: T::AccountId,
state_update_predicate: T::AccountId,
exit_predicate: T::AccountId,
exit_deposit_predicate: T::AccountId,
) {
let sender = ensure_signed(origin)?;
let plapps_hash = Self::generate_plapps_hash(
&aggregator_id,
&erc20,
&state_update_predicate,
&exit_predicate,
&exit_deposit_predicate,
);
let plapps_id = T::DeterminePlappsAddress::plapps_address_for(&plapps_hash, &sender);
<AggregatorAddress<T>>::insert(&plapps_id, aggregator_id);
<ERC20<T>>::insert(&plapps_id, erc20);
<StateUpdatePredicate<T>>::insert(&plapps_id, state_update_predicate);
<ExitPredicate<T>>::insert(&plapps_id, exit_predicate);
<ExitDepositPredicate<T>>::insert(&plapps_id, exit_deposit_predicate);
Self::deposit_event(RawEvent::Deploy(sender, plapps_id));
}
#[weight = 100_000]
fn submit_root(origin, plapps_id: T::AccountId,
block_number: T::BlockNumber, root: T::Hash) {
let aggregator = ensure_signed(origin)?;
Self::ensure_aggregator(&plapps_id, &aggregator)?;
ensure!(
Self::current_block(&plapps_id) + T::BlockNumber::one() == block_number,
Error::<T>::BlockNumberShouldBeNextBlock,
);
<Blocks<T>>::insert(&plapps_id, &block_number, root.clone());
<CurrentBlock<T>>::insert(&plapps_id, block_number.clone());
Self::deposit_event(RawEvent::BlockSubmitted(plapps_id, block_number, root));
}
#[weight = 100_000]
fn deposit(origin, plapps_id: T::AccountId,
amount: BalanceOf<T>, initial_state: PropertyOf<T>, gas_limit: Gas) {
let _ = ensure_signed(origin)?;
let total_deposited = Self::total_deposited(&plapps_id);
ensure!(
total_deposited < BalanceOf::<T>::max_value().saturating_sub(amount),
Error::<T>::TotalDepositedExceedMaxBalance,
);
let deposit_range = RangeOf::<T> {
start: total_deposited,
end: total_deposited.saturating_add(amount.clone()),
};
let state_update = PropertyOf::<T> {
predicate_address: Self::state_update_predicate(&plapps_id),
inputs: vec![
plapps_id.encode(),
deposit_range.encode(),
Self::get_latest_plasma_block_number(&plapps_id).encode(),
initial_state.encode(),
],
};
let checkpoint = Checkpoint {
state_update: state_update,
};
Self::bare_extend_deposited_ranges(&plapps_id, amount);
let checkpoint_id = Self::get_checkpoint_id(&checkpoint);
<Checkpoints<T>>::insert(plapps_id.clone(), &checkpoint_id, true);
Self::deposit_event(RawEvent::CheckpointFinalized(plapps_id, checkpoint_id, checkpoint));
}
#[weight = 100_000]
fn extend_deposited_ranges(origin, plapps_id: T::AccountId, amount: BalanceOf<T>) {
ensure_signed(origin)?;
Self::bare_extend_deposited_ranges(&plapps_id, amount);
}
#[weight = 100_000]
fn remove_deposited_range(origin, plapps_id: T::AccountId,
range: RangeOf<T>, deposited_range_id: BalanceOf<T>) {
ensure_signed(origin)?;
Self::bare_remove_deposited_range(
&plapps_id,
&range,
&deposited_range_id,
)?;
}
#[weight = 100_000]
fn finalize_checkpoint(origin, plapps_id: T::AccountId,
checkpoint_property: PropertyOf<T>) {
ensure!(
<pallet_ovm::Module<T>>::is_decided(&checkpoint_property) != Decision::True,
Error::<T>::ClaimMustBeDecided,
);
let property: PropertyOf<T> = Decode::decode(&mut &checkpoint_property.inputs[0][..])
.map_err(|_| Error::<T>::MustBeDecodable)?;
let checkpoint = Checkpoint {
state_update: property,
};
let checkpoint_id = Self::get_checkpoint_id(&checkpoint);
<Checkpoints<T>>::insert(&plapps_id, &checkpoint_id, true);
Self::deposit_event(RawEvent::CheckpointFinalized(plapps_id, checkpoint_id, checkpoint));
}
#[weight = 50_000_000]
fn finalize_exit(origin, plapps_id: T::AccountId,
exit_property: PropertyOf<T>, deposited_range_id: BalanceOf<T>, _owner: T::AccountId) {
let origin = ensure_signed(origin)?;
let state_update = Self::bare_finalize_exit(
&plapps_id,
&exit_property,
&deposited_range_id
)?;
let owner: T::AccountId = Decode::decode(&mut &state_update.state_object.inputs[0][..])
.map_err(|_| Error::<T>::MustBeDecodable)?;
let _amount = state_update.range.end - state_update.range.start;
ensure!(
origin == owner,
Error::<T>::OriginMustBeOwner,
);
}
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
IsNotAggregator,
BlockNumberShouldBeNextBlock,
LeftMustBeLessThanRight,
FirstRightMustBeGreaterThanSibling,
RangeMustNotExceedTheImplicitRange,
AddressMustNotExceedTheImplicitAddress,
TotalDepositedExceedMaxBalance,
MustApproved,
RangeMustBeOfDepositedRange,
ClaimMustBeDecided,
MustBeDecodable,
ExitMustBeDecided,
FinalizeExitMustBeCalledFromPayout,
OriginMustBeOwner,
CheckpointMustBeFinalized,
DepositContractAddressMustBeSame,
BlockNumberMustBeSame,
RangeMustBeSubrangeOfCheckpoint,
DepositContractAddressMustBePlappsId,
}
}
fn migrate<T: Trait>() {
}
impl<T: Trait> Module<T> {
pub fn retrieve(plapps_id: T::AccountId, block_number: T::BlockNumber) -> T::Hash {
<Blocks<T>>::get(&plapps_id, &block_number)
}
pub fn verify_inclusion(
plapps_id: T::AccountId,
leaf: T::Hash,
token_address: T::AccountId,
range: RangeOf<T>,
inclusion_proof: InclusionProofOf<T>,
block_number: T::BlockNumber,
) -> DispatchResultT<bool> {
let root = <Blocks<T>>::get(&plapps_id, &block_number);
Self::verify_inclusion_with_root(leaf, token_address, range, inclusion_proof, root)
}
pub fn verify_inclusion_with_root(
leaf: T::Hash,
token_address: T::AccountId,
range: RangeOf<T>,
inclusion_proof: InclusionProofOf<T>,
root: T::Hash,
) -> DispatchResultT<bool> {
let (computed_root, implicit_end) = Self::compute_interval_tree_root(
&leaf,
&inclusion_proof.interval_inclusion_proof.leaf_index,
&inclusion_proof.interval_inclusion_proof.leaf_position,
&inclusion_proof.interval_inclusion_proof.siblings,
)?;
ensure!(
range.start >= inclusion_proof.interval_inclusion_proof.leaf_index
&& range.end <= implicit_end,
Error::<T>::RangeMustNotExceedTheImplicitRange,
);
let (computed_root, implicit_address) = Self::compute_address_tree_root(
&computed_root,
&token_address,
&inclusion_proof.address_inclusion_proof.leaf_position,
&inclusion_proof.address_inclusion_proof.siblings,
)?;
ensure!(
token_address <= implicit_address,
Error::<T>::AddressMustNotExceedTheImplicitAddress,
);
return Ok(computed_root == root);
}
}
impl<T: Trait> Module<T> {
fn ensure_aggregator(sender: &T::AccountId, plapps_id: &T::AccountId) -> DispatchResult {
ensure!(
sender != &Self::aggregator_address(plapps_id),
Error::<T>::IsNotAggregator,
);
Ok(())
}
fn compute_interval_tree_root(
computed_root: &T::Hash,
computed_start: &BalanceOf<T>,
interval_tree_merkle_path: &BalanceOf<T>,
interval_tree_proof: &Vec<IntervalTreeNodeOf<T>>,
) -> DispatchResultT<(T::Hash, BalanceOf<T>)> {
let mut first_right_sibling_start = BalanceOf::<T>::max_value();
let mut is_first_right_sibling_start_set = false;
let mut ret_computed_root: T::Hash = computed_root.clone();
let mut ret_computed_start: BalanceOf<T> = computed_start.clone();
for (i, node) in interval_tree_proof.iter().enumerate() {
let sibling = &node.data;
let sibling_start = &node.start;
let is_computed_right_sibling =
interval_tree_merkle_path.clone().saturated_into::<usize>() >> i;
if is_computed_right_sibling == 1 {
ret_computed_root = Self::get_parent(
sibling,
sibling_start,
&ret_computed_root,
&ret_computed_start,
)?;
} else {
if !is_first_right_sibling_start_set {
first_right_sibling_start = sibling_start.clone();
is_first_right_sibling_start_set = true;
}
ensure!(
&first_right_sibling_start <= sibling_start,
Error::<T>::FirstRightMustBeGreaterThanSibling,
);
ret_computed_root = Self::get_parent(
&ret_computed_root,
&ret_computed_start,
sibling,
sibling_start,
)?;
ret_computed_start = sibling_start.clone();
}
}
Ok((ret_computed_root, first_right_sibling_start))
}
fn compute_address_tree_root(
computed_root: &T::Hash,
compute_address: &T::AccountId,
address_tree_merkle_path: &BalanceOf<T>,
address_tree_proof: &Vec<AddressTreeNodeOf<T>>,
) -> DispatchResultT<(T::Hash, T::AccountId)> {
let mut first_right_sibling_address = T::MaximumTokenAddress::get();
let mut is_first_right_sibling_address_set = false;
let mut ret_computed_root: T::Hash = computed_root.clone();
let mut ret_compute_address: T::AccountId = compute_address.clone();
for (i, node) in address_tree_proof.iter().enumerate() {
let sibling = &node.data;
let sibling_address = &node.token_address;
let is_computed_right_sibling =
(address_tree_merkle_path.clone().saturated_into::<usize>() >> i) & 1;
if is_computed_right_sibling == 1 {
ret_computed_root = Self::get_parent_of_address_tree_node(
sibling,
sibling_address,
&ret_computed_root,
&ret_compute_address,
);
ret_compute_address = sibling_address.clone();
} else {
if !is_first_right_sibling_address_set {
first_right_sibling_address = sibling_address.clone();
is_first_right_sibling_address_set = true;
}
ensure!(
&first_right_sibling_address <= sibling_address,
Error::<T>::FirstRightMustBeGreaterThanSibling,
);
ret_computed_root = Self::get_parent_of_address_tree_node(
&ret_computed_root,
&ret_compute_address,
sibling,
sibling_address,
);
}
}
Ok((ret_computed_root, first_right_sibling_address))
}
fn get_parent(
left: &T::Hash,
left_start: &BalanceOf<T>,
right: &T::Hash,
right_start: &BalanceOf<T>,
) -> DispatchResultT<T::Hash> {
ensure!(
right_start >= left_start,
Error::<T>::LeftMustBeLessThanRight,
);
return Ok(T::PlasmaHashing::hash_of(&(
left,
left_start,
right,
right_start,
)));
}
}
impl<T: Trait> Module<T> {
pub fn bare_extend_deposited_ranges(plapps_id: &T::AccountId, amount: BalanceOf<T>) {
let total_deposited = Self::total_deposited(plapps_id);
let old_range = Self::deposited_ranges(plapps_id, &total_deposited);
let new_start = if old_range.start == BalanceOf::<T>::zero()
&& old_range.end == BalanceOf::<T>::zero()
{
total_deposited
} else {
<DepositedRanges<T>>::remove(plapps_id, old_range.end);
old_range.start
};
let new_end = total_deposited.saturating_add(amount.clone());
let new_range = Range {
start: new_start,
end: new_end,
};
<DepositedRanges<T>>::insert(plapps_id, &new_end, new_range.clone());
<TotalDeposited<T>>::insert(plapps_id, total_deposited.saturating_add(amount));
Self::deposit_event(RawEvent::DepositedRangeExtended(
plapps_id.clone(),
new_range,
));
}
pub fn bare_remove_deposited_range(
plapps_id: &T::AccountId,
range: &RangeOf<T>,
deposited_range_id: &BalanceOf<T>,
) -> DispatchResult {
let deposited_ranges = Self::deposited_ranges(plapps_id, deposited_range_id);
ensure!(
Self::is_subrange(range, &deposited_ranges),
Error::<T>::RangeMustBeOfDepositedRange,
);
if range.start != deposited_ranges.start {
let left_split_range = Range {
start: deposited_ranges.start,
end: range.start,
};
<DepositedRanges<T>>::insert(plapps_id, left_split_range.end, left_split_range);
}
if range.end == deposited_ranges.end {
<DepositedRanges<T>>::remove(plapps_id, &deposited_ranges.end);
} else {
<DepositedRanges<T>>::insert(
plapps_id,
&deposited_ranges.end,
Range {
start: range.end,
end: deposited_ranges.end,
},
);
}
Self::deposit_event(RawEvent::DepositedRangeRemoved(
plapps_id.clone(),
range.clone(),
));
Ok(())
}
pub fn bare_finalize_exit(
plapps_id: &T::AccountId,
exit_property: &PropertyOf<T>,
deposited_range_id: &BalanceOf<T>,
) -> DispatchResultT<StateUpdateOf<T>> {
let state_update = Self::verify_exit_property(plapps_id, exit_property)?;
let exit_id = Self::get_exit_id(exit_property);
let _payout = Self::payout(plapps_id);
ensure!(
<pallet_ovm::Module<T>>::is_decided(exit_property) != Decision::True,
Error::<T>::ExitMustBeDecided,
);
ensure!(
&state_update.deposit_contract_address == plapps_id,
Error::<T>::DepositContractAddressMustBePlappsId,
);
Self::bare_remove_deposited_range(plapps_id, &state_update.range, deposited_range_id)?;
let _amount = state_update.range.end - state_update.range.start;
Self::deposit_event(RawEvent::ExitFinalized(plapps_id.clone(), exit_id));
Ok(state_update)
}
pub fn verify_exit_property(
plapps_id: &T::AccountId,
exit_property: &PropertyOf<T>,
) -> DispatchResultT<StateUpdateOf<T>> {
if exit_property.predicate_address == Self::exit_predicate(plapps_id) {
let exit: ExitOf<T> = <Deserializer<T>>::deserialize_exit(exit_property)?;
return Ok(exit.state_update);
} else if exit_property.predicate_address == Self::exit_deposit_predicate(plapps_id) {
let exit_deposit = <Deserializer<T>>::deserialize_exit_deposit(exit_property)?;
let checkpoint = exit_deposit.checkpoint;
let state_update =
<Deserializer<T>>::deserialize_state_update(&checkpoint.state_update)?;
ensure!(
Self::checkpoints(plapps_id, Self::get_checkpoint_id(&checkpoint)),
Error::<T>::CheckpointMustBeFinalized,
);
ensure!(
state_update.deposit_contract_address
== exit_deposit.state_update.deposit_contract_address,
Error::<T>::DepositContractAddressMustBeSame,
);
ensure!(
state_update.block_number == exit_deposit.state_update.block_number,
Error::<T>::BlockNumberMustBeSame,
);
ensure!(
Self::is_subrange(&exit_deposit.state_update.range, &state_update.range),
Error::<T>::RangeMustBeSubrangeOfCheckpoint,
);
return Ok(exit_deposit.state_update);
}
Err(DispatchError::Other(
"verify_exit_property not return value.",
))
}
}
impl<T: Trait> Module<T> {
fn get_parent_of_address_tree_node(
left: &T::Hash,
left_address: &T::AccountId,
right: &T::Hash,
right_address: &T::AccountId,
) -> T::Hash {
T::PlasmaHashing::hash_of(&(left, left_address, right, right_address))
}
fn get_latest_plasma_block_number(plapps_id: &T::AccountId) -> T::BlockNumber {
return Self::current_block(&plapps_id);
}
fn get_checkpoint_id(checkpoint: &CheckpointOf<T>) -> T::Hash {
<T as system::Trait>::Hashing::hash_of(checkpoint)
}
fn get_exit_id(exit: &PropertyOf<T>) -> T::Hash {
<T as system::Trait>::Hashing::hash_of(exit)
}
fn is_subrange(subrange: &RangeOf<T>, surrounding_range: &RangeOf<T>) -> bool {
subrange.start >= surrounding_range.start && subrange.end <= surrounding_range.end
}
fn generate_plapps_hash(
aggregator_id: &T::AccountId,
erc20: &T::AccountId,
state_update_predicate: &T::AccountId,
exit_predicate: &T::AccountId,
exit_deposit_predicate: &T::AccountId,
) -> T::Hash {
T::Hashing::hash_of(&(
T::Hashing::hash_of(&aggregator_id),
T::Hashing::hash_of(&erc20),
T::Hashing::hash_of(&state_update_predicate),
T::Hashing::hash_of(&exit_predicate),
T::Hashing::hash_of(&exit_deposit_predicate),
))
}
}