inital commit, which is clearly not initial

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch
2024-10-03 15:38:52 +03:00
commit 66719626bb
178 changed files with 41709 additions and 0 deletions

71
pallets/slow-clap/Cargo.toml Executable file
View File

@@ -0,0 +1,71 @@
[package]
name = "ghost-slow-clap"
version = "0.3.14"
description = "Applause protocol for the EVM bridge"
license.workspace = true
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
codec = { workspace = true, features = ["max-encoded-len"] }
scale-info = { workspace = true, features = ["derive"] }
log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["alloc"] }
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-application-crypto = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
sp-staking = { workspace = true }
sp-io = { workspace = true }
sp-std = { workspace = true }
pallet-balances = { workspace = true }
ghost-networks = { workspace = true }
[dev-dependencies]
pallet-session = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"frame-benchmarking?/std",
"log/std",
"codec/std",
"scale-info/std",
"frame-support/std",
"frame-system/std",
"sp-application-crypto/std",
"sp-core/std",
"sp-runtime/std",
"sp-staking/std",
"sp-io/std",
"sp-std/std",
"pallet-session/std",
"pallet-balances/std",
"ghost-networks/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"ghost-networks/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"sp-runtime/try-runtime",
"pallet-session/try-runtime",
"pallet-balances/try-runtime",
"ghost-networks/try-runtime",
]

View File

