add support for partial claims of governance shares
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
parent
fde1f68dc0
commit
d3b926cd47
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-governance"
|
name = "ghost-governance"
|
||||||
version = "0.3.7"
|
version = "0.3.8"
|
||||||
description = "Full-chain and cross-chain governance pallet with early adopter share claims"
|
description = "Full-chain and cross-chain governance pallet with early adopter share claims"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
@ -13,6 +13,7 @@ codec = { workspace = true, features = ["derive"] }
|
|||||||
scale-info = { workspace = true, features = ["derive"] }
|
scale-info = { workspace = true, features = ["derive"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_derive = { workspace = true }
|
serde_derive = { workspace = true }
|
||||||
|
num-traits = { workspace = true }
|
||||||
rustc-hex = { workspace = true }
|
rustc-hex = { workspace = true }
|
||||||
libsecp256k1 = { workspace = true, default-features = false }
|
libsecp256k1 = { workspace = true, default-features = false }
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ std = [
|
|||||||
"serde/std",
|
"serde/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
|
"num-traits/std",
|
||||||
"libsecp256k1/std",
|
"libsecp256k1/std",
|
||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
|
|||||||
@ -99,7 +99,8 @@ mod benchmarks {
|
|||||||
let message_string_bytes = Pallet::<T>::ethereum_signable_message(
|
let message_string_bytes = Pallet::<T>::ethereum_signable_message(
|
||||||
¤t_account,
|
¤t_account,
|
||||||
&network_id,
|
&network_id,
|
||||||
&preimage_hash
|
&preimage_hash,
|
||||||
|
0u8,
|
||||||
);
|
);
|
||||||
|
|
||||||
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
|
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
|
||||||
@ -124,6 +125,7 @@ mod benchmarks {
|
|||||||
claim_package,
|
claim_package,
|
||||||
dummy_evm_address,
|
dummy_evm_address,
|
||||||
evm_signature,
|
evm_signature,
|
||||||
|
dummy_shares,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(dummy_shares, GlobalShares::<T>::get().claimed_shares);
|
assert_eq!(dummy_shares, GlobalShares::<T>::get().claimed_shares);
|
||||||
|
|||||||
@ -9,7 +9,11 @@ use frame_system::pallet_prelude::*;
|
|||||||
use sp_core::U256;
|
use sp_core::U256;
|
||||||
use sp_std::vec::Vec;
|
use sp_std::vec::Vec;
|
||||||
use sp_io::crypto::secp256k1_ecdsa_recover;
|
use sp_io::crypto::secp256k1_ecdsa_recover;
|
||||||
use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedAdd, UniqueSaturatedInto};
|
use sp_runtime::traits::{
|
||||||
|
AtLeast32BitUnsigned, CheckedAdd, Saturating, UniqueSaturatedInto,
|
||||||
|
};
|
||||||
|
|
||||||
|
use num_traits::PrimInt;
|
||||||
|
|
||||||
use ghost_helpers::{
|
use ghost_helpers::{
|
||||||
SubstrateKeccakHasher, merkle_tree::verify_tree_proof,
|
SubstrateKeccakHasher, merkle_tree::verify_tree_proof,
|
||||||
@ -32,9 +36,10 @@ mod benchmarking;
|
|||||||
mod mock;
|
mod mock;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
const MAX_POSSIBLE_PROOF_DEPTH: u32 = 256;
|
const MAX_POSSIBLE_PROOF_DEPTH: u32 = 18;
|
||||||
|
|
||||||
type TokenId = u128;
|
type TokenId = u128;
|
||||||
|
type ClaimNonce = u8;
|
||||||
type EvmHash = sp_core::H256;
|
type EvmHash = sp_core::H256;
|
||||||
type EvmAddress = sp_core::H160;
|
type EvmAddress = sp_core::H160;
|
||||||
type EvmSignature = [u8; 65];
|
type EvmSignature = [u8; 65];
|
||||||
@ -134,7 +139,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock_balance(&mut self, extra_balance: Balance) {
|
pub fn lock_balance(&mut self, extra_balance: Balance) {
|
||||||
self.locked_balance.saturating_add(extra_balance);
|
self.locked_balance = self.locked_balance
|
||||||
|
.saturating_add(extra_balance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +220,9 @@ pub mod pallet {
|
|||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type MaxProofDepth: Get<u32>;
|
type MaxProofDepth: Get<u32>;
|
||||||
|
|
||||||
|
#[pallet::constant]
|
||||||
|
type MaxPartialClaims: Get<ClaimNonce>;
|
||||||
|
|
||||||
type WeightInfo: WeightInfo;
|
type WeightInfo: WeightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,9 +238,10 @@ pub mod pallet {
|
|||||||
|
|
||||||
#[pallet::error]
|
#[pallet::error]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
PreimageAlreadyClaimed,
|
PartialClaimsExhausted,
|
||||||
BoundedToOtherNetwork,
|
BoundedToOtherNetwork,
|
||||||
AuthorityNotGovernor,
|
AuthorityNotGovernor,
|
||||||
|
ExceedsAllowedShares,
|
||||||
NetworkHasNoGenesis,
|
NetworkHasNoGenesis,
|
||||||
InvalidMerkleProof,
|
InvalidMerkleProof,
|
||||||
SharesOverflowed,
|
SharesOverflowed,
|
||||||
@ -269,7 +279,16 @@ pub mod pallet {
|
|||||||
pub type ClaimedHashes<T: Config> = StorageMap<
|
pub type ClaimedHashes<T: Config> = StorageMap<
|
||||||
_,
|
_,
|
||||||
Blake2_256, EvmHash,
|
Blake2_256, EvmHash,
|
||||||
bool,
|
ClaimNonce,
|
||||||
|
ValueQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
#[pallet::getter(fn claimed_shares)]
|
||||||
|
pub type ClaimedShares<T: Config> = StorageMap<
|
||||||
|
_,
|
||||||
|
Twox64Concat, EvmHash,
|
||||||
|
BalanceOf<T>,
|
||||||
ValueQuery,
|
ValueQuery,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@ -317,6 +336,7 @@ pub mod pallet {
|
|||||||
claim_package: ClaimPackage<BalanceOf<T>>,
|
claim_package: ClaimPackage<BalanceOf<T>>,
|
||||||
evm_address: EvmAddress,
|
evm_address: EvmAddress,
|
||||||
evm_signature: EvmSignature,
|
evm_signature: EvmSignature,
|
||||||
|
requested_shares: BalanceOf<T>,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let who = ensure_signed(origin)?;
|
let who = ensure_signed(origin)?;
|
||||||
|
|
||||||
@ -338,12 +358,21 @@ pub mod pallet {
|
|||||||
let preimage = claim_package.get_preimage(&evm_address, network_id);
|
let preimage = claim_package.get_preimage(&evm_address, network_id);
|
||||||
let preimage_hash = SubstrateKeccakHasher::hash(&preimage);
|
let preimage_hash = SubstrateKeccakHasher::hash(&preimage);
|
||||||
|
|
||||||
ensure!(
|
let max_claim_nonces = T::MaxPartialClaims::get();
|
||||||
!ClaimedHashes::<T>::contains_key(&preimage_hash),
|
let current_nonce = ClaimedHashes::<T>::get(&preimage_hash);
|
||||||
Error::<T>::PreimageAlreadyClaimed
|
|
||||||
);
|
|
||||||
|
|
||||||
Self::verify_signature(&who, &network_id, &preimage_hash, &evm_address, &evm_signature)?;
|
ensure!(current_nonce < max_claim_nonces, Error::<T>::PartialClaimsExhausted);
|
||||||
|
|
||||||
|
let already_claimed = ClaimedShares::<T>::get(&preimage_hash);
|
||||||
|
let claimed_shares = if current_nonce == max_claim_nonces.saturating_sub(1) {
|
||||||
|
claim_package.shares.saturating_sub(already_claimed)
|
||||||
|
} else {
|
||||||
|
let cumulative_shares = already_claimed.saturating_add(requested_shares);
|
||||||
|
ensure!(cumulative_shares <= claim_package.shares, Error::<T>::ExceedsAllowedShares);
|
||||||
|
requested_shares
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::verify_signature(&who, &network_id, &preimage_hash, &evm_address, &evm_signature, current_nonce)?;
|
||||||
Self::verify_merkle_proof(&network_id, &preimage, &claim_package)?;
|
Self::verify_merkle_proof(&network_id, &preimage, &claim_package)?;
|
||||||
|
|
||||||
NetworkShares::<T>::try_mutate(&network_id, |state| -> DispatchResult {
|
NetworkShares::<T>::try_mutate(&network_id, |state| -> DispatchResult {
|
||||||
@ -351,25 +380,29 @@ pub mod pallet {
|
|||||||
let proof_size_match = package_proof_size == state.proofs_size;
|
let proof_size_match = package_proof_size == state.proofs_size;
|
||||||
ensure!(proof_size_match, Error::<T>::SharesOverflowed);
|
ensure!(proof_size_match, Error::<T>::SharesOverflowed);
|
||||||
|
|
||||||
let increased = state.try_increase_claimed(claim_package.shares);
|
let increased = state.try_increase_claimed(claimed_shares);
|
||||||
ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
|
ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
GlobalShares::<T>::try_mutate(|state| -> DispatchResult {
|
GlobalShares::<T>::try_mutate(|state| -> DispatchResult {
|
||||||
let increased = state.try_increase_claimed(claim_package.shares);
|
let increased = state.try_increase_claimed(claimed_shares);
|
||||||
ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
|
ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
member_share.increase_activated_shares(claim_package.shares);
|
member_share.increase_activated_shares(claimed_shares);
|
||||||
member_share.initiate_on_network(network_id);
|
member_share.initiate_on_network(network_id);
|
||||||
|
|
||||||
MemberShares::<T>::insert(&who, member_share);
|
MemberShares::<T>::insert(&who, member_share);
|
||||||
ClaimedHashes::<T>::insert(&preimage_hash, true);
|
ClaimedHashes::<T>::insert(&preimage_hash, current_nonce.saturating_add(1));
|
||||||
|
ClaimedShares::<T>::insert(
|
||||||
|
&preimage_hash,
|
||||||
|
already_claimed.saturating_add(claimed_shares),
|
||||||
|
);
|
||||||
|
|
||||||
Self::deposit_event(Event::<T>::SharesClaimed {
|
Self::deposit_event(Event::<T>::SharesClaimed {
|
||||||
shares: claim_package.shares,
|
shares: claimed_shares,
|
||||||
network_id,
|
network_id,
|
||||||
who,
|
who,
|
||||||
});
|
});
|
||||||
@ -390,45 +423,73 @@ impl<T: Config> Pallet<T> {
|
|||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_ascii_base10<N: PrimInt + UniqueSaturatedInto<u128>>(num: N) -> Vec<u8> {
|
||||||
|
let zero = N::zero();
|
||||||
|
if num == zero { return vec![b'0']; }
|
||||||
|
|
||||||
|
let ten = N::from(10).unwrap_or_else(|| N::zero());
|
||||||
|
let num_u128 = num.unique_saturated_into();
|
||||||
|
let capacity = (num_u128.ilog10() + 1) as usize;
|
||||||
|
|
||||||
|
let mut result = Vec::with_capacity(capacity);
|
||||||
|
let mut n = num;
|
||||||
|
|
||||||
|
while n > zero {
|
||||||
|
result.push(b'0' + (n % ten).to_u8().unwrap_or(0));
|
||||||
|
n = n / ten;
|
||||||
|
}
|
||||||
|
result.reverse();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn ethereum_signable_message(
|
fn ethereum_signable_message(
|
||||||
receiver: &T::AccountId,
|
receiver: &T::AccountId,
|
||||||
network_id: &NetworkIdOf<T>,
|
network_id: &NetworkIdOf<T>,
|
||||||
preimage_hash: &EvmHash,
|
preimage_hash: &EvmHash,
|
||||||
|
nonce: ClaimNonce,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
let prefix = b"GMV Claim:";
|
let prefix = b"GMV Claim:";
|
||||||
let network_id_u64: u64 = (*network_id).unique_saturated_into();
|
let network_id_u64: u64 = (*network_id).unique_saturated_into();
|
||||||
|
|
||||||
let receiver_ascii_hex = Self::to_ascii_hex(&receiver.encode());
|
let receiver_ascii_hex = Self::to_ascii_hex(&receiver.encode());
|
||||||
let preimage_ascii_hex = Self::to_ascii_hex(preimage_hash.as_ref());
|
let preimage_ascii_hex = Self::to_ascii_hex(preimage_hash.as_ref());
|
||||||
let network_ascii_hex = Self::to_ascii_hex(&network_id_u64.to_be_bytes());
|
let network_ascii = Self::to_ascii_base10(network_id_u64);
|
||||||
|
let nonce_ascii = Self::to_ascii_base10(nonce);
|
||||||
|
|
||||||
let what_length = receiver_ascii_hex.len()
|
let account_label = b"\naccount:0x";
|
||||||
.saturating_add(preimage_ascii_hex.len())
|
let preimage_label = b"\npreimage:0x";
|
||||||
.saturating_add(network_ascii_hex.len())
|
let network_label = b"\nnetwork:";
|
||||||
.saturating_add(10 + 11 + 12 + 11); // all 4 prefixes
|
let nonce_label = b"\nnonce:";
|
||||||
|
|
||||||
|
let what_length = prefix.len()
|
||||||
|
.saturating_add(account_label.len()).saturating_add(receiver_ascii_hex.len())
|
||||||
|
.saturating_add(preimage_label.len()).saturating_add(preimage_ascii_hex.len())
|
||||||
|
.saturating_add(network_label.len()).saturating_add(network_ascii.len())
|
||||||
|
.saturating_add(nonce_label.len()).saturating_add(nonce_ascii.len());
|
||||||
|
|
||||||
let mut what = Vec::with_capacity(what_length);
|
let mut what = Vec::with_capacity(what_length);
|
||||||
what.extend_from_slice(prefix);
|
what.extend_from_slice(prefix);
|
||||||
|
|
||||||
what.extend_from_slice(b"\naccount:0x");
|
what.extend_from_slice(account_label);
|
||||||
what.extend_from_slice(&receiver_ascii_hex);
|
what.extend_from_slice(&receiver_ascii_hex);
|
||||||
|
|
||||||
what.extend_from_slice(b"\npreimage:0x");
|
what.extend_from_slice(preimage_label);
|
||||||
what.extend_from_slice(&preimage_ascii_hex);
|
what.extend_from_slice(&preimage_ascii_hex);
|
||||||
|
|
||||||
what.extend_from_slice(b"\nnetwork:0x");
|
what.extend_from_slice(network_label);
|
||||||
what.extend_from_slice(&network_ascii_hex);
|
what.extend_from_slice(&network_ascii);
|
||||||
|
|
||||||
let mut l = what.len();
|
what.extend_from_slice(nonce_label);
|
||||||
let mut rev = Vec::new();
|
what.extend_from_slice(&nonce_ascii);
|
||||||
while l > 0 {
|
|
||||||
rev.push(b'0' + (l % 10) as u8);
|
|
||||||
l /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(26 + rev.len() + what.len());
|
let length_ascii = Self::to_ascii_base10(what.len());
|
||||||
v.extend_from_slice(b"\x19Ethereum Signed Message:\n");
|
let message_prefix = b"\x19Ethereum Signed Message:\n";
|
||||||
v.extend(rev.into_iter().rev());
|
|
||||||
|
let message_capacity = message_prefix.len() + length_ascii.len() + what.len();
|
||||||
|
let mut v = Vec::with_capacity(message_capacity);
|
||||||
|
v.extend_from_slice(message_prefix);
|
||||||
|
v.extend_from_slice(&length_ascii);
|
||||||
v.extend(what);
|
v.extend(what);
|
||||||
|
|
||||||
v
|
v
|
||||||
@ -440,8 +501,9 @@ impl<T: Config> Pallet<T> {
|
|||||||
preimage_hash: &EvmHash,
|
preimage_hash: &EvmHash,
|
||||||
evm_address: &EvmAddress,
|
evm_address: &EvmAddress,
|
||||||
evm_signature: &EvmSignature,
|
evm_signature: &EvmSignature,
|
||||||
|
nonce: ClaimNonce,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let message = Self::ethereum_signable_message(who, network_id, preimage_hash);
|
let message = Self::ethereum_signable_message(who, network_id, preimage_hash, nonce);
|
||||||
let message_bytes = SubstrateKeccakHasher::hash(&message).to_fixed_bytes();
|
let message_bytes = SubstrateKeccakHasher::hash(&message).to_fixed_bytes();
|
||||||
|
|
||||||
let recovered_pubkey = secp256k1_ecdsa_recover(evm_signature, &message_bytes)
|
let recovered_pubkey = secp256k1_ecdsa_recover(evm_signature, &message_bytes)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use super::*;
|
|||||||
pub use crate as ghost_governance;
|
pub use crate as ghost_governance;
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
derive_impl, parameter_types,
|
derive_impl, parameter_types,
|
||||||
traits::{ConstU32, ConstU128},
|
traits::{ConstU8, ConstU32, ConstU128},
|
||||||
};
|
};
|
||||||
use frame_system::EnsureRoot;
|
use frame_system::EnsureRoot;
|
||||||
use sp_runtime::BuildStorage;
|
use sp_runtime::BuildStorage;
|
||||||
@ -53,6 +53,7 @@ impl Config for Test {
|
|||||||
|
|
||||||
type MinimumDonation = ConstU128<69>;
|
type MinimumDonation = ConstU128<69>;
|
||||||
type MaxProofDepth = ConstU32<5>;
|
type MaxProofDepth = ConstU32<5>;
|
||||||
|
type MaxPartialClaims = ConstU8<3>;
|
||||||
|
|
||||||
type WeightInfo = ();
|
type WeightInfo = ();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use hex_literal::hex;
|
|||||||
|
|
||||||
use sp_runtime::AccountId32;
|
use sp_runtime::AccountId32;
|
||||||
use frame_support::traits::Get;
|
use frame_support::traits::Get;
|
||||||
use frame_support::assert_ok;
|
use frame_support::{assert_ok, assert_err};
|
||||||
use libsecp256k1::{Message, SecretKey, PublicKey};
|
use libsecp256k1::{Message, SecretKey, PublicKey};
|
||||||
|
|
||||||
use ghost_helpers::merkle_tree::{generate_tree, generate_proof};
|
use ghost_helpers::merkle_tree::{generate_tree, generate_proof};
|
||||||
@ -131,7 +131,8 @@ fn claim_happy_path() {
|
|||||||
let message_string_bytes = Governance::ethereum_signable_message(
|
let message_string_bytes = Governance::ethereum_signable_message(
|
||||||
¤t_user.substrate_account,
|
¤t_user.substrate_account,
|
||||||
&network_id,
|
&network_id,
|
||||||
&preimage_hash
|
&preimage_hash,
|
||||||
|
0u8,
|
||||||
);
|
);
|
||||||
|
|
||||||
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
|
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
|
||||||
@ -154,7 +155,8 @@ fn claim_happy_path() {
|
|||||||
network_id,
|
network_id,
|
||||||
claim_package,
|
claim_package,
|
||||||
current_user.evm_address.clone(),
|
current_user.evm_address.clone(),
|
||||||
evm_signature
|
evm_signature,
|
||||||
|
current_user.allowed_shares,
|
||||||
));
|
));
|
||||||
|
|
||||||
let updated_share = MemberShares::<Test>::get(¤t_user.substrate_account);
|
let updated_share = MemberShares::<Test>::get(¤t_user.substrate_account);
|
||||||
@ -172,6 +174,201 @@ fn claim_happy_path() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_claims_happy_path() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let network_id = 69u64;
|
||||||
|
|
||||||
|
let minimum_donation_u128 = <<Test as crate::Config>::MinimumDonation as Get<u128>>::get();
|
||||||
|
let minimum_donation: BalanceOf<Test> = minimum_donation_u128.unique_saturated_into();
|
||||||
|
|
||||||
|
let secret_key = SecretKey::parse(&[7u8; 32]).unwrap();
|
||||||
|
let public_key = PublicKey::from_secret_key(&secret_key);
|
||||||
|
let pubkey_raw = &public_key.serialize()[1..65];
|
||||||
|
let pubkey_hash = SubstrateKeccakHasher::hash(pubkey_raw);
|
||||||
|
|
||||||
|
let mut evm_addr_bytes = [0u8; 20];
|
||||||
|
evm_addr_bytes.copy_from_slice(&pubkey_hash.as_ref()[12..32]);
|
||||||
|
|
||||||
|
let substrate_account_a = AccountId32::new([11u8; 32]);
|
||||||
|
let substrate_account_b = AccountId32::new([22u8; 32]);
|
||||||
|
let substrate_account_c = AccountId32::new([33u8; 32]);
|
||||||
|
|
||||||
|
let current_user = NewTestUser {
|
||||||
|
substrate_account: substrate_account_a.clone(),
|
||||||
|
evm_secret: secret_key,
|
||||||
|
evm_address: EvmAddress::from(evm_addr_bytes),
|
||||||
|
allowed_shares: 1000u128,
|
||||||
|
token_id: 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
let max_index = 0u8;
|
||||||
|
let raw_values_complex = vec![(0u8, ¤t_user)];
|
||||||
|
|
||||||
|
let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, _>(
|
||||||
|
max_index,
|
||||||
|
raw_values_complex,
|
||||||
|
|
||||||
|
|item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> {
|
||||||
|
let (index, u) = item;
|
||||||
|
let tmp_package = ClaimPackage {
|
||||||
|
shares: u.allowed_shares,
|
||||||
|
merkle_proof: Default::default(),
|
||||||
|
token_id: u.token_id,
|
||||||
|
index: index as u32
|
||||||
|
};
|
||||||
|
Ok((index as usize, tmp_package.get_preimage(&u.evm_address, network_id).to_vec()))
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let total_shares = 1_000_000;
|
||||||
|
let merkle_root = *merkle_tree.last().unwrap();
|
||||||
|
let network_state = NetworkShare::new(total_shares, merkle_root, 0);
|
||||||
|
let global_state = ShareState::new(total_shares);
|
||||||
|
|
||||||
|
NetworkShares::<Test>::insert(&network_id, network_state);
|
||||||
|
GlobalShares::<Test>::put(global_state);
|
||||||
|
|
||||||
|
let setup_governor = |account: &<Test as frame_system::Config>::AccountId| {
|
||||||
|
MemberShares::<Test>::insert(account, MemberShare {
|
||||||
|
locked_balance: minimum_donation,
|
||||||
|
initiated_network: None,
|
||||||
|
activated_shares: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
setup_governor(&substrate_account_a);
|
||||||
|
setup_governor(&substrate_account_b);
|
||||||
|
setup_governor(&substrate_account_c);
|
||||||
|
|
||||||
|
let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, 0u8);
|
||||||
|
let claim_package = ClaimPackage {
|
||||||
|
shares: current_user.allowed_shares,
|
||||||
|
merkle_proof: BoundedVec::try_from(proof).unwrap(),
|
||||||
|
token_id: current_user.token_id,
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let preimage = claim_package.get_preimage(¤t_user.evm_address, network_id);
|
||||||
|
let preimage_hash = SubstrateKeccakHasher::hash(&preimage);
|
||||||
|
|
||||||
|
let generate_test_signature = |target_account: &<Test as frame_system::Config>::AccountId| -> EvmSignature {
|
||||||
|
let active_nonce = ClaimedHashes::<Test>::get(&preimage_hash);
|
||||||
|
let message_string_bytes = Governance::ethereum_signable_message(
|
||||||
|
&target_account,
|
||||||
|
&network_id,
|
||||||
|
&preimage_hash,
|
||||||
|
active_nonce,
|
||||||
|
);
|
||||||
|
|
||||||
|
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
|
||||||
|
let mut message_bytes = [0u8; 32];
|
||||||
|
message_bytes.copy_from_slice(message_hash.as_ref());
|
||||||
|
|
||||||
|
let msg_to_sign = Message::parse(&message_bytes);
|
||||||
|
let (sig, recovery_id) = libsecp256k1::sign(&msg_to_sign, ¤t_user.evm_secret);
|
||||||
|
|
||||||
|
let mut signature_raw = [0u8; 65];
|
||||||
|
signature_raw[0..32].copy_from_slice(&sig.r.b32());
|
||||||
|
signature_raw[32..64].copy_from_slice(&sig.s.b32());
|
||||||
|
signature_raw[64] = recovery_id.serialize();
|
||||||
|
|
||||||
|
EvmSignature::from(signature_raw)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sig_0 = generate_test_signature(&substrate_account_a);
|
||||||
|
assert_ok!(Governance::claim(
|
||||||
|
RuntimeOrigin::signed(substrate_account_a.clone()),
|
||||||
|
network_id,
|
||||||
|
claim_package.clone(),
|
||||||
|
current_user.evm_address.clone(),
|
||||||
|
sig_0,
|
||||||
|
400,
|
||||||
|
));
|
||||||
|
|
||||||
|
let global_share = GlobalShares::<Test>::get();
|
||||||
|
assert_eq!(global_share.claimed_shares, 400);
|
||||||
|
assert_eq!(global_share.total_shares, total_shares);
|
||||||
|
|
||||||
|
let network_share = NetworkShares::<Test>::get(&network_id);
|
||||||
|
assert_eq!(network_share.claimed_shares(), 400);
|
||||||
|
assert_eq!(network_share.total_shares(), total_shares);
|
||||||
|
|
||||||
|
assert_eq!(MemberShares::<Test>::get(&substrate_account_a).activated_shares, 400);
|
||||||
|
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 1);
|
||||||
|
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 400);
|
||||||
|
|
||||||
|
let sig_1_fail = generate_test_signature(&substrate_account_b);
|
||||||
|
assert_err!(
|
||||||
|
Governance::claim(
|
||||||
|
RuntimeOrigin::signed(substrate_account_b.clone()),
|
||||||
|
network_id,
|
||||||
|
claim_package.clone(),
|
||||||
|
current_user.evm_address.clone(),
|
||||||
|
sig_1_fail,
|
||||||
|
700,
|
||||||
|
),
|
||||||
|
Error::<Test>::ExceedsAllowedShares
|
||||||
|
);
|
||||||
|
|
||||||
|
let sig_1_success = generate_test_signature(&substrate_account_b);
|
||||||
|
assert_ok!(Governance::claim(
|
||||||
|
RuntimeOrigin::signed(substrate_account_b.clone()),
|
||||||
|
network_id,
|
||||||
|
claim_package.clone(),
|
||||||
|
current_user.evm_address.clone(),
|
||||||
|
sig_1_success,
|
||||||
|
100,
|
||||||
|
));
|
||||||
|
|
||||||
|
let global_share = GlobalShares::<Test>::get();
|
||||||
|
assert_eq!(global_share.claimed_shares, 500);
|
||||||
|
assert_eq!(global_share.total_shares, total_shares);
|
||||||
|
|
||||||
|
let network_share = NetworkShares::<Test>::get(&network_id);
|
||||||
|
assert_eq!(network_share.claimed_shares(), 500);
|
||||||
|
assert_eq!(network_share.total_shares(), total_shares);
|
||||||
|
|
||||||
|
assert_eq!(MemberShares::<Test>::get(&substrate_account_b).activated_shares, 100);
|
||||||
|
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 2);
|
||||||
|
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 500);
|
||||||
|
|
||||||
|
let sig_2_final = generate_test_signature(&substrate_account_c);
|
||||||
|
assert_ok!(Governance::claim(
|
||||||
|
RuntimeOrigin::signed(substrate_account_c.clone()),
|
||||||
|
network_id,
|
||||||
|
claim_package.clone(),
|
||||||
|
current_user.evm_address.clone(),
|
||||||
|
sig_2_final,
|
||||||
|
300,
|
||||||
|
));
|
||||||
|
|
||||||
|
let global_share = GlobalShares::<Test>::get();
|
||||||
|
assert_eq!(global_share.claimed_shares, 1_000);
|
||||||
|
assert_eq!(global_share.total_shares, total_shares);
|
||||||
|
|
||||||
|
let network_share = NetworkShares::<Test>::get(&network_id);
|
||||||
|
assert_eq!(network_share.claimed_shares(), 1_000);
|
||||||
|
assert_eq!(network_share.total_shares(), total_shares);
|
||||||
|
|
||||||
|
assert_eq!(MemberShares::<Test>::get(&substrate_account_c).activated_shares, 500);
|
||||||
|
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 3);
|
||||||
|
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 1_000);
|
||||||
|
|
||||||
|
let sig_3_exhausted = generate_test_signature(&substrate_account_a);
|
||||||
|
assert_err!(
|
||||||
|
Governance::claim(
|
||||||
|
RuntimeOrigin::signed(substrate_account_a),
|
||||||
|
network_id,
|
||||||
|
claim_package,
|
||||||
|
current_user.evm_address.clone(),
|
||||||
|
sig_3_exhausted,
|
||||||
|
100,
|
||||||
|
),
|
||||||
|
Error::<Test>::PartialClaimsExhausted
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_proof_from_python_preclaims_for_sepolia() {
|
fn verify_proof_from_python_preclaims_for_sepolia() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user