@@ -0,0 +1,205 @@
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::v1::*;
use frame_system::RawOrigin;
use frame_support::traits::fungible::{Inspect, Mutate};
use crate::Pallet as SlowClap;
const MAX_CLAPS: u32 = 100;
const MAX_COMPANIONS: u32 = 20;
pub fn create_account<T: Config>() -> T::AccountId {
let account_bytes = Vec::from([1u8; 32]);
T::AccountId::decode(&mut &account_bytes[0..32])
.expect("32 bytes always construct an AccountId32")
}
pub fn create_companions<T: Config>(
total: usize,
network_id: NetworkIdOf<T>,
user_account: T::AccountId,
fee: H256,
receiver: H160,
) -> Result<CompanionId, &'static str> {
T::NetworkDataHandler::register(network_id.into(), NetworkData {
chain_name: "Ethereum".into(),
default_endpoint:
"https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/".into(),
finality_delay: Some(0),
release_delay: Some(0),
network_type: Default::default(),
gatekeeper: b"0x1234567891234567891234567891234567891234".to_vec(),
topic_name: b"0x12345678912345678912345678912345678912345678912345678912345678".to_vec(),
incoming_fee: 0,
outgoing_fee: 0,
}).map_err(|_| BenchmarkError::Weightless)?;
let mut last_companion_id = 0;
for _ in 0..total {
let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
let balance = minimum_balance + minimum_balance;
let companion = Companion::<NetworkIdOf::<T>, BalanceOf::<T>> {
network_id: network_id.into(), receiver,
fee, amount: balance,
};
let _ = <<T as pallet::Config>::Currency>::mint_into(&user_account, balance);
let companion_id = SlowClap::<T>::current_companion_id();
let _ = SlowClap::<T>::propose_companion(
RawOrigin::Signed(user_account.clone()).into(),
network_id,
companion,
)?;
last_companion_id = companion_id;
}
Ok(last_companion_id)
}
pub fn create_claps<T: Config>(i: u32, j: u32) -> Result<
(
Vec<crate::Clap<T::AccountId, NetworkIdOf<T>, BalanceOf<T>>>,
<T::AuthorityId as RuntimeAppPublic>::Signature,
),
&'static str,
> {
let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
let amount = minimum_balance + minimum_balance;
let total_amount = amount.saturating_mul(j.into());
let network_id = NetworkIdOf::<T>::default();
let mut claps = Vec::new();
let mut companions = BTreeMap::new();
let authorities = vec![T::AuthorityId::generate_pair(None)];
let bounded_authorities =
WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
.map_err(|()| "more than the maximum number of keys provided")?;
Authorities::<T>::put(bounded_authorities);
for index in 0..j {
companions.insert(
index.into(),
amount,
);
}
for _ in 0..i {
claps.push(Clap {
session_index: 1,
authority_index: 0,
network_id,
transaction_hash: H256::repeat_byte(1u8),
block_number: 69,
removed: true,
receiver: create_account::<T>(),
amount: total_amount,
companions: companions.clone(),
});
}
let authority_id = authorities
.get(0usize)
.expect("first authority should exist");
let encoded_claps = claps.encode();
let signature = authority_id.sign(&encoded_claps)
.ok_or("couldn't make signature")?;
Ok((claps, signature))
}
benchmarks! {
slow_clap {
let k in 1 .. MAX_CLAPS;
let j in 1 .. MAX_COMPANIONS;
let receiver = H160::repeat_byte(69u8);
let fee = H256::repeat_byte(0u8);
let user_account: T::AccountId = whitelisted_caller();
let network_id = <<T as pallet::Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default();
let (claps, signature) = create_claps::<T>(k, j)?;
let _ = create_companions::<T>(j as usize, network_id, user_account, fee, receiver)?;
}: _(RawOrigin::None, claps, signature)
verify {
let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
let total_amount = (minimum_balance + minimum_balance).saturating_mul(j.into());
}
propose_companion {
let receiver = H160::repeat_byte(69u8);
let fee = H256::repeat_byte(0u8);
let user_account: T::AccountId = whitelisted_caller();
let network_id = <<T as pallet::Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default();
// T::NetworkDataHandler::register(network_id.into(), NetworkData {
// chain_name: "Ethereum".into(),
// // https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/
// default_endpoint:
// "https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/".into(),
// finality_delay: Some(50),
// release_delay: Some(100),
// network_type: Default::default(),
// gatekeeper: b"0x1234567891234567891234567891234567891234".to_vec(),
// topic_name: b"0x12345678912345678912345678912345678912345678912345678912345678".to_vec(),
// incoming_fee: 0,
// outgoing_fee: 0,
// }).map_err(|_| BenchmarkError::Weightless)?;
let companion_id = create_companions::<T>(1, network_id, user_account.clone(), fee, receiver)?;
let companion_id = SlowClap::<T>::current_companion_id();
let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
let balance = minimum_balance + minimum_balance;
let companion = Companion::<NetworkIdOf::<T>, BalanceOf::<T>> {
network_id: network_id.into(), receiver,
fee, amount: balance,
};
let _ = <<T as pallet::Config>::Currency>::mint_into(&user_account, balance);
assert_eq!(SlowClap::<T>::current_companion_id(), companion_id);
}: _(RawOrigin::Signed(user_account), network_id.into(), companion)
verify {
assert_eq!(SlowClap::<T>::current_companion_id(), companion_id + 1);
}
release_companion {
let receiver = H160::repeat_byte(69u8);
let fee = H256::repeat_byte(0u8);
let user_account: T::AccountId = whitelisted_caller();
let network_id = <<T as pallet::Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default();
let companion_id = create_companions::<T>(1, network_id, user_account.clone(), fee, receiver)?;
assert_eq!(SlowClap::<T>::release_blocks(companion_id), BlockNumberFor::<T>::default());
}: _(RawOrigin::Signed(user_account), network_id.into(), companion_id)
verify {
assert_ne!(SlowClap::<T>::release_blocks(companion_id), BlockNumberFor::<T>::default());
}
kill_companion {
let receiver = H160::repeat_byte(69u8);
let fee = H256::repeat_byte(0u8);
let user_account: T::AccountId = whitelisted_caller();
let network_id = <<T as pallet::Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default();
let companion_id = create_companions::<T>(1, network_id, user_account.clone(), fee, receiver)?;
SlowClap::<T>::release_companion(
RawOrigin::Signed(user_account.clone()).into(),
network_id,
companion_id,
)?;
let block_shift =
<<T as pallet::Config>::NetworkDataHandler as NetworkDataInspectHandler<NetworkData>>::get(&network_id)
.unwrap()
.release_delay
.unwrap();
frame_system::Pallet::<T>::set_block_number((block_shift + 1).saturated_into());
}: _(RawOrigin::Signed(user_account), network_id.into(), companion_id)
verify {
assert_eq!(SlowClap::<T>::companions(network_id, companion_id), None);
assert_eq!(SlowClap::<T>::companion_details(companion_id), None);
assert_eq!(SlowClap::<T>::current_companion_id(), companion_id + 1);
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::Runtime,
);
}

1334
pallets/slow-clap/src/lib.rs Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
#![cfg(test)]
use frame_support::{
derive_impl, parameter_types,
traits::{ConstU32, ConstU64},
weights::Weight,
PalletId,
};
use frame_system::EnsureRoot;
use pallet_session::historical as pallet_session_historical;
use sp_runtime::{
testing::{TestXt, UintAuthorityId},
traits::ConvertInto,
BuildStorage, Permill,
};
use sp_staking::{
offence::{OffenceError, ReportOffence},
SessionIndex,
};
use crate as slow_clap;
use crate::Config;
type Block = frame_system::mocking::MockBlock<Runtime>;
frame_support::construct_runtime!(
pub enum Runtime {
System: frame_system,
Session: pallet_session,
Historical: pallet_session_historical,
Balances: pallet_balances,
Networks: ghost_networks,
SlowClap: slow_clap,
}
);
parameter_types! {
pub static Validators: Option<Vec<u64>> = Some(vec![
1,
2,
3,
]);
}
pub struct TestSessionManager;
impl pallet_session::SessionManager<u64> for TestSessionManager {
fn new_session(_new_index: SessionIndex) -> Option<Vec<u64>> {
Validators::mutate(|l| l.take())
}
fn end_session(_: SessionIndex) {}
fn start_session(_: SessionIndex) {}
}
impl pallet_session::historical::SessionManager<u64, u64> for TestSessionManager {
fn new_session(_new_index: SessionIndex) -> Option<Vec<(u64, u64)>> {
Validators::mutate(|l| l
.take()
.map(|validators| validators
.iter()
.map(|v| (*v, *v))
.collect())
)
}
fn end_session(_: SessionIndex) {}
fn start_session(_: SessionIndex) {}
}
type IdentificationTuple = (u64, u64);
type Offence = crate::ThrottlingOffence<IdentificationTuple>;
parameter_types! {
pub static Offences: Vec<(Vec<u64>, Offence)> = vec![];
}
pub struct OffenceHandler;
impl ReportOffence<u64, IdentificationTuple, Offence> for OffenceHandler {
fn report_offence(reporters: Vec<u64>, offence: Offence) -> Result<(), OffenceError> {
Offences::mutate(|l| l.push((reporters, offence)));
Ok(())
}
fn is_known_offence(_offenders: &[IdentificationTuple], _time_slot: &SessionIndex) -> bool {
false
}
}
pub fn alice_account_id() -> <Runtime as frame_system::Config>::AccountId { 69 }
pub fn eve_account_id() -> <Runtime as frame_system::Config>::AccountId { 1337 }
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: vec![ (alice_account_id(), 100) ],
}
.assimilate_storage(&mut t)
.unwrap();
let mut result = sp_io::TestExternalities::new(t);
result.execute_with(|| {
System::set_block_number(1);
});
result
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = Block;
type AccountData = pallet_balances::AccountData<u64>;
}
parameter_types! {
pub const Period: u64 = 1;
pub const Offset: u64 = 0;
}
impl pallet_session::Config for Runtime {
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
type SessionManager =
pallet_session::historical::NoteHistoricalRoot<Runtime, TestSessionManager>;
type SessionHandler = (SlowClap,);
type ValidatorId = u64;
type ValidatorIdOf = ConvertInto;
type Keys = UintAuthorityId;
type RuntimeEvent = RuntimeEvent;
type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
type WeightInfo = ();
}
impl pallet_session::historical::Config for Runtime {
type FullIdentification = u64;
type FullIdentificationOf = ConvertInto;
}
parameter_types! {
pub static MockCurrentSessionProgress: Option<Option<Permill>> = None;
}
parameter_types! {
pub static MockAverageSessionLength: Option<u64> = None;
}
pub struct TestNextSessionRotation;
impl frame_support::traits::EstimateNextSessionRotation<u64> for TestNextSessionRotation {
fn average_session_length() -> u64 {
let mock = MockAverageSessionLength::mutate(|p| p.take());
mock.unwrap_or(pallet_session::PeriodicSessions::<Period, Offset>::average_session_length())
}
fn estimate_current_session_progress(now: u64) -> (Option<Permill>, Weight) {
let (estimate, weight) =
pallet_session::PeriodicSessions::<Period, Offset>::estimate_current_session_progress(
now,
);
let mock = MockCurrentSessionProgress::mutate(|p| p.take());
(mock.unwrap_or(estimate), weight)
}
fn estimate_next_session_rotation(now: u64) -> (Option<u64>, Weight) {
pallet_session::PeriodicSessions::<Period, Offset>::estimate_next_session_rotation(now)
}
}
impl ghost_networks::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type NetworkId = u32;
type RegisterOrigin = EnsureRoot<Self::AccountId>;
type UpdateOrigin = EnsureRoot<Self::AccountId>;
type RemoveOrigin = EnsureRoot<Self::AccountId>;
type WeightInfo = ();
}
parameter_types! {
pub static ExistentialDeposit: u64 = 2;
pub const TreasuryPalletId: PalletId = PalletId(*b"mck/test");
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Runtime {
type ExistentialDeposit = ExistentialDeposit;
type MaxReserves = ConstU32<2>;
type AccountStore = System;
type WeightInfo = ();
}
impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AuthorityId = UintAuthorityId;
type NextSessionRotation = TestNextSessionRotation;
type ValidatorSet = Historical;
type Currency = Balances;
type NetworkDataHandler = Networks;
type BlockNumberProvider = System;
type ReportUnresponsiveness = OffenceHandler;
type MaxAuthorities = ConstU32<5>;
type MaxNumberOfClaps = ConstU32<100>;
type ApplauseThreshold = ConstU32<0>;
type MaxAuthorityInfoInSession = ConstU32<5_000>;
type OffenceThreshold = ConstU32<40>;
type UnsignedPriority = ConstU64<{ 1 << 20 }>;
type TreasuryPalletId = TreasuryPalletId;
type WeightInfo = ();
}
pub type Extrinsic = TestXt<RuntimeCall, ()>;
// impl frame_system::offchain::SigningTypes for Runtime {
// type Public = <Signature as Verify>::Signer;
// type Signature = Signature;
// }
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
where
RuntimeCall: From<LocalCall>,
{
type OverarchingCall = RuntimeCall;
type Extrinsic = Extrinsic;
}
// impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Runtime
// where
// RuntimeCall: From<LocalCall>,
// {
// fn create_transaction<C: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>>(
// call: Self::OverarchingCall,
// _public: Self::Public,
// _account: Self::AccountId,
// nonce: Self::Nonce,
// ) -> Option<(RuntimeCall, <Extrinsic as ExtrinsicT>::SignaturePayload)> {
// Some((call, (nonce.into(), ())))
// }
// }
// pub fn advance_session() {
// let now = System::block_number().max(1);
// System::set_block_number(now + 1);
// Session::rotate_session();
//
// let authorities = Session::validators()
// .into_iter()
// .map(UintAuthorityId)
// .collect();
//
// SlowClap::set_authorities(authorities);
// assert_eq!(Session::current_index(), (now / Period::get()) as u32);
// }

View File

@@ -0,0 +1,696 @@
#![cfg(test)]
use super::*;
use crate::mock::*;
use frame_support::{assert_err, assert_ok};
use sp_core::offchain::{
testing,
testing::TestOffchainExt, OffchainWorkerExt,
// OffchainDbExt, TransactionPoolExt,
};
// use sp_runtime::testing::UintAuthorityId;
fn prepare_networks() -> (u32, u32) {
let network = NetworkData {
chain_name: "Ethereum".into(),
default_endpoint: get_rpc_endpoint(),
finality_delay: Some(69),
release_delay: Some(69),
network_type: ghost_networks::NetworkType::Evm,
gatekeeper: get_gatekeeper(),
topic_name: get_topic_name(),
incoming_fee: 0,
outgoing_fee: 0,
};
assert_ok!(Networks::register_network(
RuntimeOrigin::root(),
get_network_id(),
network));
(1, 69)
}
fn prepare_companion(amount: u64) -> Companion<NetworkIdOf<Runtime>, BalanceOf<Runtime>> {
Companion {
network_id: 1,
receiver: H160::from_low_u64_ne(1337),
fee: H256::from_low_u64_ne(40_000),
amount: amount,
}
}
#[test]
fn test_throttling_slash_function() {
let dummy_offence = ThrottlingOffence {
session_index: 0,
validator_set_count: 50,
offenders: vec![()],
};
assert_eq!(dummy_offence.slash_fraction(1), Perbill::zero());
assert_eq!(dummy_offence.slash_fraction(5), Perbill::zero());
assert_eq!(dummy_offence.slash_fraction(7), Perbill::from_parts(4200000));
assert_eq!(dummy_offence.slash_fraction(17), Perbill::from_parts(46200000));
}
#[test]
fn propose_companion_works_as_expected() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(100);
assert_eq!(Balances::balance(&actor), 100);
assert_eq!(Balances::total_issuance(), 100);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), None);
assert_eq!(SlowClap::companion_details(companion_id), None);
assert_eq!(SlowClap::current_companion_id(), companion_id);
assert_ok!(SlowClap::propose_companion(RuntimeOrigin::signed(actor), valid_network_id, companion.clone()));
assert_eq!(Balances::balance(&actor), 0);
assert_eq!(Balances::total_issuance(), 0);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion));
assert_eq!(SlowClap::current_companion_id(), companion_id + 1);
});
}
#[test]
fn propose_companion_emits_event() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(100);
System::reset_events();
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
System::assert_has_event(RuntimeEvent::SlowClap(
crate::Event::CompanionCreated {
companion_id,
owner: actor,
companion,
}));
});
}
#[test]
fn could_not_propose_if_not_enough_funds() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(1_000);
assert_err!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()),
sp_runtime::TokenError::FundsUnavailable);
let companion = prepare_companion(100 / 2);
let prev_balance = Balances::balance(&actor);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_eq!(Balances::balance(&actor), prev_balance / 2);
});
}
#[test]
fn companion_amount_should_be_existent() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(1);
assert_err!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()),
crate::Error::<Runtime>::CompanionAmountNotExistent);
});
}
#[test]
fn could_not_register_companion_if_network_not_registered() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let (_, invalid_network_id) = prepare_networks();
let companion = prepare_companion(100);
assert_err!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
invalid_network_id,
companion.clone()),
crate::Error::<Runtime>::NonRegisteredNetwork);
});
}
#[test]
fn release_companion_works() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id,
companion.clone()));
assert_eq!(SlowClap::release_blocks(companion_id), 0);
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
assert_eq!(SlowClap::release_blocks(companion_id), 69 + 1);
});
}
#[test]
fn release_companion_emits_event() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
System::reset_events();
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
System::assert_has_event(RuntimeEvent::SlowClap(
crate::Event::CompanionReleased {
companion_id,
network_id: valid_network_id,
who: actor,
release_block: 69 + 1 }));
});
}
#[test]
fn could_not_release_from_random_account() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_err!(SlowClap::release_companion(
RuntimeOrigin::signed(eve_account_id()),
valid_network_id, companion_id),
crate::Error::<Runtime>::NotValidCompanion);
});
}
#[test]
fn kill_companion_works() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
let release_block = ReleaseBlocks::<Runtime>::get(companion_id);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion));
assert_eq!(Balances::balance(&actor), 50);
assert_eq!(Balances::total_issuance(), 50);
System::set_block_number(release_block + 1);
assert_ok!(SlowClap::kill_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
assert_eq!(SlowClap::companions(valid_network_id, companion_id), None);
assert_eq!(SlowClap::companion_details(companion_id), None);
assert_eq!(Balances::balance(&actor), 100);
assert_eq!(Balances::total_issuance(), 100);
});
}
#[test]
fn kill_companion_emits_event() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
System::set_block_number(
ReleaseBlocks::<Runtime>::get(companion_id) + 1);
System::reset_events();
assert_ok!(SlowClap::kill_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
System::assert_has_event(RuntimeEvent::SlowClap(
crate::Event::CompanionKilled {
network_id: valid_network_id,
who: actor,
companion_id,
freed_balance: 50,
}));
});
}
#[test]
fn could_not_kill_companion_that_was_not_released() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
let release_block = ReleaseBlocks::<Runtime>::get(companion_id);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone()));
assert_eq!(Balances::balance(&actor), 50);
assert_eq!(Balances::total_issuance(), 50);
System::set_block_number(release_block / 2);
assert_err!(SlowClap::kill_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id),
crate::Error::<Runtime>::ReleaseTimeHasNotCome);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion));
assert_eq!(Balances::balance(&actor), 50);
assert_eq!(Balances::total_issuance(), 50);
});
}
#[test]
fn could_not_kill_companion_from_random_account() {
new_test_ext().execute_with(|| {
let actor = alice_account_id();
let companion_id = SlowClap::current_companion_id();
let (valid_network_id, _) = prepare_networks();
let companion = prepare_companion(50);
assert_ok!(SlowClap::propose_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion.clone()));
assert_ok!(SlowClap::release_companion(
RuntimeOrigin::signed(actor),
valid_network_id, companion_id));
let release_block = ReleaseBlocks::<Runtime>::get(companion_id);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone()));
assert_eq!(Balances::balance(&actor), 50);
assert_eq!(Balances::total_issuance(), 50);
System::set_block_number(release_block + 1);
assert_err!(SlowClap::kill_companion(
RuntimeOrigin::signed(eve_account_id()),
valid_network_id, companion_id),
crate::Error::<Runtime>::NotValidCompanion);
assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor));
assert_eq!(SlowClap::companion_details(companion_id), Some(companion));
assert_eq!(Balances::balance(&actor), 50);
assert_eq!(Balances::total_issuance(), 50);
});
}
#[test]
fn request_body_is_correct_for_get_block_number() {
let (offchain, _) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let request_body = SlowClap::prepare_request_body(
None, 0, Vec::new(), Vec::new());
assert_eq!(core::str::from_utf8(&request_body).unwrap(),
r#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#);
});
}
#[test]
fn request_body_is_correct_for_get_logs() {
let (offchain, _) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let request_body = SlowClap::prepare_request_body(
Some(1337), 69, get_gatekeeper(), get_topic_name());
assert_eq!(
core::str::from_utf8(&request_body).unwrap(),
r#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":1268,"toBlock":1337,"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#,
);
});
}
#[test]
fn should_make_http_call_for_block_number() {
let (offchain, state) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
evm_block_response(&mut state.write());
t.execute_with(|| {
let request_body = SlowClap::prepare_request_body(
None, 0, Vec::new(), Vec::new());
let raw_response = SlowClap::fetch_from_remote(
get_rpc_endpoint(), request_body).unwrap();
assert_eq!(raw_response.len(), 45usize); // precalculated
});
}
#[test]
fn should_make_http_call_for_logs() {
let (offchain, state) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
evm_logs_response(&mut state.write());
t.execute_with(|| {
let request_body = SlowClap::prepare_request_body(
Some(20335857), 84, get_gatekeeper(), get_topic_name());
let raw_response = SlowClap::fetch_from_remote(
get_rpc_endpoint(), request_body).unwrap();
assert_eq!(raw_response.len(), 1805); // precalculated
});
}
#[test]
fn should_make_http_call_and_parse_block_number() {
let (offchain, state) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
evm_block_response(&mut state.write());
t.execute_with(|| {
let evm_response = SlowClap::fetch_and_parse(
None, 0, Vec::<u8>::new(),
Vec::<u8>::new(), get_rpc_endpoint()).unwrap();
let evm_block_number = match evm_response {
EvmResponseType::BlockNumber(evm_block) => Some(evm_block),
_ => None,
};
assert_eq!(evm_block_number.unwrap_or_default(), 20335745);
});
}
#[test]
fn should_make_http_call_and_parse_logs() {
let (offchain, state) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
evm_logs_response(&mut state.write());
let expected_logs = get_expected_logs();
t.execute_with(|| {
let evm_response = SlowClap::fetch_and_parse(
Some(20335857), 84, get_gatekeeper(),
get_topic_name(), get_rpc_endpoint()
).unwrap();
let evm_logs = match evm_response {
EvmResponseType::TransactionLogs(logs) => Some(logs),
_ => None,
};
assert!(evm_logs.is_some());
let evm_logs = evm_logs.unwrap();
assert_eq!(evm_logs, expected_logs);
});
}
#[test]
fn should_catch_error_in_json_response() {
let (offchain, state) = TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
evm_error_response(&mut state.write());
t.execute_with(|| {
assert_err!(SlowClap::fetch_and_parse(
None, 0, Vec::<u8>::new(),
Vec::<u8>::new(), get_rpc_endpoint()),
OffchainErr::ErrorInEvmResponse);
});
}
// #[test]
// fn conversion_to_claps_is_correct() {
// todo!();
// }
//
// #[test]
// fn evm_block_storage_is_empty_by_default() {
// todo!();
// }
//
// #[test]
// fn evm_block_is_stored_locally_after_first_response() {
// todo!();
// }
//
// #[test]
// fn evm_block_storage_is_none_after_logs() {
// todo!();
// }
//
// #[test]
// fn send_evm_usigned_transaction_from_authority() {
// todo!();
// }
// #[test]
// fn should_report_offline_validators() {
// new_test_ext().execute_with(|| {
// let block = 1;
// System::set_block_number(block);
// advance_session();
// let validators = vec![1, 2, 3, 4, 5, 6];
// Validators::mutate(|l| *l = Some(validators.clone()));
// advance_session();
//
// advance_session();
//
// let offences = Offences::take();
// assert_eq!(
// offences,
// vec![(
// vec![],
// ThrottlingOffence {
// session_index: 2,
// validator_set_count: 3,
// offenders: vec![(1, 1), (2, 2), (3, 3)],
// }
// )]
// );
//
// for (idx, v) in validators.into_iter().take(4).enumerate() {
// let _ = clap();
// }
//
// advance_session();
//
// let offences = Offences::take();
// assert_eq!(
// offences,
// vec![(
// vec![],
// ThrottlingOffence {
// session_index: 3,
// validator_set_count: 6,
// offenders: vec![(5, 5), (6, 6)],
// }
// )]
// );
// });
// }
//
// #[test]
// fn should_increase_actions_of_validators_when_clap_is_received() {
// }
//
// #[test]
// fn same_claps_should_not_increase_actions() {
// }
//
// #[test]
// fn should_cleanup_received_claps_on_session_end() {
// }
//
// #[test]
// fn should_mark_validator_if_disabled() {
// }
fn get_rpc_endpoint() -> Vec<u8> {
b"https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".to_vec()
}
fn get_gatekeeper() -> Vec<u8> {
b"0x4d224452801ACEd8B2F0aebE155379bb5D594381".to_vec()
}
fn get_topic_name() -> Vec<u8> {
b"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".to_vec()
}
fn get_network_id() -> u32 {
// let mut network_id: NetworkIdOf<Runtime> = Default::default();
// network_id.saturating_inc();
// network_id
1u32
}
fn evm_block_response(state: &mut testing::OffchainState) {
let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#.to_vec();
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(),
headers: vec![
("ACCEPT".to_string(), "APPLICATION/JSON".to_string()),
("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()),
],
response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"result\":\"0x1364c81\"}".to_vec()),
body: expected_body,
sent: true,
..Default::default()
});
}
fn evm_error_response(state: &mut testing::OffchainState) {
let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#.to_vec();
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(),
headers: vec![
("ACCEPT".to_string(), "APPLICATION/JSON".to_string()),
("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()),
],
response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"error\":\"some bad error occures :-(\"}".to_vec()),
body: expected_body,
sent: true,
..Default::default()
});
}
fn evm_logs_response(state: &mut testing::OffchainState) {
let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":20335773,"toBlock":20335857,"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#.to_vec();
let expected_response = br#"{
"jsonrpc":"2.0",
"id":0,
"result":[
{
"address":"0x4d224452801aced8b2f0aebe155379bb5d594381",
"topics":[
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d91efec7e42f80156d1d9f660a69847188950747",
"0x0000000000000000000000005e611dfbe71b988cf2a3e5fe4191b5e3d4c4212a"
],
"data":"0x000000000000000000000000000000000000000000000046f0d522a71fdac000",
"blockNumber":"0x1364c9d",
"transactionHash":"0xa82f2fe87f4ba01ab3bd2cd4d0fe75a26f0e9a37e2badc004a0e38f9d088a758",
"transactionIndex":"0x11",
"blockHash":"0x4b08f82d5a948c0bc5c23c8ddce55e5b4536d7454be53668bbd985ef13dca04a",
"logIndex":"0x92",
"removed":false
},
{
"address":"0x4d224452801aced8b2f0aebe155379bb5d594381",
"topics":[
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000005954ab967bc958940b7eb73ee84797dc8a2afbb9",
"0x000000000000000000000000465165be7cacdbd2cbc8334f549fab9783ad6e7a"
],
"data":"0x0000000000000000000000000000000000000000000000027c790defec959e75",
"blockNumber":"0x1364cf1",
"transactionHash":"0xd9f02b79a90db7536b0afb5e24bbc1f4319dc3d8c57af7c39941acd249ec053a",
"transactionIndex":"0x2c",
"blockHash":"0x6876b18f5081b5d24d5a73107a667a7713256f6e51edbbe52bf8df24e340b0f7",
"logIndex":"0x14b",
"removed":false
}
]
}"#.to_vec();
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(),
headers: vec![
("ACCEPT".to_string(), "APPLICATION/JSON".to_string()),
("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()),
],
body: expected_body,
response: Some(expected_response),
sent: true,
..Default::default()
});
}
fn get_expected_logs() -> Vec<Log> {
let byte_converter = |s: &str| -> Vec<u8> {
(2..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i+2], 16).expect("valid u8 symbol; qed"))
.collect()
};
vec![
Log {
transaction_hash: Some(H256::from_slice(&byte_converter("0xa82f2fe87f4ba01ab3bd2cd4d0fe75a26f0e9a37e2badc004a0e38f9d088a758"))),
block_number: Some(20335773),
topics: vec![
byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
byte_converter("0x000000000000000000000000d91efec7e42f80156d1d9f660a69847188950747"),
byte_converter("0x0000000000000000000000005e611dfbe71b988cf2a3e5fe4191b5e3d4c4212a"),
],
address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()),
data: sp_std::collections::btree_map::BTreeMap::from([(0, 1308625900000000000000)]),
removed: false,
},
Log {
transaction_hash: Some(H256::from_slice(&byte_converter("0xd9f02b79a90db7536b0afb5e24bbc1f4319dc3d8c57af7c39941acd249ec053a"))),
block_number: Some(20335857),
topics: vec![
byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
byte_converter("0x0000000000000000000000005954ab967bc958940b7eb73ee84797dc8a2afbb9"),
byte_converter("0x000000000000000000000000465165be7cacdbd2cbc8334f549fab9783ad6e7a"),
],
address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()),
data: sp_std::collections::btree_map::BTreeMap::from([(0, 45862703604421729909)]),
removed: false,
},
]
}

View File

@@ -0,0 +1,18 @@
use frame_support::weights::Weight;
pub trait WeightInfo {
fn slow_clap(claps_len: u32, companions_len: u32) -> Weight;
fn propose_companion() -> Weight;
fn release_companion() -> Weight;
fn kill_companion() -> Weight;
}
impl WeightInfo for () {
fn slow_clap(
_claps_len: u32,
_companions_len: u32,
) -> Weight { Weight::zero() }
fn propose_companion() -> Weight { Weight::zero() }
fn release_companion() -> Weight { Weight::zero() }
fn kill_companion() -> Weight { Weight::zero() }
}