Compare commits

..

26 Commits

Author SHA1 Message Date
39d230afc0
update lockfile
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-16 17:39:13 +03:00
6ce12ca8d1
upgrade runtime with latest weaver implementation
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-16 17:37:12 +03:00
20e03d21ec
optimize get_preimage_slice function and add new benchmarked weights
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-16 17:03:29 +03:00
414604f296
inject the network ID into the hash chain and the arguments hash
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-16 14:10:12 +03:00
cc45fb5f9c
pin compiler version to fixed 1.93.1
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-13 11:14:21 +03:00
6e1fd726f3
merge ghost-exodus into main
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-12 20:23:24 +03:00
150e6438f8
new genesis file added
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-11 18:32:11 +03:00
c9288e7e1e
more final additions to genesis config
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-11 17:04:31 +03:00
3ed3c659c6
insert testnet preclaims into governance pallet
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-11 16:10:49 +03:00
1e1d2bdc81
update Cargo.lock after dependency version bumps
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-11 01:05:11 +03:00
2180d1c85b
extend on_initialize time ratio for safer block production
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-11 01:04:11 +03:00
3d2d905629
move complaints matrix to flat Vec, eliminate dynamic allocations
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-11 01:00:26 +03:00
8d097990a7
benchmark pallets based on new logic
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-10 23:26:20 +03:00
fe6582042a
extend genesis file with initial validator keys
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-10 22:25:21 +03:00
59b5356613
make decimals part of network data
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-10 21:40:06 +03:00
84195a64ef
migrate UTXO bridge proof to Legacy P2PKH with OP_RETURN account extraction
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-10 21:21:22 +03:00
fd8e52b520
adopt BIP-340 for all Secp256k1 networks in EXODUS
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-09 03:41:28 +03:00
a6e87c20d6
corrected text for genesis generation message
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-09 03:31:24 +03:00
43ec3492ef
merkle tree prefixes added
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-08 14:20:56 +03:00
3cf96b1820
make BFT function name more self-descriptive and align implementation with paper
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-08 14:17:01 +03:00
0e228d890d
rustfmt changes
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-18 14:41:58 +03:00
5e17c12677
use new logic from slow-clap for casper runtime
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-18 14:40:52 +03:00
c112ad22c2
apply eviction logic during the offence preparation
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-18 14:38:34 +03:00
0dd27d6429
eviction trait added
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-18 14:36:51 +03:00
8252107e96
make multiplication to round up during slash fraction calculation
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-10 11:14:06 +03:00
6419d4ba16
add hoodi network for the rpc-tester
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-06-10 11:11:46 +03:00
50 changed files with 1487 additions and 1777 deletions

22
Cargo.lock generated
View File

@ -1195,7 +1195,7 @@ dependencies = [
[[package]]
name = "casper-runtime"
version = "4.0.6"
version = "4.0.11"
dependencies = [
"casper-runtime-constants",
"frame-benchmarking",
@ -3363,10 +3363,10 @@ dependencies = [
]
[[package]]
name = "frost-secp256k1"
name = "frost-secp256k1-tr"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f6974379aee791f2f9e0db47c37d9e4c77ea8a8233e488ae8949ce4c6864e96"
checksum = "ff8a25b14bfd5d25deba5edce61ec99c3afb752a1d26630a196bba4cb1c4ca5e"
dependencies = [
"document-features",
"frost-core",
@ -3684,7 +3684,7 @@ dependencies = [
[[package]]
name = "ghost-exodus"
version = "0.0.7"
version = "0.0.11"
dependencies = [
"chacha20poly1305",
"frame-benchmarking",
@ -3693,7 +3693,7 @@ dependencies = [
"frame-system",
"frost-core",
"frost-ed25519",
"frost-secp256k1",
"frost-secp256k1-tr",
"ghost-helpers",
"ghost-networks",
"ghost-traits",
@ -3733,7 +3733,7 @@ dependencies = [
[[package]]
name = "ghost-governance"
version = "0.3.5"
version = "0.3.7"
dependencies = [
"frame-benchmarking",
"frame-support",
@ -3757,7 +3757,7 @@ dependencies = [
[[package]]
name = "ghost-helpers"
version = "0.0.6"
version = "0.0.11"
dependencies = [
"frame-support",
"ghost-traits",
@ -3839,7 +3839,7 @@ dependencies = [
[[package]]
name = "ghost-networks"
version = "0.2.9"
version = "0.2.10"
dependencies = [
"frame-benchmarking",
"frame-support",
@ -4085,7 +4085,7 @@ dependencies = [
[[package]]
name = "ghost-traits"
version = "0.4.4"
version = "0.4.6"
dependencies = [
"frame-support",
"frost-core",
@ -4109,7 +4109,7 @@ dependencies = [
[[package]]
name = "ghost-weaver"
version = "0.0.4"
version = "0.0.10"
dependencies = [
"frame-benchmarking",
"frame-support",
@ -8553,7 +8553,7 @@ dependencies = [
[[package]]
name = "runtime-common"
version = "0.4.8"
version = "0.4.9"
dependencies = [
"frame-support",
"frame-system",

View File

@ -89,7 +89,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features =
scale-info = { version = "2.5.0", default-features = false }
subxt = { version = "0.37.0", default-features = false }
frost-secp256k1 = { version = "2.2.0", default-features = false, features = ["serialization"] }
frost-secp256k1-tr = { version = "2.2.0", default-features = false, features = ["serialization"] }
frost-ed25519 = { version = "2.2.0", default-features = false, features = ["serialization"] }
chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["alloc"] }
frost-core = { version = "2.2.0", default-features = false }
@ -345,7 +345,7 @@ curve25519-dalek = { opt-level = 3 }
ed25519-dalek = { opt-level = 3 }
flate2 = { opt-level = 3 }
frost-core = { opt-level = 3 }
frost-secp256k1 = { opt-level = 3 }
frost-secp256k1-tr = { opt-level = 3 }
futures-channel = { opt-level = 3 }
hash-db = { opt-level = 3 }
hashbrown = { opt-level = 3 }

View File

@ -346,7 +346,7 @@ sha256sum /etc/ghost/casper.json
You should see:
```
9da6045ed6d4fd844e3939573b31c013d0e564e542029330faa6d978cb4a915a
4645c7d39a64424d823f659d408070f8c90426117e7af147b0360697b2db7f4f /etc/ghost/casper.json
```
Create running `ghost-node` service that starts on system boot using `--unit-file` flag.

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-exodus"
version = "0.0.7"
version = "0.0.11"
description = "Threshold signature generation with DKG included"
license.workspace = true
authors.workspace = true
@ -28,7 +28,7 @@ sp-core = { workspace = true }
sp-std = { workspace = true }
sp-io = { workspace = true }
frost-secp256k1 = { workspace = true }
frost-secp256k1-tr = { workspace = true }
frost-ed25519 = { workspace = true }
frost-core = { workspace = true }
rand_chacha = { workspace = true }
@ -75,7 +75,7 @@ std = [
"sp-core/std",
"sp-std/std",
"sp-io/std",
"frost-secp256k1/std",
"frost-secp256k1-tr/std",
"frost-ed25519/std",
"frost-core/std",
"ghost-traits/std",

View File

@ -74,7 +74,7 @@ mod benchmarks {
c: Linear<4, { T::MaxAuthorities::get() }>
) -> Result<(), BenchmarkError> {
let max_signers = c as AuthIndex;
let min_signers = get_byzantium_threshold(max_signers);
let min_signers = get_byzantium_quorum_threshold(max_signers);
let (mut dkg_state, network_curve, authority, authority_index) =
prepare_pallet::<T>(max_signers as usize);
@ -637,7 +637,7 @@ mod benchmarks {
#[benchmark]
fn register_signature_share() -> Result<(), BenchmarkError> {
let max_signers = T::MaxAuthorities::get();
let threshold = get_byzantium_threshold(max_signers);
let threshold = get_byzantium_quorum_threshold(max_signers);
let network_id: NetworkIdOf<T> = 69_420u64.unique_saturated_into();
let (mut dkg_state, network_curve, authority, authority_index) =

View File

@ -285,6 +285,9 @@ impl DistributedKeyGeneration<AuthIndex, ExodusError> for NetworkCurve {
_ => ExodusError::Unknown,
})?;
let key_package = key_package.normalize_package();
let pubkey_package = pubkey_package.normalize_package();
let key_package_bytes = key_package.serialize()
.map_err(|_| ExodusError::SerializationError)?;
@ -337,15 +340,4 @@ impl DistributedKeyGeneration<AuthIndex, ExodusError> for NetworkCurve {
.map_err(|_| ExodusError::SerializationError)
})
}
fn dkg_decompress_key(
&self,
verifying_key_bytes: &[u8],
) -> Result<Vec<u8>, ExodusError> {
with_ciphersuite!(self, |f| {
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
.map_err(|_| ExodusError::DeserializationError)?;
decomporess_key(verifying_key)
})
}
}

View File

@ -17,6 +17,13 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
fn header_bytes_len(&self) -> usize { 5 }
fn signature_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 64,
NetworkCurve::Ed25519 => 64,
}
}
fn element_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 33,
@ -76,7 +83,7 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
<InnerGroup as frost_core::Group>::Serialization::try_from(group_commitment_bytes.as_ref())
.map_err(|_| ExodusError::DeserializationError)?;
let group_commitment_element =
let mut group_commitment_element =
<InnerGroup as frost_core::Group>::deserialize(&group_commitment_serialization)
.map_err(|_| ExodusError::DeserializationError)?;
@ -95,15 +102,6 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
.map_err(|_| ExodusError::DeserializationError)?;
let challenge = frost_core::challenge(
&group_commitment_element,
&verifying_key,
message,
).map_err(|err| match err {
f::Error::SerializationError => ExodusError::SerializationError,
_ => ExodusError::Unknown,
})?;
let hiding_element_serialization =
<InnerGroup as frost_core::Group>::Serialization::try_from(nonce_hiding_bytes.as_ref())
.map_err(|_| ExodusError::DeserializationError)?;
@ -120,7 +118,25 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
<InnerGroup as frost_core::Group>::deserialize(&binding_element_serialization)
.map_err(|_| ExodusError::DeserializationError)?;
let r_i_element = hiding_element + (binding_element * binding_factor_scalar);
let mut r_i_element = hiding_element + (binding_element * binding_factor_scalar);
let group_commitment_wrapper =
frost_core::GroupCommitment::<Ciphersuite>::from_element(group_commitment_element);
if !group_commitment_wrapper.is_normalized() {
r_i_element = -r_i_element;
group_commitment_element = -group_commitment_element;
}
let challenge = <Ciphersuite as frost_core::Ciphersuite>::challenge(
&group_commitment_element,
&verifying_key,
message,
).map_err(|err| match err {
f::Error::SerializationError => ExodusError::SerializationError,
_ => ExodusError::Unknown,
})?;
let commitment_share =
frost_core::round1::GroupCommitmentShare::from_element(r_i_element);
@ -275,14 +291,14 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
let signing_package = f::SigningPackage::new(signing_commitments, message);
let binding_factors_list_wrapped =
frost_core::compute_binding_factor_list(&signing_package, &verifying_key, &[])
frost_core::compute_binding_factor_list::<Ciphersuite>(&signing_package, &verifying_key, &[])
.map_err(|err| match err {
f::Error::SerializationError => ExodusError::SerializationError,
_ => ExodusError::Unknown,
})?;
let group_commitment =
frost_core::compute_group_commitment(&signing_package, &binding_factors_list_wrapped)
frost_core::compute_group_commitment::<Ciphersuite>(&signing_package, &binding_factors_list_wrapped)
.map_err(|err| match err {
f::Error::IdentityCommitment => ExodusError::IdentityCommitment,
f::Error::UnknownIdentifier => ExodusError::UnknownIdentifier,

View File

@ -3,46 +3,53 @@ macro_rules! with_ciphersuite {
($curve:expr, |$alias:ident| $body:block) => {
match $curve {
NetworkCurve::Secp256k1 => {
use k256::elliptic_curve::sec1::ToEncodedPoint;
use frost_secp256k1 as $alias;
#[allow(unused_imports)]
use frost_secp256k1_tr as $alias;
#[allow(unused_imports)]
use frost_secp256k1::Secp256K1Sha256 as Ciphersuite;
use frost_secp256k1_tr::Secp256K1Sha256TR as Ciphersuite;
use $alias::keys::EvenY;
#[allow(dead_code)]
fn decomporess_key(
verifying_key: $alias::VerifyingKey,
) -> Result<Vec<u8>, ExodusError> {
let element = verifying_key.to_element();
if element == k256::ProjectivePoint::IDENTITY {
return Err(ExodusError::InvalidIdentityElement);
trait UniversalNormalizer {
fn normalize_package(self) -> Self;
fn is_normalized(&self) -> bool;
}
let mut fixed_serialized = [0; 65];
let serialized_point = element.to_affine().to_encoded_point(false);
let serialized = serialized_point.as_bytes();
fixed_serialized.copy_from_slice(serialized);
impl<T: EvenY> UniversalNormalizer for T {
#[inline]
fn normalize_package(self) -> Self {
self.into_even_y(None)
}
Ok(fixed_serialized.to_vec())
#[inline]
fn is_normalized(&self) -> bool {
self.has_even_y()
}
}
$body
},
NetworkCurve::Ed25519 => {
#[allow(unused_imports)]
use frost_ed25519 as $alias;
#[allow(unused_imports)]
use frost_ed25519::Ed25519Sha512 as Ciphersuite;
#[allow(dead_code)]
fn decomporess_key(
verifying_key: $alias::VerifyingKey,
) -> Result<Vec<u8>, ExodusError> {
verifying_key.serialize().map_err(|err| match err {
f::Error::SerializationError => ExodusError::SerializationError,
_ => ExodusError::Unknown,
})
trait UniversalNormalizer {
fn normalize_package(self) -> Self;
fn is_normalized(&self) -> bool;
}
impl<T> UniversalNormalizer for T {
#[inline]
fn normalize_package(self) -> Self { self }
#[inline]
fn is_normalized(&self) -> bool { true }
}
$body

View File

@ -29,7 +29,7 @@ use sp_runtime::traits::BlockNumberProvider;
use sp_io::hashing::blake2_256;
use ghost_helpers::{
get_byzantium_threshold, SubstrateBlake2Hasher,
get_byzantium_quorum_threshold, SubstrateBlake2Hasher,
hash_chain::sequential_hash,
networks::{NetworkData, NetworkCurve, NetworkType},
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
@ -130,26 +130,17 @@ const fn postcard_varint_len(len: usize) -> usize {
else { 3 }
}
const fn signature_bytes_len(
element_bytes_len: usize,
scalar_bytes_len: usize,
)-> usize {
let signature_bytes_len = element_bytes_len.saturating_add(scalar_bytes_len);
let signature_vec_prefix = postcard_varint_len(signature_bytes_len);
signature_bytes_len.saturating_add(signature_vec_prefix)
}
const fn round1_package_size(
commitments_count: usize,
element_bytes_len: usize,
scalar_bytes_len: usize,
signature_bytes_len: usize,
header_bytes_len: usize
) -> usize {
let commitments_bytes_len = commitments_count.saturating_mul(element_bytes_len);
let commitment_vec_prefix = postcard_varint_len(commitments_count);
signature_bytes_len(element_bytes_len, scalar_bytes_len)
postcard_varint_len(signature_bytes_len)
.saturating_add(signature_bytes_len)
.saturating_add(header_bytes_len)
.saturating_add(commitment_vec_prefix)
.saturating_add(commitments_bytes_len)
@ -263,12 +254,13 @@ pub struct Round1MaxBytes<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Get<u32> for Round1MaxBytes<T> {
fn get() -> u32 {
let max_authorities = T::MaxAuthorities::get();
let min_authorities = get_byzantium_threshold(max_authorities);
let min_authorities = get_byzantium_quorum_threshold(max_authorities);
let max_signature_bytes = ELEMENT_MAX_BYTES + SCALAR_MAX_BYTES;
round1_package_size(
min_authorities as usize,
ELEMENT_MAX_BYTES as usize,
SCALAR_MAX_BYTES as usize,
max_signature_bytes as usize,
HEADER_MAX_BYTES as usize,
) as u32
}
@ -324,7 +316,7 @@ pub struct BindingFactorsProof<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Get<u32> for BindingFactorsProof<T> {
fn get() -> u32 {
let max_authorities = T::MaxAuthorities::get();
get_byzantium_threshold(max_authorities)
get_byzantium_quorum_threshold(max_authorities)
.next_power_of_two()
.trailing_zeros()
}
@ -363,10 +355,8 @@ impl<T: Config> Get<u32> for SignatureShareMaxBytes<T> {
pub struct SignatureMaxBytes<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Get<u32> for SignatureMaxBytes<T> {
fn get() -> u32 {
signature_bytes_len(
ELEMENT_MAX_BYTES as usize,
SCALAR_MAX_BYTES as usize,
) as u32
let signature_max_bytes = ELEMENT_MAX_BYTES + SCALAR_MAX_BYTES;
postcard_varint_len(signature_max_bytes as usize) as u32
}
}
@ -1422,7 +1412,7 @@ pub mod pallet {
};
let threshold = ActiveAuthorities::<T>::decode_len(&network_curve)
.map(|max_participants| get_byzantium_threshold(max_participants))
.map(|max_participants| get_byzantium_quorum_threshold(max_participants))
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let exodus_session = package.session;
@ -1515,7 +1505,7 @@ pub mod pallet {
let max_authorities = ActiveAuthorities::<T>::decode_len(&network_curve)
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let threshold = get_byzantium_threshold(max_authorities);
let threshold = get_byzantium_quorum_threshold(max_authorities);
let exodus_session = package.session;
ensure!(
@ -1633,7 +1623,7 @@ pub mod pallet {
};
let threshold = ActiveAuthorities::<T>::decode_len(&network_curve)
.map(|max_participants| get_byzantium_threshold(max_participants))
.map(|max_participants| get_byzantium_quorum_threshold(max_participants))
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let exodus_session = package.session;
@ -1898,7 +1888,7 @@ pub mod pallet {
None => return weight,
};
let min_threshold = get_byzantium_threshold(qualification_len);
let min_threshold = get_byzantium_quorum_threshold(qualification_len);
weight.saturating_accrue(T::DbWeight::get().reads(1));
let maybe_cursor = DkgMaybeCursor::<T>::get(&network_curve);
@ -2427,6 +2417,7 @@ impl<T: Config> Pallet<T> {
let mut number_of_leaked_keys = 0;
let qualified_indexes = dkg_qualification.get_indexes();
let total_authorities_len = qualified_indexes.count_ones::<usize>();
let mut failed_authorities = ParticipantsBitmap::<T>::empty_from(
&qualified_indexes,
@ -2436,17 +2427,17 @@ impl<T: Config> Pallet<T> {
&qualified_indexes,
);
let mut complaints_matrix: BTreeMap<AuthIndex, ParticipantsBitmap<T>> =
BTreeMap::new();
let mut complaints_matrix = vec![
ParticipantsBitmap::<T>::empty_from(&qualified_indexes);
total_authorities_len
];
complaints_bitmap.iter().for_each(|(&accuser_index, offenders_bitmap)| {
offenders_bitmap.iter().for_each(|offended_index| {
complaints_matrix
.entry(offended_index)
.or_insert_with(|| {
ParticipantsBitmap::<T>::empty_from(&qualified_indexes)
})
.insert(accuser_index);
offenders_bitmap.iter().for_each(|offended_index: AuthIndex| {
let idx = offended_index as usize;
if idx >= complaints_matrix.len() { return; }
complaints_matrix[idx].insert(accuser_index);
});
});
@ -2457,14 +2448,18 @@ impl<T: Config> Pallet<T> {
number_of_leaked_keys.saturating_inc();
}
if let Some(accusers_bitmap) = complaints_matrix.get(&offended_index) {
let idx = offended_index as usize;
if idx >= complaints_matrix.len() { return; }
let accusers_bitmap = &complaints_matrix[idx];
if accusers_bitmap.is_empty() { return; }
let successful_justifications = accusers_bitmap & recipients_bitmap;
let failed_to_justify = successful_justifications != *accusers_bitmap;
if failed_to_justify {
failed_authorities.insert(offended_index);
}
}
});
let result_mask = if number_of_leaked_keys >= min_threshold {
@ -2533,6 +2528,7 @@ impl<T: Config> Pallet<T> {
dkg_qualification.intersect_indexes(&participants);
if dkg_qualification.count_ones::<AuthIndex>() < min_threshold {
dkg_qualification.nullify_indexes();
return;
@ -2711,12 +2707,12 @@ impl<T: Config> Pallet<T> {
}
let authorities = QualificationAuthorities::<T>::get(&network_curve);
let expected_count = get_byzantium_threshold(authorities.len());
let expected_count = get_byzantium_quorum_threshold(authorities.len());
let expected_package_len = round1_package_size(
expected_count,
network_curve.element_bytes_len(),
network_curve.scalar_bytes_len(),
network_curve.signature_bytes_len(),
network_curve.header_bytes_len(),
);
@ -3283,7 +3279,7 @@ impl<T: Config> Pallet<T> {
let max_authorities = ActiveAuthorities::<T>::decode_len(&network_curve)
.ok_or(ExodusError::NoActiveAuthorities)?;
let threshold = get_byzantium_threshold(max_authorities);
let threshold = get_byzantium_quorum_threshold(max_authorities);
let mut exodus_requests = ExodusRequests::<T>::iter_prefix(&network_curve)
.collect::<Vec<(ExodusSession, ExodusRequest<_, _>)>>();
@ -3732,7 +3728,7 @@ impl<T: Config> Pallet<T> {
.map(|max_signers| max_signers as AuthIndex)
.ok_or(ExodusError::InvalidMaxSigners(0))?;
let min_signers = get_byzantium_threshold(max_signers);
let min_signers = get_byzantium_quorum_threshold(max_signers);
if qualification_state.contains_index(authority_index) {
return Ok(ExodusOk::DkgRoundAlreadyPassed(

View File

@ -15,8 +15,8 @@ use rand_chacha::rand_core::SeedableRng;
use rand_chacha::ChaCha20Rng;
use frost_core::Field;
use frost_secp256k1::{
Secp256K1Sha256, Secp256K1ScalarField, Identifier as Secp256K1Identifier,
use frost_secp256k1_tr::{
Secp256K1Sha256TR, Secp256K1ScalarField, Identifier as Secp256K1Identifier,
keys::{
VerifyingShare as Secp256K1VerifyingShare,
SigningShare as Secp256K1SigningShare,
@ -841,7 +841,7 @@ fn run_dkg_session(
let threshold = ext.execute_with(|| {
let len = ActiveAuthorities::<Runtime>::decode_len(&curve)
.unwrap_or(0);
get_byzantium_threshold(len) as u16
get_byzantium_quorum_threshold(len) as u16
});
let mut exodus_steps_outer = BTreeMap::new();
@ -1259,7 +1259,7 @@ fn test_exodus_dkg_robustness() {
assert_eq!(state.get_dkg_index(), dkg_index);
assert_eq!(state.get_phase(), DkgPhase::Finalized);
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
assert!(qualification_count >= bft_threshold);
assert!(state.count_ones::<usize>() >= bft_threshold);
assert_eq!(state.get_indexes().active_bits_count(), authorities_len as u32);
@ -1299,7 +1299,7 @@ fn test_prepare_round4_abort_on_byzantine_attack() {
}
}
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
ext.execute_with(|| {
let mut complaints_map = BitmapByAuthority::<Runtime>::default();
for authority in 0..=bft_threshold {
@ -1344,7 +1344,7 @@ fn test_prepare_round5_abort_on_byzantine_attack() {
}
}
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
ext.execute_with(|| {
let mut justifications_map = BitmapByAuthority::<Runtime>::default();
for authority in 0..=bft_threshold {
@ -1452,7 +1452,7 @@ fn test_merkle_tree_success_power_of_two_for_shares() {
let indices = (1..=32).collect::<Vec<AuthIndex>>();
let shares = generate_mock_secp256k1_shares(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&shares,
|share| share.serialize().map_err(|_| ExodusError::SerializationError),
).unwrap();
@ -1465,7 +1465,7 @@ fn test_merkle_tree_success_power_of_two_for_shares() {
).unwrap();
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&shares,
&tree,
auth_index,
@ -1489,7 +1489,7 @@ fn test_merkle_tree_success_power_of_two_for_scalar() {
let indices = (1..=32).collect::<Vec<AuthIndex>>();
let scalars = generate_mock_secp256k1_scalars(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
|scalar| {
let serialized = <Secp256K1ScalarField as Field>::serialize(scalar);
@ -1505,7 +1505,7 @@ fn test_merkle_tree_success_power_of_two_for_scalar() {
).unwrap();
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
&tree,
auth_index,
@ -1538,7 +1538,7 @@ fn test_random_sparse_combinations_stress_for_shares() {
let indices: Vec<AuthIndex> = unique_indices.into_iter().collect();
let shares = generate_mock_secp256k1_shares(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&shares,
|share| share.serialize().map_err(|_| ExodusError::SerializationError),
).unwrap();
@ -1547,7 +1547,7 @@ fn test_random_sparse_combinations_stress_for_shares() {
for &auth_index in &indices {
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&shares,
&tree,
auth_index,
@ -1576,7 +1576,7 @@ fn test_random_sparse_combinations_stress_for_scalars() {
let indices: Vec<AuthIndex> = unique_indices.into_iter().collect();
let scalars = generate_mock_secp256k1_scalars(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
|scalar| {
let serialized = <Secp256K1ScalarField as Field>::serialize(scalar);
@ -1588,7 +1588,7 @@ fn test_random_sparse_combinations_stress_for_scalars() {
for &auth_index in &indices {
let (scalars_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
&tree,
auth_index,
@ -1614,7 +1614,7 @@ fn test_participants_with_high_index_gap_for_shares() {
let indices = vec![1, 1023];
let shares = generate_mock_secp256k1_shares(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&shares,
|share| share.serialize().map_err(|_| ExodusError::SerializationError),
).unwrap();
@ -1622,7 +1622,7 @@ fn test_participants_with_high_index_gap_for_shares() {
let root = *tree.last().unwrap();
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&shares,
&tree,
1023,
@ -1646,7 +1646,7 @@ fn test_participants_with_high_index_gap_for_scalars() {
let indices = vec![1, 1023];
let scalars = generate_mock_secp256k1_scalars(&indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
|scalar| {
let serialized = <Secp256K1ScalarField as Field>::serialize(scalar);
@ -1657,7 +1657,7 @@ fn test_participants_with_high_index_gap_for_scalars() {
let root = *tree.last().unwrap();
let (scalars_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
&tree,
1023,
@ -1684,7 +1684,7 @@ fn test_regression_consecutive_vs_position_for_shares() {
let custom_indices = vec![2, 3];
let shares = generate_mock_secp256k1_shares(&custom_indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&shares,
|scalar| scalar.serialize().map_err(|_| ExodusError::SerializationError),
).unwrap();
@ -1692,7 +1692,7 @@ fn test_regression_consecutive_vs_position_for_shares() {
let root = *tree.last().unwrap();
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&shares,
&tree,
2,
@ -1709,7 +1709,7 @@ fn test_regression_consecutive_vs_position_for_scalars() {
let custom_indices = vec![2, 3];
let scalars = generate_mock_secp256k1_scalars(&custom_indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
|scalar| {
let serialized = <Secp256K1ScalarField as Field>::serialize(scalar);
@ -1720,7 +1720,7 @@ fn test_regression_consecutive_vs_position_for_scalars() {
let root = *tree.last().unwrap();
let (scalars_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
&tree,
2,
@ -1740,7 +1740,7 @@ fn test_attack_with_unknown_authority_index_for_shares() {
let custom_indices = vec![1, 2, 3];
let shares = generate_mock_secp256k1_shares(&custom_indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&shares,
|scalar| scalar.serialize().map_err(|_| ExodusError::SerializationError),
).unwrap();
@ -1748,7 +1748,7 @@ fn test_attack_with_unknown_authority_index_for_shares() {
let root = *tree.last().unwrap();
let (shares_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&shares,
&tree,
2,
@ -1765,7 +1765,7 @@ fn test_attack_with_unknown_authority_index_for_scalars() {
let custom_indices = vec![1, 2, 3];
let scalars = generate_mock_secp256k1_scalars(&custom_indices);
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256, _, _>(
let tree = NetworkCurve::build_merkle_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
|scalar| {
let serialized = <Secp256K1ScalarField as Field>::serialize(scalar);
@ -1776,7 +1776,7 @@ fn test_attack_with_unknown_authority_index_for_scalars() {
let root = *tree.last().unwrap();
let (scalars_bytes, proof) =
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256, _, _>(
NetworkCurve::generate_merkle_proof_from_tree::<Secp256K1Sha256TR, _, _>(
&scalars,
&tree,
2,
@ -2227,7 +2227,7 @@ fn curve_wrapper_for_dkg_works() {
#[test]
fn test_common_way_of_frost_usage() {
use frost_secp256k1 as frost;
use frost_secp256k1_tr as frost;
let mut rng = ChaCha20Rng::from_seed(Default::default());

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_exodus`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -77,8 +77,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `351`
// Estimated: `3816`
// Minimum execution time: 239_348_000 picoseconds.
Weight::from_parts(241_125_000, 0)
// Minimum execution time: 239_012_000 picoseconds.
Weight::from_parts(241_388_000, 0)
.saturating_add(Weight::from_parts(0, 3816))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(3))
@ -100,11 +100,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)`
// Minimum execution time: 1_372_279_000 picoseconds.
Weight::from_parts(1_268_130_469, 0)
// Minimum execution time: 1_339_828_000 picoseconds.
Weight::from_parts(1_212_625_790, 0)
.saturating_add(Weight::from_parts(0, 3856))
// Standard Error: 17_178
.saturating_add(Weight::from_parts(32_976_202, 0).saturating_mul(c.into()))
// Standard Error: 11_831
.saturating_add(Weight::from_parts(33_120_868, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
@ -124,11 +124,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 253_301_000 picoseconds.
Weight::from_parts(255_449_282, 0)
// Minimum execution time: 251_478_000 picoseconds.
Weight::from_parts(254_144_621, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 25_029
.saturating_add(Weight::from_parts(6_615_251, 0).saturating_mul(c.into()))
// Standard Error: 24_613
.saturating_add(Weight::from_parts(6_595_819, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -148,11 +148,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 238_882_000 picoseconds.
Weight::from_parts(242_930_556, 0)
// Minimum execution time: 236_155_000 picoseconds.
Weight::from_parts(242_055_948, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 13_070
.saturating_add(Weight::from_parts(426_492, 0).saturating_mul(c.into()))
// Standard Error: 15_623
.saturating_add(Weight::from_parts(360_411, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -172,11 +172,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 250_894_000 picoseconds.
Weight::from_parts(252_272_982, 0)
// Minimum execution time: 249_507_000 picoseconds.
Weight::from_parts(248_505_595, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 17_703
.saturating_add(Weight::from_parts(4_110_522, 0).saturating_mul(c.into()))
// Standard Error: 20_302
.saturating_add(Weight::from_parts(4_191_526, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -197,8 +197,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `448`
// Estimated: `3913`
// Minimum execution time: 265_392_000 picoseconds.
Weight::from_parts(268_799_000, 0)
// Minimum execution time: 264_214_000 picoseconds.
Weight::from_parts(266_905_000, 0)
.saturating_add(Weight::from_parts(0, 3913))
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3))
@ -222,11 +222,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0 + c * (770 ±0)`
// Estimated: `1551 + c * (1019 ±27)`
// Minimum execution time: 282_654_000 picoseconds.
Weight::from_parts(286_257_905, 0)
// Minimum execution time: 281_073_000 picoseconds.
Weight::from_parts(280_778_987, 0)
.saturating_add(Weight::from_parts(0, 1551))
// Standard Error: 92_951
.saturating_add(Weight::from_parts(2_838_516, 0).saturating_mul(c.into()))
.saturating_add(Weight::from_parts(3_489_552, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(7))
.saturating_add(RocksDbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
@ -253,8 +253,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `588`
// Estimated: `4053`
// Minimum execution time: 296_169_000 picoseconds.
Weight::from_parts(298_267_000, 0)
// Minimum execution time: 291_972_000 picoseconds.
Weight::from_parts(295_122_000, 0)
.saturating_add(Weight::from_parts(0, 4053))
.saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(5))
@ -283,8 +283,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `968`
// Estimated: `4433`
// Minimum execution time: 332_972_000 picoseconds.
Weight::from_parts(336_569_000, 0)
// Minimum execution time: 331_528_000 picoseconds.
Weight::from_parts(333_974_000, 0)
.saturating_add(Weight::from_parts(0, 4433))
.saturating_add(RocksDbWeight::get().reads(10))
.saturating_add(RocksDbWeight::get().writes(4))
@ -324,7 +324,7 @@ impl WeightInfo for () {
// Measured: `11259`
// Estimated: `14724`
// Minimum execution time: 3_842_259_000 picoseconds.
Weight::from_parts(3_853_670_000, 0)
Weight::from_parts(3_985_167_000, 0)
.saturating_add(Weight::from_parts(0, 14724))
.saturating_add(RocksDbWeight::get().reads(14))
.saturating_add(RocksDbWeight::get().writes(8))
@ -343,11 +343,11 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_evm_bridge_out_exodus() -> Weight {
// Proof Size summary in bytes:
// Measured: `447`
// Estimated: `3912`
// Minimum execution time: 138_303_000 picoseconds.
Weight::from_parts(139_968_000, 0)
.saturating_add(Weight::from_parts(0, 3912))
// Measured: `448`
// Estimated: `3913`
// Minimum execution time: 137_053_000 picoseconds.
Weight::from_parts(138_693_000, 0)
.saturating_add(Weight::from_parts(0, 3913))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4))
}

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-governance"
version = "0.3.5"
version = "0.3.7"
description = "Full-chain and cross-chain governance pallet with early adopter share claims"
license.workspace = true
authors.workspace = true

View File

@ -40,24 +40,27 @@ mod benchmarks {
};
let preimage_init = claim_package_init.get_preimage(&dummy_evm_address, network_id);
let some_hash = SubstrateKeccakHasher::hash(&preimage_init);
let mut leaf_combined = sp_std::vec![0u8; 1 + preimage_init.len()];
leaf_combined[1..].copy_from_slice(&preimage_init);
let some_hash = SubstrateKeccakHasher::hash(&leaf_combined);
let mut current_hash = some_hash;
let mut proof_hashes = Vec::with_capacity(max_proof_depth as usize);
let mut current_index = target_index as usize;
let hash_len = SubstrateKeccakHasher::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
let mut combined = sp_std::vec![1u8; 1 + hash_len * 2];
for _ in 0..max_proof_depth {
proof_hashes.push(some_hash);
if current_index % 2 == 0 {
combined[..hash_len].copy_from_slice(&current_hash.to_fixed_bytes());
combined[hash_len..].copy_from_slice(&some_hash.to_fixed_bytes());
combined[1..1 + hash_len].copy_from_slice(&current_hash.to_fixed_bytes());
combined[1 + hash_len..1 + hash_len * 2].copy_from_slice(&some_hash.to_fixed_bytes());
} else {
combined[..hash_len].copy_from_slice(&some_hash.to_fixed_bytes());
combined[hash_len..].copy_from_slice(&current_hash.to_fixed_bytes());
combined[1..1 + hash_len].copy_from_slice(&some_hash.to_fixed_bytes());
combined[1 + hash_len..1 + hash_len * 2].copy_from_slice(&current_hash.to_fixed_bytes());
}
current_hash = SubstrateKeccakHasher::hash(&combined);

View File

@ -176,18 +176,18 @@ fn claim_happy_path() {
fn verify_proof_from_python_preclaims_for_sepolia() {
new_test_ext().execute_with(|| {
// ===========================================================================================================
// === FINAL MERKLE ROOT FOR [SEPOLIA]: 0xd7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c ===
// === FINAL MERKLE ROOT FOR [SEPOLIA]: c41c281c8404f5ce0adfca067ef8ed260f28c2cb1d7944c84ce2431936b1919e ===
// ===========================================================================================================
// [+] Total padded leaves: 128
// [+] Merkle tree size: 255
// [+] Network ID: 11155111
// [+] Proof size: 7
// [+] Total shares: 694000000000000000000
// [+] Total shares: 1398900000000000000000
let network_id = 11155111u64;
let merkle_root = EvmHash::from(hex!("d7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c"));
let merkle_root = EvmHash::from(hex!("c41c281c8404f5ce0adfca067ef8ed260f28c2cb1d7944c84ce2431936b1919e"));
let proof_size = 7;
let total_shares = 694000000000000000000;
let total_shares = 1398900000000000000000;
let network_state = NetworkShare::new(total_shares, merkle_root, proof_size);
let global_state = ShareState::new(total_shares);
@ -198,16 +198,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_1 = ClaimPackage {
index: 0,
token_id: 1,
shares: 670000000000000000000,
shares: 684900000000000000000,
merkle_proof: BoundedVec::try_from(
vec![
EvmHash::from(hex!("4dba08ba4fae6cbb0a6be6bdce81e91baf913d1857f470e97388157edd4d8d5c")),
EvmHash::from(hex!("7c1a4d2ebe6a500171b0c0f6c624898261da62d09c132106c4acec277660bd01")),
EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")),
EvmHash::from(hex!("86d599b297ed8fb56476884b43502e15e7b207835f9a62bd07b4f3338ac63b44")),
EvmHash::from(hex!("85ec1f887b1a32e76cd1b70de6a5b1900045efd7dfe3b218d3b6cef672735da6")),
EvmHash::from(hex!("1e8a291eb2ba08696ca482c7869cab5ad360da4e4df55589adf40b08b020cc5b")),
EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
]
).unwrap(),
};
@ -226,16 +226,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_2 = ClaimPackage {
index: 2,
token_id: 3,
shares: 8000000000000000000,
shares: 14900000000000000000,
merkle_proof: BoundedVec::try_from(
vec![
EvmHash::from(hex!("a8bbe4e0578964e3e6c40b93380967d69b7a1fd0358bffe789473892ec82872d")),
EvmHash::from(hex!("9b771d13977930979a26632d74e350885d5750b00646a5b03114b5815afb9f2f")),
EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")),
EvmHash::from(hex!("547eb6ce3345e183448c61ee30e4eca0523537114e8d3971894196598651040e")),
EvmHash::from(hex!("9e9936d6cd248898747a8d29d2ef1b02ca06c40583873fd7c3a7ddd1554f5472")),
EvmHash::from(hex!("1e8a291eb2ba08696ca482c7869cab5ad360da4e4df55589adf40b08b020cc5b")),
EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
]
).unwrap(),
};
@ -254,16 +254,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_3 = ClaimPackage {
index: 6,
token_id: 7,
shares: 4000000000000000000,
shares: 10900000000000000000,
merkle_proof: BoundedVec::try_from(
vec![
EvmHash::from(hex!("d28eec1544597d9e050c88a7a84535be82caf9b9e0b0c9b59b39283e7885a0d1")),
EvmHash::from(hex!("6e939188f98c3ed00931e66fdfc220c036b98edc54a212f5b18e2d3776c7c22f")),
EvmHash::from(hex!("4141baa2d892b0111bbb102078bd3ad5331c74f176b228c588741c1c306cb67d")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4"))
EvmHash::from(hex!("441f13d353fe0224fe4ab418fe56c66287e0ba1e5e562f56a4b7195ca3d9f741")),
EvmHash::from(hex!("17f9ae41fbcbbfc35accf1e4e132a905b0a95e68e085757ae8304b6a0554e908")),
EvmHash::from(hex!("a6f6783441ed0d4c5f1da8d1f026c037e15706c4ecb4c400216d485105ec9596")),
EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
]
).unwrap(),
};
@ -282,16 +282,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_4 = ClaimPackage {
index: 35,
token_id: 36,
shares: 4000000000000000000,
shares: 10900000000000000000,
merkle_proof: BoundedVec::try_from(
vec![
EvmHash::from(hex!("6b405422bcab22b7c28a9b429096e31d664522154856efb0d7b16485080b2e2a")),
EvmHash::from(hex!("620bb1858eaf6c6bb543fcf5f4b0d0b5adfbb95506403038eb153f075a00f522")),
EvmHash::from(hex!("e0b7058849ff216808963201ad2336f5587ff02eff08aceddb4764cc785413fd")),
EvmHash::from(hex!("e23325a502fe4248c8e049400a92a1c655689499c129b0ce6be3fad5d4a12dc1")),
EvmHash::from(hex!("0cde8f646452003d6c8ff1fd265ea41cd3af2e733e63ec25a9dfc82ddf41e968")),
EvmHash::from(hex!("c012942dae89ee6d1e1e44e876ae29b9421aaf0597dd2345f07a1439a98e0cbd")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4"))
EvmHash::from(hex!("5ddb291d62d02afab17cee218f2f367d612736b0f1ae59d2d28aac811e2057f3")),
EvmHash::from(hex!("d990cb37076db084b1621bc8327e0f740e242da4c463ea3a0457a6483c07fdbf")),
EvmHash::from(hex!("7bf41c14904c75e189893d05cf99c55516a194882463980cf08468c3bfd7dd8a")),
EvmHash::from(hex!("1c4c8136fe9e6dc08dc1c69f2da707a271ae510cdb5af6051b5222f2812edee0")),
EvmHash::from(hex!("3ec8d2443fd6f9decef06bf930e37cd37d78bf1c6004dc84b9ca1199712a82f8")),
EvmHash::from(hex!("1951c00e48f734ba95daeb3e8f6a2d971cc6fb0afac471ae39a21013f5756794")),
EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
]
).unwrap(),
};

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_governance`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -65,11 +65,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `283`
// Estimated: `3748`
// Minimum execution time: 216_605_000 picoseconds.
Weight::from_parts(217_612_486, 0)
// Minimum execution time: 214_208_000 picoseconds.
Weight::from_parts(216_987_870, 0)
.saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 23_409
.saturating_add(Weight::from_parts(1_544_250, 0).saturating_mul(p.into()))
// Standard Error: 30_856
.saturating_add(Weight::from_parts(1_702_104, 0).saturating_mul(p.into()))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(4))
}

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-helpers"
version = "0.0.6"
version = "0.0.11"
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
license.workspace = true
authors.workspace = true

View File

@ -34,20 +34,18 @@ pub type NetworkIdU32 = u32;
pub type NetworkIdU64 = u64;
pub type NetworkIdU128 = u128;
pub fn get_byzantium_threshold<I>(value: I) -> I
pub fn get_byzantium_quorum_threshold<I>(value: I) -> I
where
I: num_traits::PrimInt,
{
let zero = I::zero();
let one = I::one();
let two = one + one;
let three = two + one;
let three = one + one + one;
if value == zero {
return zero;
}
if value == zero { return zero; }
(value - one) / three * two + one
let f = (value - one) / three;
value - f
}
pub struct SubstrateBlake2Hasher;
@ -97,3 +95,43 @@ impl GhostHasher for UtxoSha2Hasher {
H256::zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bft_threshold_standard_paper_cases() {
// Lower boundaries (f increases here)
assert_eq!(get_byzantium_quorum_threshold(1u32), 1);
assert_eq!(get_byzantium_quorum_threshold(4u32), 3);
assert_eq!(get_byzantium_quorum_threshold(7u32), 5);
assert_eq!(get_byzantium_quorum_threshold(10u32), 7);
// Upper boundaries (max nodes for a given 'f')
assert_eq!(get_byzantium_quorum_threshold(3u32), 3);
assert_eq!(get_byzantium_quorum_threshold(6u32), 5);
assert_eq!(get_byzantium_quorum_threshold(9u32), 7);
}
#[test]
fn test_bft_threshold_zero_nodes() {
assert_eq!(get_byzantium_quorum_threshold(0u32), 0);
}
#[test]
fn test_bft_threshold_integer_boundaries() {
let max_val = usize::MAX;
let expected_f = (max_val - 1) / 3;
let expected_honest = max_val - expected_f;
assert_eq!(get_byzantium_quorum_threshold(max_val), expected_honest);
}
#[test]
fn test_bft_threshold_different_types() {
assert_eq!(get_byzantium_quorum_threshold(4u8), 3u8);
assert_eq!(get_byzantium_quorum_threshold(4u64), 3u64);
assert_eq!(get_byzantium_quorum_threshold(4usize), 3usize);
}
}

View File

@ -3,6 +3,29 @@ use sp_arithmetic::traits::AtLeast8BitUnsigned;
use sp_runtime::traits::UniqueSaturatedInto;
use sp_std::vec::Vec;
const LEAF_PREFIX: u8 = 0;
const NODE_PREFIX: u8 = 1;
fn leaf_prefixed<H>(preimage: &[u8]) -> H::Hash
where
H: GhostHasher,
{
let mut prefixed_data = sp_std::vec![LEAF_PREFIX; 1 + preimage.len()];
prefixed_data[1..].copy_from_slice(preimage);
H::hash(&prefixed_data)
}
fn node_prefixed<H>(preimage1: &[u8], preimage2: &[u8]) -> H::Hash
where
H: GhostHasher,
{
let total_len = preimage1.len() + preimage2.len();
let mut prefixed_data = sp_std::vec![NODE_PREFIX; 1 + total_len];
prefixed_data[1..1 + preimage1.len()].copy_from_slice(preimage1);
prefixed_data[1 + preimage1.len()..].copy_from_slice(preimage2);
H::hash(&prefixed_data)
}
pub fn generate_tree<H, I, S, F, E>(
max_index: I,
raw_values: S,
@ -17,30 +40,28 @@ where
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let padded_leaves = num_of_leaves.next_power_of_two();
let total_capacity = (2 * padded_leaves) - 1;
let mut merkle_tree = sp_std::vec![H::empty(); total_capacity];
let empty_hash = H::empty();
let empty_leaf_hash = leaf_prefixed::<H>(empty_hash.as_ref());
let mut merkle_tree = sp_std::vec![empty_leaf_hash; total_capacity];
for item in raw_values.into_iter() {
let (index, preimage) = generate_preimage(item)?;
merkle_tree[index] = H::hash(&preimage);
if index >= padded_leaves { return Ok(sp_std::vec![]); }
merkle_tree[index] = leaf_prefixed::<H>(&preimage)
}
let mut layer_start = 0;
let mut current_layer_len = padded_leaves;
let mut write_ptr = padded_leaves;
let hash_len = H::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
while current_layer_len > 1 {
for i in (0..current_layer_len).step_by(2) {
let layer_index = layer_start + i;
let left_bytes = merkle_tree[layer_index].as_ref();
let right_bytes = merkle_tree[layer_index | 1].as_ref();
combined[..hash_len].copy_from_slice(left_bytes);
combined[hash_len..].copy_from_slice(right_bytes);
merkle_tree[write_ptr] = H::hash(&combined);
merkle_tree[write_ptr] = node_prefixed::<H>(left_bytes, right_bytes);
write_ptr += 1;
}
layer_start += current_layer_len;
@ -50,11 +71,17 @@ where
Ok(merkle_tree)
}
pub fn generate_proof<H, I>(merkle_tree: &[H::Hash], max_index: I, index: I) -> Vec<H::Hash>
pub fn generate_proof<H, I>(
merkle_tree: &[H::Hash],
max_index: I,
index: I,
) -> Vec<H::Hash>
where
H: GhostHasher,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>,
{
if index > max_index { return sp_std::vec![]; }
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let mut current_index: usize = index.unique_saturated_into();
let mut current_layer_len = num_of_leaves.next_power_of_two();
@ -87,29 +114,21 @@ where
H: GhostHasher,
I: PartialOrd + UniqueSaturatedInto<usize>,
{
let mut current_hash = H::hash(preimage);
let mut current_index: usize = index.unique_saturated_into();
if current_index >= (1 << merkle_proof.len()) { return false; }
if current_index >= (1 << merkle_proof.len()) {
return false;
}
let hash_len = H::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
let mut current_hash = leaf_prefixed::<H>(preimage);
for sibling in merkle_proof.iter() {
let sibling_bytes = sibling.as_ref();
let hash_bytes = current_hash.as_ref();
if current_index % 2 == 0 {
combined[..hash_len].copy_from_slice(hash_bytes);
combined[hash_len..].copy_from_slice(sibling_bytes);
current_hash = node_prefixed::<H>(hash_bytes, sibling_bytes);
} else {
combined[..hash_len].copy_from_slice(sibling_bytes);
combined[hash_len..].copy_from_slice(hash_bytes);
current_hash = node_prefixed::<H>(sibling_bytes, hash_bytes);
}
current_hash = H::hash(&combined);
current_index >>= 1;
}
@ -157,24 +176,27 @@ mod tests {
assert_eq!(tree.len(), 7);
let h0 = TestBlake2Hasher::hash(&vec![1]);
let h1 = TestBlake2Hasher::hash(&vec![2]);
let h2 = TestBlake2Hasher::hash(&vec![3]);
let h3 = TestBlake2Hasher::hash(&vec![4]);
let h0 = TestBlake2Hasher::hash(&[ &[0u8], [1].as_slice() ].concat());
let h1 = TestBlake2Hasher::hash(&[ &[0u8], [2].as_slice() ].concat());
let h2 = TestBlake2Hasher::hash(&[ &[0u8], [3].as_slice() ].concat());
let h3 = TestBlake2Hasher::hash(&[ &[0u8], [4].as_slice() ].concat());
let mut c1 = [0u8; 64];
c1[..32].copy_from_slice(h0.as_ref());
c1[32..].copy_from_slice(h1.as_ref());
let mut c1 = [0u8; 65];
c1[0] = NODE_PREFIX;
c1[1..33].copy_from_slice(h0.as_ref());
c1[33..].copy_from_slice(h1.as_ref());
let parent_left = TestBlake2Hasher::hash(&c1);
let mut c2 = [0u8; 64];
c2[..32].copy_from_slice(h2.as_ref());
c2[32..].copy_from_slice(h3.as_ref());
let mut c2 = [0u8; 65];
c2[0] = NODE_PREFIX;
c2[1..33].copy_from_slice(h2.as_ref());
c2[33..].copy_from_slice(h3.as_ref());
let parent_right = TestBlake2Hasher::hash(&c2);
let mut c_root = [0u8; 64];
c_root[..32].copy_from_slice(parent_left.as_ref());
c_root[32..].copy_from_slice(parent_right.as_ref());
let mut c_root = [0u8; 65];
c_root[0] = NODE_PREFIX;
c_root[1..33].copy_from_slice(parent_left.as_ref());
c_root[33..].copy_from_slice(parent_right.as_ref());
let expected_root = TestBlake2Hasher::hash(&c_root);
assert_eq!(tree.last().unwrap(), &expected_root);
@ -196,17 +218,23 @@ mod tests {
)
.unwrap();
let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data);
assert_eq!(tree.len(), 7);
assert_eq!(tree[3], H256::zero());
assert_eq!(tree[3], empty_hash);
let h2 = TestBlake2Hasher::hash(&vec![3]);
let h3_empty = H256::zero();
let mut leaf2_data = sp_std::vec![LEAF_PREFIX];
leaf2_data.extend_from_slice(&[3]);
let h2 = TestBlake2Hasher::hash(&leaf2_data);
let mut combined_right = [0u8; 65];
combined_right[0] = NODE_PREFIX;
combined_right[1..33].copy_from_slice(h2.as_ref());
combined_right[33..].copy_from_slice(empty_hash.as_ref());
let mut combined_right = [0u8; 64];
combined_right[..32].copy_from_slice(h2.as_ref());
combined_right[32..].copy_from_slice(h3_empty.as_ref());
let expected_parent_right = TestBlake2Hasher::hash(&combined_right);
assert_eq!(tree[5], expected_parent_right);
}
@ -251,7 +279,7 @@ mod tests {
.unwrap();
assert_eq!(tree.len(), 1);
assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![42]));
assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![LEAF_PREFIX, 42]));
}
#[test]
@ -375,7 +403,11 @@ mod tests {
.unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2);
assert_eq!(proof[0], H256::zero());
let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data);
assert_eq!(proof[0], empty_hash);
let root = *tree.last().unwrap();
let is_valid =

View File

@ -175,27 +175,43 @@ pub struct NetworkClaim<NetworkId, Balance> {
pub proof_size: u32
}
impl<NetworkId, Balance> NetworkClaim<NetworkId, Balance> {
#[derive(Default)]
pub struct NetworkClaimBuilder<NetworkId, Balance> {
network_id: Option<NetworkId>,
merkle_root: Option<H256>,
amount: Option<Balance>,
proof_size: Option<u32>
}
impl<NetworkId, Balance> NetworkClaimBuilder<NetworkId, Balance> {
pub fn with_amount(mut self, amount: Balance) -> Self {
self.amount = amount;
self.amount = Some(amount);
self
}
pub fn with_network_id(mut self, network_id: NetworkId) -> Self {
self.network_id = network_id;
self.network_id = Some(network_id);
self
}
pub fn with_merkle_root(mut self, merkle_root: H256) -> Self {
self.merkle_root = merkle_root;
self.merkle_root = Some(merkle_root);
self
}
pub fn with_proof_size(mut self, proof_size: u32) -> Self {
self.proof_size = proof_size;
self.proof_size = Some(proof_size);
self
}
pub fn build(self) -> NetworkClaim<NetworkId, Balance> {
NetworkClaim {
network_id: self.network_id.expect("NetworkClaim Error: missing network_id"),
amount: self.amount.expect("NetworkClaim Error: missing amount"),
merkle_root: self.merkle_root.expect("NetworkClaim Error: merkle_root"),
proof_size: self.proof_size.expect("NetworkClaim Error: missing proof_size"),
}
}
}
#[derive(
@ -218,6 +234,7 @@ pub struct NetworkData {
pub r#type: NetworkType,
pub curve: NetworkCurve,
pub decimals: u8,
pub finality_delay: u64,
pub rate_limit_delay: u64,
@ -235,6 +252,7 @@ pub struct NetworkDataBuilder {
pub r#type: NetworkType,
pub curve: NetworkCurve,
pub decimals: u8,
pub finality_delay: u64,
pub rate_limit_delay: u64,
@ -265,6 +283,11 @@ impl NetworkDataBuilder {
self
}
pub fn with_decimals(mut self, decimals: u8) -> Self {
self.decimals = decimals;
self
}
pub fn with_finality_delay(mut self, finality_delay: u64) -> Self {
self.finality_delay = finality_delay;
self
@ -323,6 +346,7 @@ impl NetworkDataBuilder {
r#type: self.r#type,
curve: self.curve,
decimals: self.decimals,
finality_delay: self.finality_delay,
rate_limit_delay: self.rate_limit_delay,

View File

@ -3,11 +3,10 @@ use sp_std::vec::Vec;
const MAX_INPUTS: usize = 3;
const MAX_OUTPUTS: usize = 4;
const MAX_SCRIPT_LEN: usize = 34;
const MAX_WITNESS_STACK_DEPTH: usize = 3;
const MAX_WITNESS_ITEMS_LEN: usize = 73;
const MAX_SCRIPT_LEN: usize = 512;
pub const MAX_TX_LEN: usize = 4096;
pub const MAX_TX_LEN: usize = 512;
type Prefix = [u8; 7];
pub struct TxIn<H: GhostHasher> {
pub prev_txid: H::Hash,
@ -21,24 +20,11 @@ pub struct TxOut {
pub script_pubkey: Vec<u8>,
}
pub struct Witness {
pub stack: Vec<Vec<u8>>,
}
pub enum Transaction<H: GhostHasher> {
Legacy {
pub struct Transaction<H: GhostHasher> {
version: i32,
inputs: Vec<TxIn<H>>,
outputs: Vec<TxOut>,
lock_time: u32,
},
SegWit {
version: i32,
inputs: Vec<TxIn<H>>,
outputs: Vec<TxOut>,
witnesses: Vec<Witness>,
lock_time: u32,
},
}
pub struct Header<H: GhostHasher> {
@ -163,24 +149,8 @@ where
H: GhostHasher<HashBytes = [u8; 32]>,
{
pub fn compute_txid(&self) -> Option<H::Hash> {
let (version, inputs, outputs, lock_time) = match self {
Self::Legacy {
version,
inputs,
outputs,
lock_time,
} => (version, inputs, outputs, lock_time),
Self::SegWit {
version,
inputs,
outputs,
lock_time,
..
} => (version, inputs, outputs, lock_time),
};
let version_bytes = version.to_le_bytes();
let lock_time_bytes = lock_time.to_le_bytes();
let version_bytes = self.version.to_le_bytes();
let lock_time_bytes = self.lock_time.to_le_bytes();
let mut tx_buffer = [0u8; MAX_TX_LEN];
let mut cursor = 0;
@ -188,11 +158,14 @@ where
tx_buffer[cursor..cursor + 4].copy_from_slice(&version_bytes);
cursor += 4;
cursor = Self::serialize_inputs_and_outputs(&mut tx_buffer, cursor, inputs, outputs)?;
cursor = Self::serialize_inputs_and_outputs(
&mut tx_buffer,
cursor,
&self.inputs,
&self.outputs,
)?;
if cursor + 4 > MAX_TX_LEN {
return None;
}
if cursor + 4 > MAX_TX_LEN { return None; }
tx_buffer[cursor..cursor + 4].copy_from_slice(&lock_time_bytes);
cursor += 4;
@ -267,29 +240,21 @@ where
let mut cursor = cursor;
if value < 253 {
if cursor + 1 > MAX_TX_LEN {
return None;
}
if cursor + 1 > MAX_TX_LEN { return None; }
buffer[cursor] = value as u8;
cursor += 1;
} else if value <= 0xffff {
if cursor + 3 > MAX_TX_LEN {
return None;
}
if cursor + 3 > MAX_TX_LEN { return None; }
buffer[cursor] = 253;
buffer[cursor + 1..cursor + 3].copy_from_slice(&(value as u16).to_le_bytes());
cursor += 3;
} else if value <= 0xffffffff {
if cursor + 5 > MAX_TX_LEN {
return None;
}
if cursor + 5 > MAX_TX_LEN { return None; }
buffer[cursor] = 254;
buffer[cursor + 1..cursor + 5].copy_from_slice(&(value as u32).to_le_bytes());
cursor += 5;
} else {
if cursor + 9 > MAX_TX_LEN {
return None;
}
if cursor + 9 > MAX_TX_LEN { return None; }
buffer[cursor] = 255;
buffer[cursor + 1..cursor + 9].copy_from_slice(&value.to_le_bytes());
cursor += 9;
@ -298,141 +263,73 @@ where
Some(cursor)
}
pub fn validate<I, A>(
pub fn parse_receiver_and_amount(
&self,
o_index_i: I,
w_index_i: I,
destination: &[u8],
expected_amount_a: A,
) -> Option<[u8; 32]>
where
I: sp_runtime::traits::UniqueSaturatedInto<usize>,
A: sp_runtime::traits::UniqueSaturatedInto<u128>,
{
let o_index: usize = o_index_i.unique_saturated_into();
let w_index: usize = w_index_i.unique_saturated_into();
prefix: &Prefix,
) -> Option<([u8; 32], u64)> {
let mut maybe_receiver = None;
let mut op_return_count = 0;
let mut total_amount: u64 = 0;
let expected_amount_u128: u128 = expected_amount_a.unique_saturated_into();
let expected_amount: u64 = expected_amount_u128.try_into().ok()?;
for output in self.outputs.iter() {
let script = &output.script_pubkey;
match self {
Self::Legacy { outputs, .. } => {
let output = outputs.get(o_index)?;
if expected_amount != output.value {
return None;
}
if output.script_pubkey.len() != 57 {
return None;
}
let slice = &output.script_pubkey;
let receiver_address = self.parse_receiver(slice)?;
if receiver_address != destination {
return None;
if let Some(receiver) = self.parse_receiver(script, prefix) {
op_return_count += 1;
maybe_receiver = Some(receiver);
}
let mut extra_data = [0u8; 32];
extra_data.copy_from_slice(&slice[25..57]);
Some(extra_data)
}
Self::SegWit {
witnesses, outputs, ..
} => {
let output = outputs.get(o_index)?;
if expected_amount != output.value {
return None;
}
let receiver_address = self.parse_receiver(&output.script_pubkey)?;
if receiver_address != destination {
return None;
}
let witness = witnesses.get(w_index)?;
let target_account: [u8; 32];
// NOTE: change it right after the real bridging tx
// will take place, or find something similar
#[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
{
let _ = witness.stack;
target_account = [105u8; 32]; // 0x69 in decimal
}
#[cfg(not(any(feature = "runtime-benchmarks", feature = "std")))]
{
if witness.stack.len() < 3 || witness.stack[2].len() != 32 {
return None;
}
target_account = witness.stack[2][..32].try_into().ok()?;
}
Some(target_account)
}
if script == &destination {
total_amount = total_amount.checked_add(output.value)?;
}
}
fn parse_receiver(&self, script: &[u8]) -> Option<[u8; 32]> {
if total_amount > 0 && op_return_count == 1 {
maybe_receiver.map(|receiver| (receiver, total_amount))
} else {
None
}
}
pub fn convert_into_p2pkh(hash: &[u8]) -> Option<[u8; 25]> {
if hash.len() != 20 { return None; }
let mut script = [0u8; 25];
script[0] = 0x76; // OP_DUP
script[1] = 0xa9; // OP_HASH160
script[2] = 0x14; // OP_PUSHBYTES_20
script[3..23].copy_from_slice(hash);
script[23] = 0x88; // OP_EQUALVERIFY
script[24] = 0xac; // OP_CHECKSIG
Some(script)
}
pub fn convert_into_p2tr(pubkey: &[u8]) -> Option<[u8; 34]> {
if pubkey.len() != 32 { return None; }
let mut script = [0u8; 34];
script[0] = 0x51; // OP_1 (Taproot indicator)
script[1] = 0x20; // OP_PUSHBYTES_32
script[2..34].copy_from_slice(pubkey);
Some(script)
}
fn parse_receiver(&self, script: &[u8], prefix: &Prefix) -> Option<[u8; 32]> {
// Standard OP_RETURN with 32 bytes of data:
// script[0] = 0x6a (OP_RETURN)
// script[1] = 0x27 (OP_PUSHBYTES_39)
// script[2..9] = 7 bytes for prefix
// script[9..41] = 32 bytes of account
if script.len() == 41 && script[0] == 0x6a && script[1] == 0x27 {
if &script[2..9] == prefix.as_ref() {
let mut receiver = [0u8; 32];
// 1. Parse Pay-to-Taproot (P2TR / SegWit v1)
// Markers: OP_1 (0x51) + OP_PUSHBYTES_32 (0x20) + 32 bytes key
if script.len() == 34 && script[0] == 0x51 && script[1] == 0x20 {
receiver.copy_from_slice(&script[2..34]);
return Some(receiver);
receiver.copy_from_slice(&script[9..41]);
return Some(receiver)
}
// 2. Parse Pay-to-Witness-Script-Hash (P2WSH / SegWit v0)
// Markers: OP_0 (0x00) + OP_PUSHBYTES_32 (0x20) + 32 bytes hash script
if script.len() == 34 && script[0] == 0x00 && script[1] == 0x20 {
receiver.copy_from_slice(&script[2..34]);
return Some(receiver);
}
// 3a. Parse Native SegWit 20-bytes key (P2WPKH) zero-padding
// Markers: 22 bytes len, starts with OP_0 (0x00), next OP_PUSHBYTES_20 (0x14)
if script.len() == 22 && script[0] == 0x00 && script[1] == 0x14 {
receiver[..20].copy_from_slice(&script[2..22]);
return Some(receiver);
}
// 3b. Parse Pay-to-Script-Hash (P2SH)
// Script: OP_HASH160 (0xa9) OP_PUSHBYTES_20 (0x14) + 20 bytes hash + OP_EQUAL (0x87)
if script.len() == 23 && script[0] == 0xa9 && script[1] == 0x14 && script[22] == 0x87 {
receiver[..20].copy_from_slice(&script[2..22]);
return Some(receiver);
}
// 4. Parse Legacy (P2PKH) zero-padding
// Script: OP_DUP(0x76) OP_HASH160(0xa9) OP_PUSHBYTES_20(0x14) +
// 20 bytes hash + OP_EQUALVERIFY(0x88) OP_CHECKSIG(0xac)
if script.len() == 25
&& script[0] == 0x76
&& script[1] == 0xa9
&& script[2] == 0x14
&& script[23] == 0x88
&& script[24] == 0xac
{
receiver[..20].copy_from_slice(&script[3..23]);
return Some(receiver);
}
// 5. Custom one: Legacy (P2PKH) + 32 bytes
// Script: [0..25] — basic P2PKH, [25..57] — 32 bytes of extra data
if script.len() == 57
&& script[0] == 0x76 // OP_DUP
&& script[1] == 0xa9 // OP_HASH160
&& script[2] == 0x14 // OP_PUSHBYTES_20
&& script[23] == 0x88 // OP_EQUALVERIFY
&& script[24] == 0xac
// OP_CHECKSIG
{
receiver[..20].copy_from_slice(&script[3..23]);
return Some(receiver);
}
None
@ -520,11 +417,6 @@ impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Transaction<H> {
);
bytes = &bytes[4..];
let is_segwit = bytes.len() >= 2 && bytes[0] == 0x00 && bytes[1] == 0x01;
if is_segwit {
bytes = &bytes[2..];
}
let input_count = read_varint(&mut bytes)?;
if input_count > MAX_INPUTS {
return Err(ParseError::ExceededMaxLimits);
@ -615,69 +507,17 @@ impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Transaction<H> {
.filter_map(|opt| opt.take())
.collect();
if is_segwit {
let mut witnesses_buffer: [Option<Witness>; MAX_INPUTS] = [const { None }; MAX_INPUTS];
for idx in 0..input_count {
let item_count = read_varint(&mut bytes)?;
if item_count > MAX_WITNESS_STACK_DEPTH {
return Err(ParseError::ExceededMaxLimits);
}
let mut stack_buffer: [Option<Vec<u8>>; MAX_WITNESS_STACK_DEPTH] =
[const { None }; MAX_WITNESS_STACK_DEPTH];
for s_idx in 0..item_count {
let item_len = read_varint(&mut bytes)?;
if item_len > MAX_WITNESS_ITEMS_LEN {
return Err(ParseError::ExceededMaxLimits);
}
if bytes.len() < item_len {
return Err(ParseError::InvalidLength);
}
stack_buffer[s_idx] = Some(bytes[..item_len].to_vec());
bytes = &bytes[item_len..];
}
let stack: Vec<Vec<u8>> = stack_buffer[..item_count]
.iter_mut()
.filter_map(|opt| opt.take())
.collect();
witnesses_buffer[idx] = Some(Witness { stack });
}
let witnesses: Vec<Witness> = witnesses_buffer[..input_count]
.iter_mut()
.filter_map(|opt| opt.take())
.collect();
if bytes.len() < 4 {
return Err(ParseError::InvalidLength);
}
let lock_time = u32::from_le_bytes(
bytes[..4]
.try_into()
.map_err(|_| ParseError::TypeConversionFailed)?,
);
Ok(Transaction::SegWit {
version,
inputs,
outputs,
witnesses,
lock_time,
})
} else {
if bytes.len() < 4 {
return Err(ParseError::InvalidLength);
}
let lock_time = u32::from_le_bytes(
bytes[..4]
.try_into()
.map_err(|_| ParseError::TypeConversionFailed)?,
);
Ok(Transaction::Legacy {
Ok(Transaction {
version,
inputs,
outputs,
@ -685,7 +525,6 @@ impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Transaction<H> {
})
}
}
}
impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Header<H> {
type Error = ParseError;

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-networks"
version = "0.2.9"
version = "0.2.10"
description = "Registry and lifecycle management for external network metadata and cryptographic profiles."
license.workspace = true
authors.workspace = true

View File

@ -186,6 +186,18 @@ benchmarks! {
assert_ne!(GhostNetworks::<T>::networks(chain_id), prev_network);
}
update_network_decimals {
let decimals = 18;
let (chain_id, network) = prepare_network::<T>(1, 1, 1);
let authority = T::UpdateOrigin::try_successful_origin()
.map_err(|_| BenchmarkError::Weightless)?;
let prev_network = create_network::<T>(chain_id, network)?;
}: _<T::RuntimeOrigin>(authority, chain_id, decimals)
verify {
assert_last_event::<T>(Event::NetworkDecimalsUpdated { chain_id, decimals }.into());
assert_ne!(GhostNetworks::<T>::networks(chain_id), prev_network);
}
update_network_gatekeeper {
let mut raw_gatekeeper = b"0x".to_vec();
for i in 0..40 { raw_gatekeeper.push(i + 1); }

View File

@ -175,6 +175,7 @@ pub mod module {
NetworkBlockDeviationUpdated { chain_id: T::NetworkId, block_deviation: u64 },
NetworkTypeUpdated { chain_id: T::NetworkId, network_type: NetworkType },
NetworkCurveUpdated { chain_id: T::NetworkId, network_curve: NetworkCurve },
NetworkDecimalsUpdated { chain_id: T::NetworkId, decimals: u8 },
NetworkGatekeeperUpdated { chain_id: T::NetworkId },
NetworkIncomingShareUpdated { chain_id: T::NetworkId, incoming_share: u32 },
NetworkOutgoingShareUpdated { chain_id: T::NetworkId, outgoing_share: u32 },
@ -396,6 +397,17 @@ pub mod module {
T::RemoveOrigin::ensure_origin_or_root(origin)?;
Self::do_remove_network(chain_id)
}
#[pallet::call_index(12)]
#[pallet::weight(T::WeightInfo::update_network_decimals())]
pub fn update_network_decimals(
origin: OriginFor<T>,
chain_id: T::NetworkId,
decimals: u8,
) -> DispatchResult {
T::UpdateOrigin::ensure_origin_or_root(origin)?;
Self::do_update_network_decimals(chain_id, decimals)
}
}
}
@ -591,6 +603,23 @@ impl<T: Config> Pallet<T> {
Ok(())
}
/// Update existent network decimals.
pub fn do_update_network_decimals(
chain_id: T::NetworkId,
decimals: u8,
) -> DispatchResult {
Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult {
let network = maybe_network.as_mut().ok_or(Error::<T>::NetworkDoesNotExist)?;
network.decimals = decimals;
Ok(())
})?;
Self::deposit_event(Event::<T>::NetworkDecimalsUpdated {
chain_id,
decimals,
});
Ok(())
}
/// Update existent network type.
pub fn do_update_network_type(
chain_id: T::NetworkId,

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_networks`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -57,6 +57,7 @@ pub trait WeightInfo {
fn update_network_block_deviation() -> Weight;
fn update_network_type() -> Weight;
fn update_network_curve() -> Weight;
fn update_network_decimals() -> Weight;
fn update_network_gatekeeper() -> Weight;
fn update_incoming_network_share() -> Weight;
fn update_outgoing_network_share() -> Weight;
@ -79,15 +80,15 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3471`
// Minimum execution time: 60_167_000 picoseconds.
Weight::from_parts(56_015_384, 0)
// Minimum execution time: 57_039_000 picoseconds.
Weight::from_parts(53_368_140, 0)
.saturating_add(Weight::from_parts(0, 3471))
// Standard Error: 1_219
.saturating_add(Weight::from_parts(6_146, 0).saturating_mul(i.into()))
// Standard Error: 617
.saturating_add(Weight::from_parts(37_564, 0).saturating_mul(j.into()))
// Standard Error: 8_216
.saturating_add(Weight::from_parts(864_641, 0).saturating_mul(k.into()))
// Standard Error: 874
.saturating_add(Weight::from_parts(8_884, 0).saturating_mul(i.into()))
// Standard Error: 442
.saturating_add(Weight::from_parts(32_778, 0).saturating_mul(j.into()))
// Standard Error: 5_890
.saturating_add(Weight::from_parts(833_630, 0).saturating_mul(k.into()))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(4))
}
@ -95,11 +96,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_selector() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_957_000 picoseconds.
Weight::from_parts(44_541_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_680_000 picoseconds.
Weight::from_parts(43_454_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -110,11 +111,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 44_092_000 picoseconds.
Weight::from_parts(45_181_439, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Standard Error: 402
.saturating_add(Weight::from_parts(16_387, 0).saturating_mul(n.into()))
// Minimum execution time: 42_265_000 picoseconds.
Weight::from_parts(43_357_887, 0)
.saturating_add(Weight::from_parts(0, 3632))
// Standard Error: 457
.saturating_add(Weight::from_parts(13_745, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -124,9 +125,9 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_603_000 picoseconds.
Weight::from_parts(46_116_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Minimum execution time: 43_008_000 picoseconds.
Weight::from_parts(43_917_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -134,11 +135,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_rate_limit_delay() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_607_000 picoseconds.
Weight::from_parts(46_615_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 43_104_000 picoseconds.
Weight::from_parts(43_981_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -146,11 +147,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_block_deviation() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_510_000 picoseconds.
Weight::from_parts(43_840_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 41_734_000 picoseconds.
Weight::from_parts(42_645_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -158,11 +159,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_type() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_737_000 picoseconds.
Weight::from_parts(44_347_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_080_000 picoseconds.
Weight::from_parts(42_404_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -174,23 +175,35 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::NetworkCurvesVec` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn update_network_curve() -> Weight {
// Proof Size summary in bytes:
// Measured: `179`
// Estimated: `6119`
// Minimum execution time: 85_784_000 picoseconds.
Weight::from_parts(86_768_000, 0)
.saturating_add(Weight::from_parts(0, 6119))
// Measured: `180`
// Estimated: `6120`
// Minimum execution time: 82_975_000 picoseconds.
Weight::from_parts(84_583_000, 0)
.saturating_add(Weight::from_parts(0, 6120))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(4))
}
/// Storage: `GhostNetworks::Networks` (r:1 w:1)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_decimals() -> Weight {
// Proof Size summary in bytes:
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 41_602_000 picoseconds
Weight::from_parts(42_603_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
/// Storage: `GhostNetworks::Networks` (r:1 w:1)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_gatekeeper() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 44_801_000 picoseconds.
Weight::from_parts(45_240_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_643_000 picoseconds.
Weight::from_parts(43_190_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -198,11 +211,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_incoming_network_share() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_404_000 picoseconds.
Weight::from_parts(46_161_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_978_000 picoseconds.
Weight::from_parts(43_002_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -210,11 +223,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_outgoing_network_share() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_476_000 picoseconds.
Weight::from_parts(46_312_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 43_436_000 picoseconds.
Weight::from_parts(44_193_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
@ -228,11 +241,11 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::NetworkCurvesVec` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn remove_network() -> Weight {
// Proof Size summary in bytes:
// Measured: `179`
// Estimated: `3644`
// Minimum execution time: 78_480_000 picoseconds.
Weight::from_parts(79_105_000, 0)
.saturating_add(Weight::from_parts(0, 3644))
// Measured: `180`
// Estimated: `3645`
// Minimum execution time: 75_969_000 picoseconds.
Weight::from_parts(77_610_000, 0)
.saturating_add(Weight::from_parts(0, 3645))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(4))
}

View File

@ -41,6 +41,7 @@ use ghost_networks::{
NetworkData, NetworkDataBasicHandler, NetworkDataInspectHandler, NetworkDataMutateHandler,
NetworkType,
};
use ghost_traits::evictor::StakingEvictor;
use ghost_traits::exposure::ExposureListener;
pub mod migrations;
@ -309,6 +310,7 @@ pub mod pallet {
>;
type DisabledValidators: DisabledValidators;
type ExposureListener: ExposureListener<BalanceOf<Self>, Self::AccountId>;
type StakingEvictor: StakingEvictor<Self::AccountId>;
#[pallet::constant]
type MaxAuthorities: Get<u32>;
@ -1455,7 +1457,15 @@ impl<T: Config> Pallet<T> {
let authority_index = index as AuthIndex;
if offence_bitmap.exists(&authority_index) {
weight.saturating_accrue(T::DbWeight::get().reads(2));
if let Some(account_id) = T::ExposureListener::get_account_by_index(index) {
weight.saturating_accrue(T::DbWeight::get().reads(1));
if T::StakingEvictor::try_evict_validator(&account_id) {
weight.saturating_accrue(T::DbWeight::get().writes(2));
}
}
if let Some(full_id) = <T::ValidatorSet as ValidatorSetWithIdentification<
T::AccountId,
>>::IdentificationOf::convert(id.clone())
@ -1637,7 +1647,7 @@ impl<Offender: Clone> Offence<Offender> for SlowClapOffence<Offender> {
missed_percent.saturating_mul(Perbill::from_rational(1, offenders_count))
}
OffenceType::CommitmentOffence => offenders_count
.checked_sub(Perbill::from_percent(9).mul_ceil(self.validator_set_count))
.checked_sub(Perbill::from_percent(9).mul_floor(self.validator_set_count))
.map(|threshold| {
Perbill::from_rational(threshold.saturating_mul(4), self.validator_set_count)
.square()

View File

@ -5,6 +5,7 @@ use frame_support::{
traits::{ConstU32, ConstU64},
};
use frame_system::EnsureRoot;
use ghost_traits::evictor::StakingEvictor;
use pallet_session::historical as pallet_session_historical;
use sp_runtime::{
curve::PiecewiseLinear,
@ -36,6 +37,7 @@ frame_support::construct_runtime!(
);
parameter_types! {
pub static EvicatedValidators: Vec<u64> = vec![];
pub static FixedValidators: Vec<u64> = vec![0, 1, 2, 3];
}
@ -172,6 +174,25 @@ impl pallet_balances::Config for Runtime {
type Balance = u64;
pub struct TestStakingEvictor;
impl StakingEvictor<u64> for TestStakingEvictor {
fn try_evict_validator(who: &u64) -> bool {
if !FixedValidators::get().contains(who) {
return false;
}
let mut evicted_validators = EvicatedValidators::get();
match evicted_validators.iter().position(|x| x == who) {
Some(_) => false,
None => {
evicted_validators.push(*who);
EvicatedValidators::set(evicted_validators);
true
}
}
}
}
pub struct TestExposureListener;
impl ExposureListener<Balance, u64> for TestExposureListener
where
@ -217,6 +238,7 @@ impl Config for Runtime {
type ReportUnresponsiveness = OffenceHandler;
type DisabledValidators = Session;
type ExposureListener = TestExposureListener;
type StakingEvictor = TestStakingEvictor;
type MaxAuthorities = ConstU32<5>;
type ApplauseThreshold = ConstU32<500_000_000>;

View File

@ -1691,6 +1691,40 @@ fn migration_from_v2_to_v3_works_fine() {
});
}
#[test]
fn validators_are_evicted_during_offence() {
let (network_id, _, _) = generate_unique_hash(None, None, None, None, None);
new_test_ext().execute_with(|| {
let session_index = advance_session_and_get_index();
prepare_evm_network(None, None);
assert_eq!(BlockCommitments::<Runtime>::get(network_id).len(), 0);
System::set_block_number(5 * EpochDuration::get() / 2);
let last_stored_block = 69;
for i in 0..3 {
assert_ok!(do_block_commitment(
session_index,
network_id,
i,
last_stored_block,
));
}
assert_eq!(EvicatedValidators::get().len(), 0);
let current_block = SlowClap::current_block_number();
SlowClap::on_initialize(current_block);
System::assert_has_event(RuntimeEvent::SlowClap(
crate::Event::SomeAuthoritiesDelayed {
delayed: vec![(3, 3)],
},
));
assert_eq!(EvicatedValidators::get().len(), 1);
});
}
fn assert_clapped_amount(
session_index: &SessionIndex,
unique_hash: &H256,

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-traits"
version = "0.4.4"
version = "0.4.6"
description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more."
license.workspace = true
authors.workspace = true

View File

@ -0,0 +1,3 @@
pub trait StakingEvictor<AccountId> {
fn try_evict_validator(who: &AccountId) -> bool;
}

View File

@ -17,6 +17,7 @@ pub trait FlexibleRoundOptimizedSchnorrThresholdSignature<A, E>: MerkleTreeBuild
fn header_bytes_len(&self) -> usize;
fn element_bytes_len(&self) -> usize;
fn signature_bytes_len(&self) -> usize;
fn scalar_bytes_len(&self) -> usize;
fn verify_signature_share(
@ -84,7 +85,6 @@ pub trait DistributedKeyGeneration<A, E>: MerkleTreeBuilder<A, E> {
) -> Self::Part3Result;
fn dkg_derive_verifying_share(&self, sk: &[u8]) -> Result<Vec<u8>, E>;
fn dkg_decompress_key(&self, vk: &[u8]) -> Result<Vec<u8>, E>;
}
pub trait MerkleTreeBuilder<A, E>: IdentifierConverter<A, E> {

View File

@ -4,7 +4,7 @@ use sp_core::H256;
pub trait MerkleTree<A, E>: IdentifierConverter<A, E> {
type VerifyingShares;
type Hash;
type Hash: AsRef<[u8]>;
fn merkle_build_tree(vs: &Self::VerifyingShares) -> Result<Vec<Self::Hash>, E>;
fn merkle_generate_proof_from_tree(vsm: &Self::VerifyingShares, t: &[Self::Hash], i: A) -> Result<(Vec<u8>, Vec<H256>), E>;

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-weaver"
version = "0.0.4"
version = "0.0.10"
description = "Weaving a secure cryptographic tapestry across different external chains."
license.workspace = true
authors.workspace = true

View File

@ -3,13 +3,13 @@
use super::*;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
use sp_runtime::AccountId32;
use sp_std::vec;
use ghost_helpers::networks::NetworkDataBuilder;
fn prepare_pallet<T: Config>(
authorities_len: u32
authorities_len: u32,
maybe_network_id: Option<u64>,
) -> (NetworkIdOf<T>, T::AuthorityId, AuthIndex) {
let authority = T::AuthorityId::generate_pair(None);
let authorities = vec![authority.clone(); authorities_len as usize];
@ -19,12 +19,14 @@ fn prepare_pallet<T: Config>(
Authorities::<T>::put(bounded_authorities);
let network_id = NetworkIdOf::<T>::default();
let network_id_u64 = maybe_network_id.unwrap_or_default();
let network_id: NetworkIdOf<T> = network_id_u64.unique_saturated_into();
let network_data = NetworkDataBuilder::default()
.with_network_type(NetworkType::Utxo)
.with_gatekeeper(
// Transaction: https://blockstream.info/tx/c41999f86269d5a22f39e18530b065854f447ef64a48be4013c4a9faa657f418
hex::decode("c6e4e0f822c31540b4b05903ce3c67b6cff8e6d4000000000000000000000000")
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
hex::decode("4635b1794a22bfbe6c5c5eba17b693f4aaf0e348")
.expect("Bytes32 from UTXO should be valid"),
)
.build();
@ -43,7 +45,7 @@ mod benchmarks {
fn extend_warp(
a: Linear<1, { T::MaxAuthorities::get() as u32 }>,
) -> Result<(), BenchmarkError> {
let (network_id, authority, authority_index) = prepare_pallet::<T>(a);
let (network_id, authority, authority_index) = prepare_pallet::<T>(a, None);
TapestryDrafts::<T>::remove(&network_id);
@ -74,7 +76,7 @@ mod benchmarks {
let next_session = weaving_session + 1;
let root_hash = H256::repeat_byte(0x69);
let (network_id, authority, authority_index) = prepare_pallet::<T>(a);
let (network_id, authority, authority_index) = prepare_pallet::<T>(a, None);
let tapestry_key = (network_id, weaving_session, next_session, root_hash);
TapestryStrands::<T>::mutate(&tapestry_key, |tapestry| {
@ -140,204 +142,184 @@ mod benchmarks {
) -> Result<(), BenchmarkError> {
let session = 69;
let max_history_depth = a as usize;
let (network_id, _, _) = prepare_pallet::<T>(1);
let (network_id, _, _) = prepare_pallet::<T>(1, None);
// https://blockstream.info/tx/6fdcb55eaeddae87fdbbd3e6fd8c2b80c1e7e7c1214e1a6e35c06a70e5f21fba?expand
// NOTE: this is the best transaction that matches the max sizes I found,
// would be nice to find exact edge case
// Real world tx at block #481838
// Block https://blockstream.info/block/0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
let historical_hashes = vec![
hex::decode("00a01520c1d508977a3787523a0f959e14a8df763dc87927c7910000000000000000000036303f2bcf94f752a1d4b0641d1adbc2d80aa89628feefd7f837a74792e26ab8bb276a6ad43a0217bece2e64").expect("Bytes32 from UTXO should be valid"),
hex::decode("0400e020702ff95ecc1b8575cff205bfc24592c87ba09280bd84000000000000000000008ca9c40d5ab762f160661635cade50fd14571dcf5fdf8c463b617caf9b07dc5a3b296a6ad43a02171340d661").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000345457ca4006ddc376db24b7a37db04dfa0b46c1d99267010000000000000000009409d64eb06a6dd83ae2937f70d07222d75505a07f12e756b61a27189851e3bbfe2c6a6ad43a0217a06575b5").expect("Bytes32 from UTXO should be valid"),
hex::decode("004007220e37af053c56a86899e1219107937fd5e83ff61aeb7800000000000000000000c0588f8b7a697ff9f367ec4a69a9998ef8f2552f808b5025b4b8f604d0a145c6123e6a6ad43a0217a662289d").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0ff3feeae586d6f7215f48e857ed7fcf823b27117f2805fe000000000000000000000dcaa345e44c85be86da35ab0f25e2f089f04f66c1e3a3881a8455b1e42ba2500ad3e6a6ad43a0217b3810985").expect("Bytes32 from UTXO should be valid"),
hex::decode("00600520214f05f95e93d1d46c768a910fe0a15da3de556c2a7a01000000000000000000a129b7d281efd307700bff535137324aa0c5decc9829243bb7b29f1cafcbbd6c114b6a6ad43a0217cd209b45").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000df2d608f226bda092f6d8da18e92e3cbb41d5c99c7ddbf20010000000000000000009848dfe4f23c9211df920b37d571b2a4363afac3efb83d7cf4249b6491a6c8dfff4c6a6ad43a02174646c9ff").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000020c69aef55529fcbf6d18aea8a701177317483d63c32d1000000000000000000000c3551d009eb18b1cc5da8420c5e1268c1de482127623b11f74743d46f3f34ebf84e6a6ad43a021796234549").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800820d50e734c76e83a3d2658d1977034372b68362927c06b010000000000000000007b701acfc56d4ef6086eda92cde2c809d6960dca65ec9e617d53e0761f620bb72e506a6ad43a0217416f2f2b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00009f23d562db91d625b878e92f5d410c04775ed88006a18a0300000000000000000000a7b1bbad02565047b4428e2ef0cf3acbd0714c77934e44abc367451b85c3e56be3556a6ad43a0217c3b1aebd").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000003e8c4ec35b8fd1f07d6ff39de67c1ade69a25df69d84030100000000000000000033d4dab1c4958ae6d9cb853278362cbf74d25c1c3c7150889883c7fbdfa5004506566a6ad43a021751a2160f").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a00020dfde782b9446c17ae40a86dcb2e308d4c729a46c162500000000000000000000cadaf134229eba7775042b3d8689553ba93d0ea929f411d3a1a01693b923b1bf22586a6ad43a02172393ef65").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800c204ec04712a453ed72818b60cb9f74931cc5a59bbcac0d0100000000000000000059eb7a758ff3809a2a5010a3e849201314378120b02b6386490c8b11ad56be308d5c6a6ad43a02177bc26d48").expect("Bytes32 from UTXO should be valid"),
hex::decode("00801935e8c63aa1065f46a7d31bc5063be04108bdd3b18dd326020000000000000000009a3cfbb7b79d1d51f11ebd0a28ed2bf29e53d36ccefddff12322ca47d00665076c5d6a6ad43a0217dc5d92d8").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000003644663bcc00a79e38e833e4a16805ae72cc373cb7e51c01000000000000000000a33b3c0378b2a2d213aac062c7cd8b2c2b3a9c11aaeb4824a037863e7bd5fe7701636a6ad43a02173b36822e").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002a4a1da985bd4aa15be31a03fd3efbe77ad0ebabc392ff0100000000000000000077eb14d0e30b97aa2baff829fa7754de88a359e6a3711300f4e2e0c1ad1435829d6b6a6ad43a0217c8ef1999").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000024a4efb58211a7c804ef24ee11f5f9f9db2509be9bc5da010000000000000000001cc7ac2fe2e38bfd4582367690b0521e71c30f0083f68565fd948ad4ee124fb91b6c6a6ad43a0217851e16c0").expect("Bytes32 from UTXO should be valid"),
hex::decode("0060e729537b191715a6f594c55e85574253c84580909a2fa72200000000000000000000fa2959118ac66e3569081e4645f87f9560742290b52ee54f7886b6fa5f83aa3d016d6a6ad43a021708a2ad64").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3f6fe5befcf9e4c77cf03673076ff822740622f75cd74e010000000000000000008da0cd1fb34cfd4e58b5d3f1e20f6104e491320d5dc109fe16a455e3d385adb90d6f6a6ad43a02175c26ad30").expect("Bytes32 from UTXO should be valid"),
hex::decode("00204120cd2dba55f0867cff62ee5bab27b0ba85ac58c869f55c00000000000000000000a685eaec339dbb6e4fa52dacd2752291d5bb55dceb0b8b359e5ecbc0b2d1f884506f6a6ad43a0217de2f0c8c").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e01420a16b828d829748aa9602930d3ef1d4c983b86b39a20101000000000000000000d6e8aecebb3748113be25a6522f47e10e63c57fe95a09f35052fdabcc8a68ad819736a6ad43a021729b35edd").expect("Bytes32 from UTXO should be valid"),
hex::decode("008067269f4c1aaf3129211de19714c6a5bb9c5481ea34f044da00000000000000000000734b07e26a80d3345fa74d1450a1b68d95e0714c163dc7b2b3eb9a0aef8b50c934756a6ad43a02172eba7608").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0fc20ed544115339aa1a59249f02f62856feb602ea083f93300000000000000000000d30c9e1e276efd3be62b7cd7194aa7fb0b807f2fecb9a56074247887b2299882f5756a6ad43a021702a2b823").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000202d2c60af64f5b2a415bc46bca915042d5136b46929c501000000000000000000e0e3b49cd8b926a7468831c1b115e14838640cb735833561d551a804cf1a752cc3766a6ad43a0217d81e878d").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3f3c41153e725b51dc793b18c0bfd9cfaf14cd091e000402000000000000000000ff9b55b7ed1dcff73f458e48dd2799ae141829586ce0a3902a783f00981c798bff776a6ad43a021700849f4b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00200f237376e2f7ed32d9e0818617150255cc5c87bfc43d182c000000000000000000003c08d2ba07dda561bcb53f56fff0780c5d1046686c363743b17e1591de293a8538776a6ad43a02176fda963f").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800020f3ac5bb512764387873548d1cc0d4fef3b70077b6e700100000000000000000035accf350c72501ce6aedd556177cd99090cb4952c7b23fa424ac1f7a282c0315a786a6ad43a02174cf87729").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3fcee6362f1a42288aa20c3f21e9fcde46dcc1a9402d19010000000000000000006e40bfc29eb8859f30d083cefc45482f77946dcb429bcf1e064d1bcbd378f23c0c7b6a6ad43a02173d75f866").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002ae9ef834e71842c871391dd44a09960dd63990dd557e30100000000000000000052b6b1f008e5b0a5f38be6b2082ec17889619f0627f4064106e820364e1cc8072f7a6a6ad43a021740becfc5").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a0ab238496ac097daf6fee7acdd40ef1a503089d76db2256c400000000000000000000a6e564e0de9af79eb797a4b9afb26b5ad56395f3cbb7a9b4bde5a760d140aae8ba7a6a6ad43a021735b5f7f8").expect("Bytes32 from UTXO should be valid"),
hex::decode("00200320e3d603f57832afb5e67c782aa5fd5d8e2d655230b50e020000000000000000005451ec2c708395b414a347863a8cb7ee4a7d8b0309086af4c329c3ba164be5930d7c6a6ad43a02171ffe7b21").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000720159f0a42d5afd3196d6bc4745786b08017e9f2fe65c4010000000000000000002204634460a10b17be10462c08d3a3247e8955da5ec01f525fd0d58b14a5e2ebab826a6ad43a021737c8b684").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000022030d1a71c70fe8d8e994e1dcddcda8c8c5798ea9bb20d000000000000000000005d5579d7b5cc4c5e43d45169c93abf90dc6f212cd462407a48fc144d8315efb35b836a6ad43a0217aeaa2b47").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000222a3e9205528e4d5618a7287d3d2370a41a6cd2d346fb0000000000000000000016d19ca5aad54bceea5245a221fddbaedb3f56fd6b8f12d59af65a167a7ab56aee836a6ad43a02173c540bc8").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0ff3f9ddcd3ff85fdcd54b32df85a1eb8648b211b72a247870000000000000000000043b56016ddc1c29bd76c92a831b4de36a59cc0fb88d294b6faf24339afd8d64de6886a6ad43a0217126de83e").expect("Bytes32 from UTXO should be valid"),
hex::decode("0060c424aa0dedbd17459541b2df58d403e78824d0b306e13888000000000000000000000d05d08f6044eb00963044e75b4dc14417fd7c3acf1f6d2f9eaf9432627d6a93c68a6a6ad43a0217971b219c").expect("Bytes32 from UTXO should be valid"),
hex::decode("00006c2235ade56a30b832a1d86a389853d9d7ef251dcd31d5de0000000000000000000028cbac80a576b4b03407029062c64268eae619201f84de43d65b6cc29d49945d598b6a6ad43a02178216ff0c").expect("Bytes32 from UTXO should be valid"),
hex::decode("0060c924fee183b290d1e5ba0dd2a211888cb9a45ac3261128ca01000000000000000000c64693de6d95445e23df797f3591719428ea07754aad0c5c03707a1696a49316e48c6a6ad43a02172c70405e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c09c217b4715e06ee536c88f2b04f8da84c9be46bb89b337cb0000000000000000000035ed0514da32bff54f234681d567b7bb8bebd48c1981e88a480a0069cf19fae9318f6a6ad43a0217578cf0e8").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c00f2089ef5721cd6bfe541cf56a30fe029e8b6a99d426b59b010000000000000000001cc5db1b35955722b2b072be5497665f2bcc0116021fc52ad106b86f85eb7a15ea8f6a6ad43a021726299c25").expect("Bytes32 from UTXO should be valid"),
hex::decode("00400a20b73d674783455e1728c82701ae1c5bab268563697aa001000000000000000000c7ef430a886118fb6bdd0536e867b9b6fc15cb198c7df4f4669c2fb94244445ebc916a6ad43a021757775d4f").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0012028d9ad98ee6302bffc27d2fc51e905e7ff1455e9ad9a000000000000000000005334bd5deb896d99d5f85f762ca5b5057bd8a11fb1f718f794ed67525ce5275639926a6ad43a021742a4ad22").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0982497ed444856620be9d4a2b3f2dd8385aef13794945fc4000000000000000000007a27f7d0b1a20ec5e2f5b69edb844ceb52acbe24cc44b1e486a86105591678c846926a6ad43a02176d3f1083").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800420a90444f33700b0a903d8dd28c7e4b00f1784a4f12c0801000000000000000000068f9295a349845eb2431428cfc318b72eff6d8b828b61533e5b9889be6a455f69926a6ad43a02176536c20e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0ff3f9efdf00d718c728b94e39dde5c42b0450539b1e0d20a02000000000000000000fff04185f99913ff903e7fff46f04e0ae0f132677d10b8fa1537b98be859020c3f946a6ad43a021759b8976c").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002ecf497d98fdd70adce90e8ce350f3d9f14b735f7cd03c010000000000000000003e3c02c9fd4bf5cde2f4f0ef50570669aa586b3b12e1dff52507337198d3d879bc966a6ad43a0217d2822239").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000522c9273bf66ca7ea531cd3409f3b7fb9f228966e67149300000000000000000000ad5c81834cc230f4ea59cd148298080d22cd008a440a75e34d946ea646f94e7791976a6ad43a0217899d978e").expect("Bytes32 from UTXO should be valid"),
hex::decode("008003203404ad959ad7bc9787388e0035542ad5d540f5077130010000000000000000005082dd1fd7487596fe92f1c06261d3f1abd2da19c2116b175d62adc245ae9ccfcf976a6ad43a0217eafb7678").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0ff3f6e2ede80814d029f614eca38ab9c9a15e9400a023d0200000000000000000000429fa6b33b892eb6544a11e0f3728d109acbeb0f9ad59a48ecd3d64e28543a4987986a6ad43a02179c1c0930").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a00820b709f5dde282465f55c4d7e66cb2ba6fc916c0d51c4c01000000000000000000eb37059a5ba1cdd3f3810607c8d194363679bc9f99dcd95652283a12d7603b29ed986a6ad43a02174b733dd8").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3f4b173d1bc45aa63418b7bba7c0fa5a5acfe53d22eb1e01000000000000000000f745f6a23d5cc180ff74ad724e3a12bf2bdf6d5d8f1cbacac629b28634fdd3b71c9c6a6ad43a02174c304983").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002830ec33bdc09b86dd4d9e637a2c5c4811989d9bc33505010000000000000000008caab6a74d42b54943d5ab85c80a95f17bdabaaccf27f8e1d18923fe2ad0113c2f9d6a6ad43a0217a53735c3").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a04c25fb6183fac04e92b6a4b899c9ef156babf959dce7c7b600000000000000000000adae556e5b521d5c392f39d3c8695d5cc11dd3234a6c38c7c423ac430fe5d10f2b9f6a6ad43a0217fd964416").expect("Bytes32 from UTXO should be valid"),
hex::decode("00606b202e489dafa11d28ef8c748ed8da4a954c5affb25454bb01000000000000000000e3f9ee43488ffbb8b711222f0540d1fd61bbec8ffbb9ced960dc2352392eeee180a06a6ad43a02170208ea28").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a00b207940814db8828f2f99a835fec3507ac1e3c6b8c7aaf100000000000000000000a4986016840beba7d215510f02f5e88cd41fc4cfc9bee4069ea56a4a7a9cae8715a26a6ad43a02177d018b27").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e04325f4a84a1a7043948c2af0faa632f4735b8dd989f49c250100000000000000000072707db2fc682148e6aa27d4dbdf7c20009db65c848132f76bc1b3a31bcddcb57ca46a6ad43a021777dae935").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0ff3fad0b6289eaa9c8932a76daadd7a5c3bef539fa19708b00000000000000000000dd2c965c6cea3dc01c21466b8b575c24f0b789a79c6e3f6ba7dcbe45740e1fbb9ea86a6ad43a02173bd6dce6").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000022064c18468116c4e6aa84afd708a1dc597a4aeb5e0d26001000000000000000000d0132ba263c4eb125280313ea30d4853b2201973b825588780c5e080eaaa8418c0a96a6ad43a0217d3de833b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c00e25575e08652190884e0ce0e963d24dc654eeeb47c77f1900000000000000000000068daf8886ba4daec55638e8f7e06890a186b6e6aefb5e7912b360a5a644bf1704aa6a6ad43a0217693c1a54").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002ccb2e57d6fa6db31eb2fe4b099c98602747060080927a010000000000000000004ae5c82107d3b01d762bd7250842757420ab88bd9eafa531e45ddd5460ac1c4524aa6a6ad43a0217ae883618").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000420385cfb56a9c0aa212ad2d8cfc3be492cb768e7be66e801000000000000000000e47b35c93528a98e5ccc07f7a64120f10fe44a7d2add76f851f2ef1ff86fb35588aa6a6ad43a0217e74aa794").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000002ae44f541085b616ee2b6b66143d45b8d9d005a13ee58601000000000000000000eb7efe30fbcae6ef197af5303e922d5f13af046afe6722a44a407de33cfb05ecd1aa6a6ad43a0217cc1c1fc5").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c00220ddf782b16cfb698ec47c721caa0e043c6e3045bd7d3f010000000000000000008ebe86fc87779b0ab7b1eeda4f938fa6bff75fdf3f03326d4c3f0f212db1a5df24ac6a6ad43a021787917f4b").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000003a8e0ffb97a26cb108666a56d11cfa7e8083858aefd35c00000000000000000000f741327a673e2680ea856c80fe7c45f495f35c0809c79095d2aa49bdac42d8a91eae6a6ad43a0217ee19f61c").expect("Bytes32 from UTXO should be valid"),
hex::decode("0420a12a06c61bdd24754ca5788b5b98127fa4cad6bfc6b4943000000000000000000000c0bc9db8ba6c3cd5e4852713723ab9462ed8cdff9a8e7a59f106a7d1e5d9596223af6a6ad43a021752931fe7").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a0cd2bcda5fed994deb88252ba588292824dafbfed2be7bb8300000000000000000000b021ceecadb5f6f0fcdec2d4dc38cc9021bec331a411769cdbfa1f0d731756c55fb16a6ad43a02174c791b12").expect("Bytes32 from UTXO should be valid"),
hex::decode("04e05c24b1a2e59ea42a7424eea8fdb9b95d65865090c6745fec01000000000000000000d49392556462b7cc6a324f3297b95655e8558a842829fa4dbe42a8e8740029149ab16a6ad43a021794875146").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a037310cb4c38e4d2d46702721cf104e9adf7a2120c0ddf85e01000000000000000000309c7334a84c50b5274a7efcc0ee46380415322cb2215a0220d84b7db6f96e6ac3b16a6ad43a0217d06362de").expect("Bytes32 from UTXO should be valid"),
hex::decode("00001c2a5253d07fb8f9c845066cd8d88ae9cbe590f2239d323a010000000000000000002430bbca343667d2cee8a3d7a86901914c852f38a92dcc8142f694235c3d8bf334b26a6ad43a0217dd6db83c").expect("Bytes32 from UTXO should be valid"),
hex::decode("00805c22ab4c07fb2ce6a683075bde23e7d94f50b588e8f9aa9c00000000000000000000ae3e39b85b23c403d31143839b34cc074e7eef0ecb1bf27c54e324a5be96676d92b26a6ad43a02177008583b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c004200a2cd7ef7703ae1fb2093c550eb744c3ea18108ae7a800000000000000000000d0bada625a70a2fd6cfaa2aede6d72bf4bdd10b64a52289820832323519d62ef43b56a6ad43a021767cab9b1").expect("Bytes32 from UTXO should be valid"),
hex::decode("008000206d1cce7f0978d68ffdf92d43c7027bdb81b612641586010000000000000000008d818799a9cada0f4f9243f2cad85b8940125850a9ba37f7d1e6fd282c8c62e145b76a6ad43a02179e855276").expect("Bytes32 from UTXO should be valid"),
hex::decode("0060af24e8f12bb06b4a71cf56a91e73a3966e14c17208a54d6501000000000000000000fbc1e7250797d3e71b49e1abaef047d8cf7e16f1c0ff9de688182d503d0cae67d6b86a6ad43a02171ebed5cc").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c00220511c75e44d1d627b986749aead0f55ab37f04df146ad0100000000000000000037e94c77e2c639bbec32dc4dd570b31516ea25fd1497156602b5e149f8d391d682c06a6ad43a02176534c3d3").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3f78fb96b3c3c7b29aa1915342bddc921bffd01bfbb63d010000000000000000007a5c1741944ca4332ef03742b971469c0c966655b31d10609a8ffbea38a94cfa2dc56a6ad43a0217a35c947e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e004204870e85ab18fcb38d67c35979efc5df4105490b6713f00000000000000000000c7714efab1a223e7fa21c6f87db62b32cdcd048d5b70e59def0d434de384b3e871c66a6ad43a02178f3d7140").expect("Bytes32 from UTXO should be valid"),
hex::decode("004005209f1f0b92d362f5d4f3b1cceab1a7b6e0d2e55e2c38e8010000000000000000000f4e65ca15db0700809fa074c96785754236d3429e2fcf909a0a0cd7e59a31e6c0ce6a6ad43a02172c31d37e").expect("Bytes32 from UTXO should be valid"),
hex::decode("0080e029852c36bc969233723d0d0c7b44c3820b1233edd53c0001000000000000000000b068c891e2d2e65e1d48a74cb19d5e2aba449403d0cc021cf49cec0175226499d5ce6a6ad43a02171413e6c3").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e00020947175283c53840a5fd95ff5fb52a69cbf049640600e00000000000000000000d4ffb8d309f3aaf7023d9e3df8587e63b672b2180b85c0d307cfbf3e334ddb1a5fd76a6ad43a0217d543d85a").expect("Bytes32 from UTXO should be valid"),
hex::decode("00801d21203bd017379dede3b30858ec121ad3afebe66093deaa00000000000000000000b15433a1853ef7eab266109f32e22af002690eea752fa0afcfd517c8c51957ed5ad76a6ad43a0217e750210f").expect("Bytes32 from UTXO should be valid"),
hex::decode("0040032083cc6f37b94764a79231d6aff125a14f964aa83aef2300000000000000000000605c53cb797ad5e44717e9345170a30375813712b4374440b7b2db0c916fcce34ddd6a6ad43a0217ac7dca95").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e00020d8f1948230933cf727b9d64f1e5786939d595727ca0c0000000000000000000064559da1901f51ddb2307790cd717cdb08421a0d596a119ea26c795695e91fb60ade6a6ad43a0217252accd0").expect("Bytes32 from UTXO should be valid"),
hex::decode("0060e524ba12f94ea5504e31ef86e8cfaacd1dfde5f2981a0a1202000000000000000000b7768b6df326e64a6091b06c253adf3ad85d58b6900c9f0262ceeff036c65ae969df6a6ad43a02177e5d71b9").expect("Bytes32 from UTXO should be valid"),
hex::decode("00200a2052a2caca7cbb37e81cafc0d37c29bb530ac9bbc42cc701000000000000000000d2b7cf8533ff1f8f67aa7a9278cea1885a1404465b8d42beee3c22572887406e7be36a6ad43a021723dfac06").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000220550058d8ed56d8640ee35b6684020f73610626342c900000000000000000000adfcd1ef1d32d7394be8a552ced86e00d4a1fecd68c78e047ea79bf6fc002b7a91e46a6ad43a0217ca6118f3").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0c02658c8f4dfd9d02cccdc514498243a7fefb91f9c49069400000000000000000000a48bf36c7ff5543f078df43076242f3992e00a7cf842e80af8f3e1699acb242db3e76a6ad43a0217637c9ff1").expect("Bytes32 from UTXO should be valid"),
hex::decode("002047242bfc14c46c37991c2cef140720b1b3081ef7d87db8d3010000000000000000007f4d937fe1cfa4629e958b43d638b2079ea77e57c9cef7606f07e2fe5e888b18c4e76a6ad43a02178d9c644f").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a091278a26924cfba43b61448a7a64c5b1fee904fb8e811cd70000000000000000000008abe2adedf7edfe2fab80f34a7a930b1267f123679414348649a39ff53230808def6a6ad43a021753c783de").expect("Bytes32 from UTXO should be valid"),
hex::decode("00004830337426f9ece788d4c8241e02589ffea33f898cbe09f501000000000000000000e2e8f5511c6553157451a89067fdcc790097d030bb4bc0690bc17014eef5d2c990f16a6ad43a0217a872716e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e02920546922cd52e7f92111d725d812c1532308eb59fa22ee00000000000000000000fe634310b53673fc6c018af12c64c19060157b1fc3b73d5a5c9caa03cf3565f2baf56a6ad43a0217c76fd370").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0c121fa38469efe9223b571f74e760c96df203d80b08f3b5600000000000000000000f33c1eea09c6d87bbc4d1d2321270c21454495f21ff525534f4e20c85138a592c1f56a6ad43a021792df7b16").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0a126df55ad5154bd8f45fbe0cdb9fc9722783ce4b7a535ac01000000000000000000c533cefab59d506f73176e595df8cea46363e9b5567dff08a163a3c465dc98d14ffc6a6ad43a0217c26d2516").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a09d2b81fd6f7a8e612d81d80d5b5d64859c1b2b2b101bd1fb00000000000000000000800989d2c31d1dc5a3308452b91b1ed40206a030839737953c9d21d6923786451efd6a6ad43a021749e04799").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0d729b34736f399b13a8a2fb7479954d43c2df771c757e23901000000000000000000f7c933bad214186ec2e49c9d1b1585937280868818f320803cf6436e09a8626973016b6ad43a02171b81e870").expect("Bytes32 from UTXO should be valid"),
hex::decode("00601521556ce1a2354410a18c80b0a67be6dc16257be28890ec010000000000000000003059a134b7eca83de9aca917fabb76ee41d97032583222d6840ef22da16b5b92dd016b6ad43a021725071a5b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00602e2064168b2c362c354444ea0828f36d7cdb3572169b615601000000000000000000a4aeaacb4c25d3a83b2f8ea594325d756ec78f290a73c4953f3cbfaff10e3a3792026b6ad43a02170bb4640f").expect("Bytes32 from UTXO should be valid"),
hex::decode("0040c82357a6c79e2fb6f4d4bf95724a4bcc29bde7b449debdec01000000000000000000fa99bac89c24d15717ad877fecfbf734eb0bc75f8fb578f9470a5db668dbb8fbc0046b6ad43a02174b869a7d").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000034395bcc21a6a2c2d3ccbc2773194d11a7ddfebf0e0919000000000000000000006f505e60f36ce9f73494bfbf0c0f058fee1e2e4822c1b0fa766fb9e3251fd9b38e056b6ad43a02172c4c0184").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800d20354b5388fb81effe12c3291a4cb50cc2adc5b98d079b00000000000000000000103e5e5b3d6d17e134cc0c76af0e91fe08d72591bf4c3ad7b6aa869d5d4cb02ba7066b6ad43a02172202f70c").expect("Bytes32 from UTXO should be valid"),
hex::decode("00200e20b3001fb8573b91195cb7b9c1de390d9991f3c07978d3010000000000000000003e6addca9c34eee9459e631d9d08e77c2de70703f102ce7903838cc77208f595c7066b6ad43a02173fa4ae35").expect("Bytes32 from UTXO should be valid"),
hex::decode("006004209e4646297e4d4fd4f60548b0616a359650c35f1de7f30000000000000000000072aff40819e1e686905c68af2e73b9b951be3736437b416986cf18fd71fb8bd95a0b6b6ad43a021729d33887").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000022ee651093128e4598ff28ca297f351401d19696638b380000000000000000000038d529507d803dcf8080875cf2258aa8a551511c24f8b8ae3fa88423b0602748920b6b6ad43a02170846a89b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00608320be57374d6179c78ada919fffb2d9f908f5e18a74ff0501000000000000000000fa2f2dfb7b4d5235da7f1029804e23edbb7d3323af88199fc0ef1b79a1753459120c6b6ad43a02179a132a56").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a0d227a580e546e615e1c532f47d89de7368c30df20e2e781b01000000000000000000e12705f05a4bbf09b87493335471db4f833e12d99fd36d76c5b3279082dd096e580c6b6ad43a02177e704ef5").expect("Bytes32 from UTXO should be valid"),
hex::decode("00600f3912f664c2673dde9ab6886a85a2c9fc4efc177a0cbf4c01000000000000000000f1a81ddd80ef01b4a480de488f15402069ae8683ecc46770878b54fb7991c1a57d0d6b6ad43a0217bb384b12").expect("Bytes32 from UTXO should be valid"),
hex::decode("00803427b9cddcc2df57f4563ae0131adb3dd35bb9917139b95400000000000000000000c0b2ec9353f60fbdf5cf7da0ab0c17a78003bce2052a412c27d12665f76cb555fa136b6ad43a0217066d21e6").expect("Bytes32 from UTXO should be valid"),
hex::decode("0040ec33bb8f6fa5e2f2ed3cf60cf91d6e79fcf7cde05b11087e01000000000000000000e6075b19a084d7c5aaa8484aeff15d8b5db4db88ad69d5ee647a3d9f778fedda61146b6ad43a0217a27b3033").expect("Bytes32 from UTXO should be valid"),
hex::decode("10a0c2227eb8240f150309fcaad8a2be205ddcbf06429a508662010000000000000000007e17cf9282b77723437c083af1aa83482a73fee35c73f110303d402d88fa8bed4c156b6ad43a02174b470b31").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e00320cb4dec5b41a12bc19c29a6d424197f86c3b2ba025e25010000000000000000009d1a60c3889690294fc993f397a4dd1df48b3f9c4bdcb41306c3dedf033e4ee8461b6b6ad43a021763b89d28").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000022052e6e25c9112220845c4c3e7fd939340336d3d33cdb600000000000000000000bfc9920255dcbba49376571e9ce3bcafc87bd36045903ecf4076480d96614b7b051d6b6ad43a02172e1a9b25").expect("Bytes32 from UTXO should be valid"),
hex::decode("00209b22982e9a7ed287539bce7c3e603687aa2d3125079fb86a010000000000000000004102e56d0ce9029acb48c28745a9dce1783074391f3e70c695afdd392f6ad6d0ef1d6b6ad43a021722025f6d").expect("Bytes32 from UTXO should be valid"),
hex::decode("10e0ff3f1bb91d2d09c8f74d7245ec18f7e5b6810a7f461dcdc900000000000000000000ac2b5e4ba6695790ec04953ef4774ece540667da058dc6c0888363c4fe922e0dc81f6b6ad43a021760ea40d6").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0f731f4aa3d9f670eaece00ac84c4c19cc4e2a26ef07851220200000000000000000060ee14b2e84e0f736907009618bc2d8296194d66c7ca0a027c702569990b49090d206b6ad43a0217eeb19de9").expect("Bytes32 from UTXO should be valid"),
hex::decode("0020f92325a882bfdca063304e330248bdc067d51df8e92bcaf800000000000000000000c753e6732da8e5c98b3376480be610cbdf3cb16d836b59ab6362d2d228efedea29206b6ad43a0217c924f2a1").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a0a220f0dc827b72a458a9ce7550312c0a8750d4f068713efa01000000000000000000cc94b5a5e75437a7c856b29b12da804ddb02fe1353bb210da2606d8180ec6fb631206b6ad43a02174e9d590a").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000082063447f3bc8b428d461b82a070be3db9410ab1fc40f24000000000000000000008dc755c30eaa2c22cc7a4a5792e3656f4bf698d38c41ebc4c85165fb8c9c6ded79206b6ad43a02173e731a1f").expect("Bytes32 from UTXO should be valid"),
hex::decode("10c05729b6ea9fbe42fb1d9405e47116cb0424329411d017e1aa0000000000000000000076a747223eaa6d45d3f84e2f6828cdc487c0dbdbad42b08a9b8fffc27665a960a7216b6ad43a02174394d82b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a08520af579ef409a189ab162c788896ad825b38faea3641c401000000000000000000dee9be72bd60eda2ab12e0217e2ca16a8bcb34c32f4972b273b77a17440d00e70a226b6ad43a0217116884ce").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a00620482fd173f0a825fc34f487502c1566555f2862f0a02801000000000000000000bb5482aae60bfd220388693225362b18f72e82f9b5ceb9c6d2f9a61f2c28094fe0296b6ad43a0217fb407034").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c00120e0e9282069496b464ef564a9ead03d37ce8e7a9f12c701000000000000000000975e9f48c1083438f32c873d67b2fddfc3c8e130ac9fbc1a53da01c01c3d46c8992a6b6ad43a0217ed908988").expect("Bytes32 from UTXO should be valid"),
hex::decode("00200723899fc0d3576718c15a6719c4bc71a91bd3ccc96192ec0000000000000000000004fdb6ab38260ca750671d7a6637ef75a651448c82cfc14554d57e2748be2be9722e6b6ad43a02178a807416").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3fbf073df0898e0324336409d64b6b11987fd4465bd82900000000000000000000f4e8fc5a02f4d8f4311eb5fbf73592c88906b0bce6a08b6e321e6c7b19509dfe8f326b6ad43a02175cba391b").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000b2080a47bfbe3066208d4fc39e065d584930a323adb645b0100000000000000000044ad1fa9768359f8528ddcbee3b5f1c773f184fabb6bf87d883e91b0f3f15b89d8316b6ad43a0217329de815").expect("Bytes32 from UTXO should be valid"),
hex::decode("006003204682f5d50b7c8bdea389f6c43515d3459584ab50c406010000000000000000003d441907ec00061b73368da7754ad305e6fd796cb4cf9d3410d5a65927d8e6ce74356b6ad43a02174b0fcdc3").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000d3255924665d3d5fe933b16e09b06dcdd3bf7cce977223690000000000000000000024f812c8d99a9596021d3934a646bd50db9ca98931f3228c236952ac222217d3fe356b6ad43a0217effcbbff").expect("Bytes32 from UTXO should be valid"),
hex::decode("0000ff3f11bce3edc9ad7d6858a81f240977adb5517ef078ac5600000000000000000000a11d0de0146f427f426965160dbb0db66cebb6eb805bb3af11ee21924f2bdd71383c6b6ad43a0217936d08ca").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e0002007bb096f631e7168dcda31d75201201e543037a0b43300000000000000000000a2a7803b593fbcd881866db771b59f2f98f5f69600460fb6ad553867a1e00a00323e6b6ad43a02170dbeda27").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000205a18258ccf12385ebd72c8f4f0aa2f5b5c5ec841ce10000000000000000000004a994b5f6c109f3cf616de3c0641711d1d4b848c7a04fe6e243765f67ec9cac875406b6ad43a02170d80ed5a").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0722b62395602530b6d60060a8fce332cd1908f21b30b8be4010000000000000000009508e51190aa63ec2298af3cb644eeb1498c0d83d67680efe30d5fd1f6a95d6d7e426b6ad43a02179c08e6e0").expect("Bytes32 from UTXO should be valid"),
hex::decode("00a02320b4b25be6c2ba4576586b90f8c4c3732648680cc5c2d901000000000000000000e5865a3d45646d51b65855b4b9a972198fca36241574a652ecfbab67b3f6256f97456b6ad43a021715c30439").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000030ee8847f7a144b68c6e1e0bef2b4183db01bf825d6d05020000000000000000009798b0f81dc791ce7b17eec0d89ef9db7a2b541e9f582fb2387fd9b642418faa6e476b6ad43a0217a06ef431").expect("Bytes32 from UTXO should be valid"),
hex::decode("00804521440b70d25839dbc3dd0bccd37ef751d50e576188eaa500000000000000000000314ff0850863609b917541ec201a10cd9f47f69cd2d1bf267cdb19fe3e91238bcc486b6ad43a02175e15bd74").expect("Bytes32 from UTXO should be valid"),
hex::decode("000065337e07a76349f99f3eaedfbd7ec9b048ae462fc439cddf0100000000000000000000268bf66e3c00ffff66a507729af9df227492b86f436331ebad0241ad055fe97b516b6ad43a021729b64713").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000022b936d35a2f4a755778159e39d6d80b5946bbf15b7c0b01000000000000000000640504d1606de9fce2fd0be5304c553b7f7092bd5f18c291e3fb1c63a973194fc3566b6ad43a021725c6c2d8").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000020301ba83277f8012e1902f97eea3e53d0515ef73319f201000000000000000000f8a415b0a937ef5175c9715ac98e3373e373afad63cbc9c8541c51119d91115fca5b6b6ad43a02176c9b53e1").expect("Bytes32 from UTXO should be valid"),
hex::decode("00800420ec8823f23684b5a607fa9d6194dc174497f8501a194301000000000000000000af001c7d8797bdc2f8f45149d1f4cd394903e12d59c1f8afbcdbdce3b9925436665d6b6ad43a021776224a13").expect("Bytes32 from UTXO should be valid"),
hex::decode("00000020465a566082cb8faeeb44e097377a9174a11b65a051ff010000000000000000000b6ef86b33a25b37856c2e7c68543a5066dbe67c2a087c8f4199eb289357b17360616b6ad43a021776fc5e72").expect("Bytes32 from UTXO should be valid"),
hex::decode("10e0a02c5d89719ab4dce3ed69c543336776072cdcca1c26514301000000000000000000e860d743e10db71b2b7c5a1fadb2cea11ab7200ba04b4b776fd55d0939dc6cbc61616b6ad43a02171151725e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00606e31bb3cc5e304822bfdf4506cc3b2e0aeb98a7b21280a0f01000000000000000000c897183f710feca8271a8709df053983a6d4489610a73d85b9f0014f58dd770262636b6ad43a0217a723ee06").expect("Bytes32 from UTXO should be valid"),
hex::decode("00c0812002e5d76425f391738eac0c614d8afc795fde8c0dbf7d010000000000000000008c6bae74a46ad9f2905e5258d4adaf61af2bcf7c87e86a22f084f11710d58e2b36646b6ad43a021795ac6c1e").expect("Bytes32 from UTXO should be valid"),
hex::decode("00601820856aed25759848cd1ad02caec25afdc0cf17e9ffa0aa01000000000000000000c17d5c0a7bf4af165552c3e91b4d98f4d2afa2938e7b1bdf0912f1e850680a1c53656b6ad43a0217193b3d87").expect("Bytes32 from UTXO should be valid"),
hex::decode("00e02c3ade01a429e0f5c328824f4ee063b98f24ff3b0dc0db08000000000000000000009d0b402f41ab9c16e08f95de962044b206413f878608abca2015ee2074713078606d6b6ad43a0217c0650150").expect("Bytes32 from UTXO should be valid"),
hex::decode("00008f2604b952dcd44dfcb61080ad77c5b626efd760e5f8ac8401000000000000000000ee81697e6cf7f41db37b63a9680906d3023be7a905785ef1b5ebd03fd22dd8bb2d6f6b6ad43a0217287aa17f").expect("Bytes32 from UTXO should be valid"),
hex::decode("008017262c315a069a8896ec04d88ef423ff5cb3845320a6f123010000000000000000002d42d9519e62f5a48d4880b0ff0cd8021a38eadd469a2f7aabd0886061026a5026706b6ad43a02172ea14dd2").expect("Bytes32 from UTXO should be valid"),
hex::decode("000000204a9037b3465b3c657997cddcffed602a949ee139cbdc1b010000000000000000669c3e2472384bc95993f154aada38d6b2113d8d32069e32e2724ba40915a1802a669e59e93c011801c8c735").unwrap(),
hex::decode("02000020926b3b1cfec537afaa88f002a45a5a5c3dc25c0664dae7000000000000000000af0f270bf3f868d47089f395197809c360f1506636c960d96afed68bbd72d0b4496d9e59e93c01183ac94b67").unwrap(),
hex::decode("0000002093b3d5716570bdd5afd75a1c7d87873bacc062936dda630000000000000000009ab437e7c66b10a59b67127f290d24f381aba4e03d5e16c062a356eea4d608dea56d9e59e93c0118264f310e").unwrap(),
hex::decode("0200002026309618b52c68271702f57fbe684326663374eb6360ec00000000000000000023c441f913d1c77fb635544f9061fa761f876ce7d93ccbef5dc7f88e84891cb1aa6e9e59e93c01185bce3c9b").unwrap(),
hex::decode("00000020a3776fc8ad2be759cd4bd3d60eaf8d8225c869864490520000000000000000006a8e7d27c71b9886b090a7422e7b5c12bb2c88255af0273d2724b69bb8c1a817b3709e59e93c01186a257ab3").unwrap(),
hex::decode("1200002076d65fb74b1285c9b4d809645f6bb6f8f076a36295d9d400000000000000000062c8a7f4f7aa43726f753304ac9bc071877ef81273fa5f9e2f8e6f2df92f1211eb739e59e93c0118b2f12aee").unwrap(),
hex::decode("02000020762d95fbba0466c983446595dd608487ffafaa71e123d9000000000000000000e639b7f88ac7d1187bf9e8c82e8e504eabb2af5f0f05beaaefa09bff7d7314d6a07a9e59e93c0118be840af6").unwrap(),
hex::decode("020000203e5a55d70de3f462efd40e8780adee7dfdb451b57eda230100000000000000003efb1342b5fe12376a09f89a1dbb775e5855f9a1accc41b40929ec911ec1efd6f07f9e59e93c011829615752").unwrap(),
hex::decode("020000206cac3182c85d630fe6938284a88177b1265f8875738b0a0000000000000000006b9d69ba608cc06e8d519373c2d0a78a62b0bcffd150e37f79f31e97a8af1af4568c9e59e93c01185f5c7c32").unwrap(),
hex::decode("02000020e2f550d7404324de1bd8c83a79a1c0133635a34269120700000000000000000057819057a8a85c8f1cafb13e5eed7d6739e13d855d63421c9cef5bf1a98101a33b939e59e93c0118ee220467").unwrap(),
hex::decode("12000020ce9c0848d500c2fea3efd38f4e2822d8962555a1d68b2f000000000000000000e38b12110e6b8978155dad2724cf0ce4d8ab827712f8e750ce7be253227a10bdc4969e59e93c0118dc3403b6").unwrap(),
hex::decode("020000202431a9c869c26ce0e76b1ed6c6543be9b7fff0ace4735d00000000000000000073811d01111496203c08c0e5941fee2c7791e1b7cfdc852258864c873df8fa6b6c9e9e59e93c01187a4ed56c").unwrap(),
hex::decode("12000020808101492d7f3237125b79288a39cf6f71bc279e45b5c9000000000000000000d7d57e05a60bae464dced00c83eff37b447fcb07e95fadce2f610267146c622fa2a39e59e93c0118919a5f2a").unwrap(),
hex::decode("020000207272193a5971c9c5b8440ade64d14a0de46baa43c3faa3000000000000000000e7b1dbce250ea8e3e44e601fb26cb814e6e6e7b3b3e87e6239f6a745e3c9194780a79e59e93c011849f1593d").unwrap(),
hex::decode("020000202e6cfa8b6c2ee74f0648137131bf782c6756a7f8462f4100000000000000000056fb99126c1a78c143d86e89787de7bfc08f5492bd26df1c4314280a34e64be797a79e59e93c0118ae471ff4").unwrap(),
hex::decode("00000020be74317e078482f6a5a3723044aa62cb858b82dffd1be300000000000000000051111cc62fdcd7de11a577853ee3e4832394963da62e5bae92173ae2f366e4dabba89e59e93c01185adb0d11").unwrap(),
hex::decode("000000209cd483b9091395be33ed7712c0e8c3695b35ed8f62e71b01000000000000000046df5dea1198544e5bbbbe9db155f97f79716cd811f8d208c06be7603b70c742a4ac9e59e93c01180c032031").unwrap(),
hex::decode("000000207936ae0f3ffbf1673598053377e6283e3a27d9fb023e440000000000000000002ee47a3ec50659fbfb117eb7c21ed26b493908bb9f8d172ed19cea85e402e66b57b59e59e93c01188178d5bf").unwrap(),
hex::decode("0000002094fb4f7b930b3c540db44b2dfc909ef5baccba57b435ee0000000000000000006dd2cc7164d9e6d2d0400609cf5d76531391ca93f879bbbcfafbf560b6b031e8cbb79e59e93c0118374c86df").unwrap(),
hex::decode("00000020e4085486f70e220273bff0fa6fc767eb74b69d2c978d26000000000000000000c991b50232e52220d94177b6ee0b606c52c010037070fc101945e160ca24786c38b99e59e93c0118c1d0e6a4").unwrap(),
hex::decode("020000200a5d7c8f4134ebd1e3aa19ad554dd8fd3281c94f8fda1b000000000000000000a95d611bdd5d90d2710a62915ebb1592d1bf224b104e3b8f5b289fe873d021eaf1bd9e59e93c0118d5e7ef59").unwrap(),
hex::decode("00000020d7cafeddd0815ba95c6e90afc16970e0514729c0a7cd600000000000000000001b3886b882cfaca6d59367c1406f9b7556fc22c190b11ff414b8b2f0269c968aeec19e59e93c0118434c5f61").unwrap(),
hex::decode("020000204384ba6db53aa87a604259c9f9c6844b99ced37e29db0b0100000000000000000c24d07c38afc18bd830aebc2476564df8fa64dfcc8d44dd68ed7df3fc5d206795c49e59e93c0118b415f46f").unwrap(),
hex::decode("000000209986db62bd16a5de375470344aeec6d17ea322972d75bb000000000000000000d5c8ccb89f92e3b16e3a230d941d7ccc946b91fddae922c08c3245823210f47f43c89e59e93c011843c47fea").unwrap(),
hex::decode("000000201e8afdebfade79e0a483e56f2fb87a277a513769add0410000000000000000003b8b9fc7fb33f2b8c8520dcf76eb751017c06d0ed4bac49c2ebdd036abd9ee76a4c99e59e93c0118b7b6dbcd").unwrap(),
hex::decode("000000200a5fa5f3d854fecd0c8fea9a71879443347058ddec1c0f0000000000000000009d3249cb64dda39bf0ed9ab4b665780f6c6aa72d35460bd972f1212a32e84c75a5c99e59e93c0118ae216161").unwrap(),
hex::decode("020000201cdefc545aa71150e97a9763785dbc2855710f33ba3f1e010000000000000000e072dd1b8043b5dd632d4a91769cb91ca55c0343dc3d8cbbbf838c6efb1940d229d09e59e93c011853549672").unwrap(),
hex::decode("00000020375d4cbf7b4d0550803dcf160ebd264bcb594d3c82c56700000000000000000058dee1ace217c483b05544f9e5d77da2857b5b443380d22761ff14869fd8ea473dd39e59e93c01187e31b1b4").unwrap(),
hex::decode("02000020560be37ca005f2c792755368aaf63b5a664ea36c2c69be00000000000000000026cda0eeaf84644b2aa9d072e5b5bf14c8c8b707a671e4b2232c7e569fc2a07763d69e59e93c0118e2b558a6").unwrap(),
hex::decode("02000020546da8a3fde6bd1f52247bc8a6cd0251e772eae3b5556600000000000000000029b4e20ecbc778975e189bd28631e94b2809422d8efef15b7e440f624e79d9964fda9e59e93c01184f91c402").unwrap(),
hex::decode("020000200bee1075fc01582e0b453bfacfe49add055d6cbccea7a500000000000000000020e6280b3d966f5af7290a164087a6c8cb177b41ec7be91035d0f5c45f1434f129dc9e59e93c0118cb35d326").unwrap(),
hex::decode("00000020f07f65f1ed56e656a942eb424dbc76d78cde25acf1e7f30000000000000000006c6419dc5a46235b11f9a14f472ec166f7ee40f4ba8f031294c9ec8a20471bd6ffdd9e59e93c0118f55c1c76").unwrap(),
hex::decode("020000201c30d0ecfff1a55c6531ce992467b0a02d25d6a545120e0100000000000000004349f28fae9da9ced1744f9988d6c7ffffe10892e2ee2a0a3228f2555c13639461e19e59e93c011802b33340").unwrap(),
hex::decode("02000020f47f05efbfddee4a1d0a0c502f8a9604ef2236e60b02c8000000000000000000ad0f318ca8bb97a73a3490ac79c8cd1922ec53084493a5d2dd7c6bbe64889b1766e29e59e93c0118184be748").unwrap(),
hex::decode("02000020a9446ad105a3a4434273e72d9d47f23544bd2d4223d4fc00000000000000000081078f171421ea27808a8bf0e74d9679d9e756197a832d17e44cad3a67e5b8104be79e59e93c011822774361").unwrap(),
hex::decode("0200002060e290b87ee42f2d9497b8d588a144c8ff3013d4036ee4000000000000000000cf51f2b3e82a3da5adbf8baa8da3ce0dde7d9bc0f68ea7d241bfe7ddb6a6a5302be89e59e93c011841dd5d0e").unwrap(),
hex::decode("0000002037e7cd68d910094e9963ae1953173141bd1dd22756bf1801000000000000000098416fb809d13de6c54ccce4927b167e9d561db613df3cd05fd2345daa1ceee5b5e99e59e93c0118a9f6e6fd").unwrap(),
hex::decode("00000020cd116024d228583ad1f60c649452ff34224cba9943288f000000000000000000b9c1df740343350581e7b896f3e8edb4ed5a04d3f6fbccff4f69fa272916b110ffea9e59e93c011875c11682").unwrap(),
hex::decode("0000002050599ca8da62a8cfd3a07144f555651df0036933cf72c0000000000000000000485624901693271d8c6e343e603c2ce8e90b471cca311f023ba85e010a0e21913cec9e59e93c0118945a3cda").unwrap(),
hex::decode("020000205ca35aa16b669d252de566ff111a2800250bfdfae5e72300000000000000000099a99f1cb9f487794d70dd2dabdb63542aad056ff20cf5e435d1a350a5dd8f7435f09e59e93c01182d21bc70").unwrap(),
hex::decode("02000020751510083e009cc429c10ce2a1f7ce1f95c87f4716275c000000000000000000d1c1b3b2a58a4d7780fd7855cb168a3187fbb48766a77e2237e61a987f7a04a0a4f49e59e93c01188c23e65e").unwrap(),
hex::decode("02000020ca376c104bded3b6f2ecd502c4626b4e4ba77ede0f2817010000000000000000007ed4eebdd8353dbaf9d2038858dd5d075efbc8d88ecf8390c9bd4629561739b6f49e59e93c0118680992f1").unwrap(),
hex::decode("0000002054ddd7cc1845abc2f45fb6fdd477fc060c621cfc3f64a300000000000000000027b24757e89286c84b438fdd7221d5eeec72dc3214f96bfcbb034480b8d17dfcf2f79e59e93c01189e1f97f6").unwrap(),
hex::decode("00000020f49dc697e664b8b0c2029098b02ffef095d48c1e477145000000000000000000c644ff082cb1fa4ecce54bed1b73568f1d822871004222d5a72f3cc498e2f44763f99e59e93c0118f9f8e680").unwrap(),
hex::decode("000000206549bb716f4d1f739f6ec0eec4e7bd579d13a08f32ec910000000000000000004d9a9c3c118ada3bc0bf92527bcf58b9dd9a2ebfcd32e863ddf55bf4b8ea51acd3fa9e59e93c0118dd521ee6").unwrap(),
hex::decode("000000209a368fcdfa9401ade089f9caffa5f4247b8d98df587907000000000000000000b4f37f6ec854a54768e754813ffb7e8a28cbf1e96c83d591b991d3f18e314c2b35fc9e59e93c01180d8068c8").unwrap(),
hex::decode("02000020452cab805c77683d7311ca8678c7597791da79fe540a090100000000000000006445c736195151df49c936a6f0d981b6a13cfe2786da44e49972753dc90af2e452fd9e59e93c0118f0572502").unwrap(),
hex::decode("020000200d95d828b4772e62a3fda7c5105a5e62ae59bfa41b897c000000000000000000762cef2e97c31814210393e4acb278c155fe040b711635c6e91ee500e53754431f049f59e93c0118969361d6").unwrap(),
hex::decode("00000020649b9a19c0145467f3a1a58e1bcc05a65ec1056ef93688000000000000000000dd4ccf726384ebdc3ccc54cb2fb333e1d53929c8ab31d53e67b53cd77d61454894079f59e93c0118bed9774f").unwrap(),
hex::decode("020000206f3f9474e424ca652456920f944c7b17b3d240ad37aa3b0000000000000000009a92e0f32aeb11ff2edd48fb838cbb0a3e25c77110c903821f339f7af5b241fb540a9f59e93c0118b5e8548c").unwrap(),
hex::decode("000000200aa99abfd7da0f5f5448aa667e6de3e077c883614be8b200000000000000000075761a440b33470907d040f1a155297ca051031a51f8fa86354759b07bd1f664a10a9f59e93c01184b5fa896").unwrap(),
hex::decode("020000206bd57fe048b0bfc457f7d5d2391e01726fe292ab1fcb18010000000000000000172bba412df81c10668c127231b0aa6d0bb956e5e8fc0e8d049bdf2ad5ae7fb7ad0a9f59e93c0118503dab85").unwrap(),
hex::decode("0000002001930a8eaedf739fcee4ff0b813f3c30e9893adc7c2f750000000000000000008c070fd6e6fb402464b941679aa9d4729039649e6d0291503f707b0e18f15e43050d9f59e93c011853eff7b1").unwrap(),
hex::decode("0000002032065359c9d1ec45b0e44777179e815c5dea5df6d2b80d000000000000000000e0df9831a1340b0f43e9c1cb184b3c908088c22b3f7c0e83753899fadd7b19a4160f9f59e93c0118ae79fdbb").unwrap(),
hex::decode("020000208790994382f84e67407b33e82f0a8e268d5728cdf51113000000000000000000012a6be8a94ed2b80d931826d1c3e9041b0db02d526bdb01f29cdafe867e8e1952169f59e93c0118baa84dfa").unwrap(),
hex::decode("000000206ee4581dd794f9a4fdfe832fe065309357d674b8128834010000000000000000888e623880da62cf81367c8b77e3e2ce52747b2371e82f4abd0f1ba8e41f84fc78189f59e93c011811f10c19").unwrap(),
hex::decode("00000020077535a944b5339304140abe45ae8074fc2608647123210100000000000000005ed755f1b7abccc300cfa05d5912b2735fd5af94005c483363750315352d91a0971b9f59e93c01186f9d5e9f").unwrap(),
hex::decode("000000202c66b86db7a058c3c44539ddeffb4f1b57015f9b2b8fd5000000000000000000e9b0a033bc22b16c9947bb1cf8666c8b7e31ab98572a12df526c7941eab4325a4f209f59e93c0118d5062214").unwrap(),
hex::decode("02000020f22aab764863f3f9e88a769ac022585b6ffe1bbad8ee510000000000000000000ee1ae23c89a4e6a19d737f0bf177d3a208bd00e1d0ad4cae38227e86ddfb36250229f59e93c01182447fb2c").unwrap(),
hex::decode("02000020d1570bf5d1ac720a7452201a3287325bc5dae8524739b30000000000000000007a05c7cd9b70624c6a5d60bdd06839aba4fcc8e7252edd243b65c14243074437ce239f59e93c0118f63384cf").unwrap(),
hex::decode("02000020aca96f50dac6c74043cc30b2ab63a11af71348913c99070000000000000000006dda0c090803e1207eb4d5151bfc572fec8c5869014dcd528255280fc3c6be0ff02b9f59e93c0118aaeafdd9").unwrap(),
hex::decode("0000002062b7c2b4bfdac0e47f8a510e05e56ec3a2aa159a198b210100000000000000005d1c0f705fb6dda30d8787b483ec14391dca7dba3bc927b5c5d2d6ce5aa605f8e42c9f59e93c01182b168198").unwrap(),
hex::decode("02000020ed0a2c738b93a2020850d1e6e696c409c7e1f386e36f5d000000000000000000ad0ab3a5dc8fa3680ec23d44ad6ae25c34ff4b8880c50ec5930ac8e2e539a59247319f59e93c0118a250018b").unwrap(),
hex::decode("020000209ddcae121db975e6dd5e81c170baf5b3c282ab20394bde00000000000000000080f2facb6a18fafe52015165da8389eeaf7c3ecefb7407bc64746b4848d237a096319f59e93c0118832b1484").unwrap(),
hex::decode("120000209eb0ab39e2c28b15ea5af8d29384b0cd75e0fd8ca333800000000000000000009e664c6c013e556a0e533ea58cdb611fd18f004780f2d8fa9e960f531077723ed3369f59e93c01183ed29cd1").unwrap(),
hex::decode("0000002004b9908261d98fc854b3a60e7c6550be0e80f1faca2e9200000000000000000051ab7c2206c0af2fd0109857e2813667b20c60b44eba0429d12d2bf445b62dd18b399f59e93c011866f2e5ec").unwrap(),
hex::decode("000000208aa7de3780cc6675d5dba2bf9cc9a282affe39a6f7150500000000000000000097f2a23d7efd9dfe84826085d7c60aa3af1838853b39e0de3eb2479070f86ad8b73e9f59e93c01189c2b7650").unwrap(),
hex::decode("020000200446629b6bc0dcacc173df9cb47f001784913e61aff4db0000000000000000003118dcc3cbfd4f0d70955243b0f82945303bc0c1065d81dcb804cc960b2e229406409f59e93c011882e333c3").unwrap(),
hex::decode("020000209dd0c92839267422e8a4c18abc1d098219bdc9cbcee6c500000000000000000020449bc225a5eb4fa2599bb828ad08315ff6070cdae1fcd54f4c12af5a738e78e4429f59e93c0118a43c58f5").unwrap(),
hex::decode("00000020a8ce692a8a4feb0aa11b111173578f62fe62a1f9b7d1250000000000000000008da525d2694931ef00938ad1adf80dc1d98961da742899a3e8e9a9f9d52d139d5d479f59e93c0118299a5e44").unwrap(),
hex::decode("020000209d0fd9053369245ce38bb78a5615f28c35e11a5973c5f700000000000000000063b3af6eb819369034653030bde01fdb932c387de6df52de2706247d85e459cc114d9f59e93c01184a0be1da").unwrap(),
hex::decode("020000202edbe7bc738f7738539edbe8ef297845d6dd3172929008010000000000000000c168d44f58778e26a32f2cae4ad24438ef9a224247560597f90807150f12ce2c954d9f59e93c011837f3cd54").unwrap(),
hex::decode("0000002015fa542454cfb0ae97e23ad4b4ce5703b3108716c01357000000000000000000a806a100d3b35f96c39ca66e22a1beda6f3f6fe713d42dc2e6ffeb28dd8c28e9565f9f59e93c011806b57820").unwrap(),
hex::decode("020000205ec54cb5b7b605cfc74c84f8ba30b366967477158e7eaa00000000000000000003bba352d0ff1f1da8d6236d56537f04f010c244241cfada8413d411bb05f2acbf5f9f59e93c0118c1ddb052").unwrap(),
hex::decode("0200002099e2bf03160945de2c32994587f206fc03a8729873f71b0100000000000000000a41ec1170bd9ae021715e237a8bceb081bbf7fe54c710bc53d5e847cf0e42f83e659f59e93c0118d5368f54").unwrap(),
hex::decode("02000020f7039f439dc89d835d02760938b27f38aa23ca450edfe7000000000000000000d53db2eefe53fe8f7b0bbfda2f6b0fdcdf05b3db319c6d0f8b1c6de724cdfad4bd669f59e93c011886b1e482").unwrap(),
hex::decode("02000020849f13fa4db5a9df69b6c7432787aa46ab907a73eb8958000000000000000000cce6f694fb27b616b555dc7102ec152bde972c81e2872a3e563ef42012aff052cb6b9f59e93c0118e0c9a5a7").unwrap(),
hex::decode("02000020a2483433891049526ac33b6ea00686a35559e02754612e01000000000000000089f3b6be3169c4a876aca2ffe4356c3d0360318b0944361b50877c4c18684d0a466e9f59e93c0118d708e05a").unwrap(),
hex::decode("0200002005b1a1dde1c62d56a012b4f03d8b728a2a94f673e32654000000000000000000d02cfa1f6e7718bcc995a9ae1058dd104b46f5f1602fa04342976a33b2d9de4fed6e9f59e93c0118565c6ad1").unwrap(),
hex::decode("0200002058a8c2c6e79a29d7cae4638b45250afcba2c78931a1a4e000000000000000000047a409d56ecb504f03f625f02debe5e4f8e4afb2e7db585da0e6267e5a9c1241b749f59e93c01183cc51dda").unwrap(),
hex::decode("020000208ea76979f75c77af7770c510ba0b29d10929a78a67a1ab0000000000000000000025f4241f566b426d0482decd6e50433e599e876a5eb19e077198490341c633d1749f59e93c0118eea47b4e").unwrap(),
hex::decode("0200002017cbe57c7bdd58d8dd4a330e75134f505d23337545a0c3000000000000000000a0c7805564032c4837f6aadf9ca1f7c0c05e43a15b5c85b24113edf5463a3c2b63759f59e93c01180d829750").unwrap(),
hex::decode("000000201daf8e737418b092385d91052dc9bf25c78db4947eb55b00000000000000000060a707c9f3152e692ded4eac22e1b94b5ad2ec5054146f252656824385e648a8b8759f59e93c01182341df8c").unwrap(),
hex::decode("0000002096c2dcf962dbfa7ad045b4b8b8b7a4886cad2379ebaea6000000000000000000af2bd41304d6282d189dd7dfa2a9b4e4f9d6dbec56fbec8ca89a79c04f8120a3b1779f59e93c01181ab38dd1").unwrap(),
hex::decode("0200002030656d4c0fd65650a9816435d319a28b65b7565ef68e1100000000000000000026b47d3d67ea2ac89e74a31b9db03d4d3055efa98bb8368e12699a10406e49e4437b9f59e93c01185a356dc0").unwrap(),
hex::decode("02000020d595c94c828b7f0d7833781a9c7c7c698966f251ee3420000000000000000000b182443ec96ca031c8e2fbb6492a1a51ae6cc80e886122d29d01b69269e91ca03a7c9f59e93c0118bfd8da6e").unwrap(),
hex::decode("0200002018ea7a48e4f65e887ae7f5f1fc1395d25c5e454432995c0000000000000000008533a46a3c5b3e2ade0cf423d12d400b62586f8b8bea62e6a075d6b8c976ff6e8f7e9f59e93c01180f245714").unwrap(),
hex::decode("00000020bfdc2e0d03ac8d66bb2a18da3b9d03574e538c5e837a1d010000000000000000ac88acc66f208e84153c725753bdae77fc6d19ef0a640b6d1caf12cf8b851aeabf7f9f59e93c011805fc019a").unwrap(),
hex::decode("00000020831e7f6ff11c94025c12c7ee0246309c60243cfa79c6340100000000000000002a4d15a07780dd0685e4f517ee3fe822c19c1f88c97fbef7d4cc9125156244de95809f59e93c0118032ce04b").unwrap(),
hex::decode("0000002010fa7fe6e27ce9ad5f51bf00d5078a9e577f2d08115c1f00000000000000000062d112144604b9f9b83e4f421b234fdb17d0d7bd71590b2a1994ac73f0d9fb2759879f59e93c011870e590f2").unwrap(),
hex::decode("0000002065fee6168588d22fbe99ac61f5767f7d1f38408d4a15390000000000000000002ae612abb95ed62f051af05b0e942fd1763ef6f3011662c71d4f4d9c984c6da1dd899f59e93c01181231b844").unwrap(),
hex::decode("0200002024bf07643212552bd0d4fa4779762994eda338868ba20b010000000000000000700b14840728bb630ee467a705dacba32ef040e39245d4e18e0381a1fb42e5b2e2899f59e93c0118687dc2a8").unwrap(),
hex::decode("02000020fd21bdf774f4ab391ee951404c7c3d9df1f20a21fbd6be000000000000000000e948d27a0e531f5cbab3a5c5cb3f33cddf8228215ca3d934336f26cdecc8a587aa979f59e93c011835ccc946").unwrap(),
hex::decode("02000020153d617dd664fa4f49aa0ef145227331b4aef23a9beed0000000000000000000283f8529ed8419bbd45076bf1ef58a2f232c4cd869aa5e9a8687a932ea6b8cab2a9d9f59e93c011884f44a28").unwrap(),
hex::decode("0200002094a5ac9376865366f4494ff6782e9e0558eb485671451101000000000000000031dbb81b18060f9afa4a82a55391911102a01122b6e25bbad2227d036aa402cf69a19f59e93c0118a38dcf53").unwrap(),
hex::decode("1200002020311f7c3ea7fabfb3da39c120d7256421b8ba1f7c9d2700000000000000000043b494d744397958f9fcbdbc78e2fa238a65ce4a8c99c8cae41094bf5f620274dcab9f59e93c01187257ce43").unwrap(),
hex::decode("02000020be1808de4c0bc3a8cca96651411de3bcb04b03ca9cd5ad00000000000000000061248b66ec93f45b75d1fe61bc62994b769ac8fc6a5140c2e1fcd8ed3ad8734bf9ab9f59e93c0118c46a224d").unwrap(),
hex::decode("020000206de1e86d50d4043ea7b06394598eb0d39316de61ebdb1d010000000000000000cbad54eff729d2e964a3b97e754a01da55cdbc8924788f415ad0e5e04f030bfb6baf9f59e93c0118bc7d5352").unwrap(),
hex::decode("12000020971fb5337db4cfcdabe3d0a19414e22f200d0dcfa60ae9000000000000000000f1e07bba0d854c76d60dfbdab247d68ebfc80ba5e95d4d83997178653fdbdcc5d1b09f59e93c01180b0f8fab").unwrap(),
hex::decode("00000020b915dca0172c00dff21d436900f81fa55c4d0c94789708000000000000000000c0d80eedeab0a2e5ac800e4e0800b9a67a51d8095ff013977c56a5ee6a82564b91b19f59e93c0118626d6930").unwrap(),
hex::decode("0200002038b29a8148601f802389e59448bd915254374cd89e3e07000000000000000000000f9be6ad775720838d044faabaa507c1d27374710fe36b66e7e855f275603c11b59f59e93c01186830dc10").unwrap(),
hex::decode("00000020f66bbe99a28e70f00ed13fae994a12950025129ee29dbb0000000000000000006e2e590253d6d4de2cb44f95292681e8a8dfe425ba3a231aefb0e239241d338747b99f59e93c01186621bcd3").unwrap(),
hex::decode("020000200b449c16f78cc9f3f56e7eb84c8c535b29230866ccc410010000000000000000b201b9bc39851f34c3d68a80453de339560947fa76f0d3d28aaa0fe6f3c09f0870bc9f59e93c0118a6f6bf4b").unwrap(),
hex::decode("020000209073c42915b4ec2f93cb625299befbbe115867f9ffd6a400000000000000000065e326a16c150c95b145deff23e31b829ec224e16d62f426037fa0a9f515d96c11bf9f59e93c01189c3db0ca").unwrap(),
hex::decode("0200002000a52727e49fa397f8f211204c5a0bc40497b9b142c311000000000000000000ac88a88861b6c454b4dfc4bb8e4cd2fb475cacbb003c80bb4dcc44d89e324ef4ddbf9f59e93c01184ca16dab").unwrap(),
hex::decode("00000020643522aca18b6dd72993667995c1a0ed0db40a8ccfb014010000000000000000536ef1c8b3b657824e7dcf7650cb37bf8916773d0961ea46039880e81150dce130c19f59e93c01189304ead8").unwrap(),
hex::decode("000000209f39c386609cf5fcde82901e09e3c21614d6c91bf1bf500000000000000000006a2196cfa5da0d2bfcddbafb8ef33c3535e2c6abfbbf0b9be5668a7d382d182b95c79f59e93c0118d7c51033").unwrap(),
hex::decode("020000204171fce850d6b9972bc84b5c129097700fcc940801daed000000000000000000c802782b023172b77fd0d67868e2c6fff4691b9081385c0b1fa8ee9b0edb9488afc89f59e93c0118dc47db2e").unwrap(),
hex::decode("000000208229fd4875d341096fe972b42ec09c37ba5b3f5761307a000000000000000000f6d6b2238c3510a57ea4c85efa9ce8131a2e0e18b5db6ccb3ba62cfdb360d79a7ecd9f59e93c011878faaac7").unwrap(),
hex::decode("0200002016209868f23cee45d08d78e0a0a407cd2f44163e44cb3901000000000000000093d2238bcdc26dc75023b9c5118928bad163e39c2f71df8e95d6edd751ba79e648cf9f59e93c01188440a013").unwrap(),
hex::decode("000000209207740837717a41d191055edaca417441ba8a518a008d000000000000000000ca7617ee77e441b43320206c73b0ab694e1a6e5cae68de543906bc78884ec96f54d19f59e93c0118ea3cdbf7").unwrap(),
hex::decode("0200002095de633721183adca50ee64eb432278067f271ed5c6d110000000000000000008d58a1f0310aaa879af6d1567042c56316d0944a3745a48477eee8c0a9b160e0ded59f59e93c011840274171").unwrap(),
hex::decode("00000020eac6f48951848edb142e7d2cb65e5325e168eff217a9b100000000000000000018a5f3609c392597179460b0eb4f552150ea919674c07110541b3b937d61895bbcd69f59e93c0118990775cc").unwrap(),
hex::decode("00000020cd852383fdc3580f03d70427842597998ff05d642c301700000000000000000016d1f41abbeb84cf1260a156bb107f1831e1d4043087a62b91a802b6504ae82100de9f59e93c0118a2caa3e0").unwrap(),
hex::decode("02000020ba3564570e67b5e9258a0741a9b0a8c7eb21fc531e47340000000000000000008d51759065e5b563aa22534da2fe013cb78081b109a430f5224f2e3de395c31423de9f59e93c0118f387b5b2").unwrap(),
hex::decode("000000205dd7d70d5b3b8654915a80a502636d762de4349b7ea35e0000000000000000005fad44d833d5963038adccc421a1f027e3ecdd321d94ce2981958a92d543a9823fde9f59e93c011809f75b68").unwrap(),
hex::decode("02000020f6a223280df450a99052b9644fdbbb17c873cc650079c2000000000000000000ce4ecb69781542236a03b3f1ac1bcf33ff0c5392ea1e10810092c71607ed9b5cadec9f59e93c011831acafd8").unwrap(),
hex::decode("00000020e572b677fdbc2c58ccfd064be23380d1a48880e2f57630010000000000000000de94574c2ba3c13464037089196614b4c7d3cb4d5af1fd8c42b7dc48099ddb29ecef9f59e93c0118718ceb94").unwrap(),
hex::decode("020000202b8b12967db07d4ebb88aa85ef66116c191a93e99b27120100000000000000003fd47c683e1e17d680a9110af97f0ca4c645760d1a2b9934dce5f2e88be5093c7af29f59e93c01185714a38b").unwrap(),
hex::decode("000000207880fe8eb9ce3c0868742ae98e71b18e23bc94f62ab02a010000000000000000817fb1fa49e3aff325c1b0e445629e06d31699b565daebf741b1b731d1e3db4a8cfc9f59e93c0118fc165574").unwrap(),
hex::decode("00000020eae605b0e27d01a4a967f8b2788222bdf7d06c3f2cffb70000000000000000004647da086d8d604990e4e95a832430372fa388532599f5f2dc2c4248d6c62019c1fc9f59e93c0118346c62f4").unwrap(),
hex::decode("00000020fb5de90e254a00c4f2e88b478d927e97f8cc7bcde99de7000000000000000000d31910146254c1034ee847eeb991d7a758be6b475bcfa2039ed8b8746160c6365bff9f59e93c01184568f141").unwrap(),
hex::decode("0000002001d8d41aac3bc8be7ec45a469db006bfd484e174c86d1400000000000000000080707a5e00bf7ee43650b1428d3fb7aa3ae10edce68fc1c44c0d50042251eee8c102a059e93c0118c28a0a25").unwrap(),
hex::decode("00000020f4d340137354701e5acbaed498ebd833cb0bf6d4d4c0a400000000000000000058972fc8cad5065ec473d657070631c769714d5245f2a1c60819c0d58f2110d67306a059e93c01188ad1f723").unwrap(),
hex::decode("000000201a0c546ed7a8560f402810f7db7e9ed31bb9ee33cca548000000000000000000fbc835eee64458ccf98e4e9b2f464b74c4a8f19ca62d88e91d370a5140ca849f8609a059e93c0118f85d071c").unwrap(),
hex::decode("000000202ed3ce68191cf5f2a141e1beda144282aa3cb91c86bb3a0100000000000000006baff826721270cbb61b6e87a200878944de3bc288b89c1ad9f4660a90b0de592d0da059e93c01180913db03").unwrap(),
hex::decode("0000002037fefbaa4a417acddcff01b67e375aa274a5159b00cd250000000000000000000ad0731c0295fa7ad74b78ab37dda1760d98feb225c38932ebed3b3ab86010219d15a059e93c01180029355d").unwrap(),
hex::decode("020000208d3517e8c5ac9576de6376dbe18bbc3f68b714bd90ff06000000000000000000f09393c9f8cdb4fa5e45a556d7aa3ee3abc08ff175fab17c321144c1540da3aa9316a059e93c0118394eb993").unwrap(),
hex::decode("0000002093b6ad64d95790be03f40782e42dd217d91a062514b482000000000000000000b4ce3863a62be400d10c4b06f31f8d805c52df6bbcc9d672fbf1ed110394f015f916a059e93c01188f006b8b").unwrap(),
hex::decode("000000206af6d3062235cfbc8dbac1f5f33b790b48eb025e945677000000000000000000e0b4b06f31170c9cc54e623247a18d3dcb5b37a97799abadf5be22cac18ab650a818a059e93c01182a2eadc0").unwrap(),
hex::decode("00000020e44d7e3e4e7ed258fd9dabb368bd20f42a5134ac63a9eb00000000000000000047da7bf618553524026b5f6054372b9fc26e3f561345290b4b2775f65214d76a7d22a059e93c011889243aa1").unwrap(),
hex::decode("000000200428d486033251c65fc8921aeda5d627b4974b2ed8710a01000000000000000026049ae797b8658ddff66af57c00fff50d83d123ecf2d70c5f7b6f40537aabbe9526a059e93c0118568368f3").unwrap(),
hex::decode("000000204025364ce1f01cdfaf82e8c16665cb47a1b8156ce2c5be000000000000000000c4a56c66a75c261587536605fd4442dcfc190a4ddf5a13d6a063110e140bc5177e2ea059e93c0118fe232164").unwrap(),
hex::decode("0000002072dd4401dd1ee65956efb1ef3e3c468611d2c4cbb9dfae000000000000000000f6d476f9b69b756b87fb6596d16409bc5d40803489c5d52ffbbb03d12d594a174b30a059e93c0118ad048658").unwrap(),
hex::decode("020000200f2e93d616b03ffa6d2377bb6c8a5eb31385fdb30f5ac7000000000000000000f6187a2f8e73a661cc90ed86546b62f45ab68a174e56d08d2d93ae12eda9b181ed32a059e93c01184d5d246d").unwrap(),
hex::decode("020000207f3d929b96f2121056e0cad6a3363316d648a56a3baa2d0000000000000000001ba48a3c40fd114080ca67441bbf6c1b787b7caf4e3f19a124627493f0e3334e6234a059e93c0118b34d5e41").unwrap(),
hex::decode("0000002091c6841e0adf8d4bea707e3ab1e412b45bbfc80068b37a000000000000000000c273a936450ebd6f0ec9f16b04a955ccadd42fa446fb4ba0ac8cbd2874af988a2838a059e93c011889d44f30").unwrap(),
hex::decode("000000208a4af4109c84e53ca1ca671dc1ba75a55a2886f8eafd55000000000000000000e5b46a9cb2b8a975ab7c742fa57d942f5e52d0b92eff25954e916718f0eaaee1343aa059e93c0118e9904b69").unwrap(),
hex::decode("020000202d6cf055320a0e2781b77d1c0525e2897cd01cb03c9118010000000000000000fc8093d44e1279783a4d478a53bddcb55b58c4d06d51ea4bd8aeda28b66b2e7b423aa059e93c0118bc883a1e").unwrap(),
hex::decode("020000208d5c1f0e78fc6b87fba844848dd41a37bedebe409ebb0f0100000000000000008b34b89a7e9b732d940cb77f642cf9c9eabff198f369aa94220ebcdf07d3e138ab3aa059e93c0118e0078029").unwrap(),
hex::decode("02000020c639651d0a5acf604c4feac92e0879c16154ca9cad8c940000000000000000001802b2fb1bf4dd20a96a68aa416cd86325201b813ac9b3081d025e06a8632a2cfc3ca059e93c011864300336").unwrap(),
hex::decode("000000202ed62872636d3ae7803d9515f3a4360c0f096b08d9f8380100000000000000008476fe527a397800280d359dad7720ef220f2d749665fa8f3a5767a85d81a39f273fa059e93c0118ef87a752").unwrap(),
hex::decode("000000206bf7dfa1d97a1bfca63d76e41644ddbf34e1df525ff657000000000000000000cb05597fae3e31ae3a0dc3696bad38552e47b3d87987111e2ee602a8112f9495463fa059e93c0118d8578079").unwrap(),
hex::decode("0000002085dfc2bc2e4753ea4fa2079c69acc42a6d9da90b26530c00000000000000000069a1ec768f8b395c3efd9c04a7f11273b10589e1bfd4fa145fe26dc9cc603655d048a059e93c0118122e33dd").unwrap(),
];
let receiver_32: [u8; 32] =
hex::decode("6969696969696969696969696969696969696969696969696969696969696969")
.expect("Bytes32 from UTXO should be valid")
.try_into()
.expect("Receiver address must be exactly 32 bytes");
let receiver_account = AccountId32::from(receiver_32);
let transfer_amount: BalanceOf<T> = 140_693u64.unique_saturated_into();
let ancestry_headers_vec: Vec<Vec<u8>> = historical_hashes
.iter()
.take(max_history_depth)
.cloned()
.collect();
let gettxproof_bytes = hex::decode("008008201643a05d24fe1f015bf472fdedd8ef39dd8247568d3901000000000000000000569ac8f0aed0c5b632dd1dcc031f3d826a170e13cd8176430a2873a33d4694c2501f6a6ad43a02178dbac0d66b1600000e88be94dbcedc3f32bce789eb4512c68b39b91b54c4f2913e84baf7bae21bdc466d954b10876c1e8ad6591461027236c9a471883f96c028a7d1f08deb833f56fa18f457a6faa9c41340be484af67e444f8565b03085e1392fa2d56962f89919c4fcfff99fc6e92be067d01028fb54e8dc1e8425f0ef6bf1cc64220a2ba22163666eb01f99648e8c2af1bb1a0e1ba297f0244a09aa6a6ff87092312b02e8d75aef61e5c08f5b59184a45855e987c5ea114e5da61e55337bfae99f3a2e9601e72ae6036633dc97631609813a30d1185f98e7d6effc4a0330db0394da388c0058e9a730a62d8c679c35bf8d270ca8b95cec7d848729cdfd94a284f060f353b379bfc36310ac78306183a056a04156efd7e77661ce7ba50a4ef9f9e6b3681dfb39d8d0161ad0d2ec46834b5f270b67aa63a8a5b69287e8606b8a438785f8a5af66f84a2641438ba4a5fa22029227e9c2ddb6f23867b112c027d2fc2cf031a46c874701768b5c4983959b98d98736a2ed5ab0dd064ee24e49a3a7dac63e8b857ad4c8b5aa0cf22a7eac3180f3a54630b486de928ecf590e807d2e821818e2fe7e86610187d6673a6f95c639af80a7a7710b0088d44057ca61bdf5f4d5ba68d46b88e4304ffb70000").expect("Bytes32 from UTXO should be valid");
let gettxproof_bytes = hex::decode("00000020375a14cfcf5730daa8ff8d7a5bd2cf59fba797e289c2e0000000000000000000529cd017146361b57e6b7353b16d8d38d32f92ee842039dd779d35bfbfa2f3e53f649e59e93c01187900ea9fb40600000cfbd7b718845d123b1ae1a7fd3bce9c97acba097dc912c4ac67a920395db07ba4eef685960869ba70e0ca38890565c97dc11f1d7ae538e7b509069f81e38eff1a2e4a6b66fc4233d7d0580af4d195c7f0b274849f19d405dfc6b930bb9edd3f4a4c1f1e9fa3a336c50e8ea16039e4db4bef7ff5946e3a019937bdf626c2336d0e3926a83a575218f84e6dc55ffb9d0997a41e4b4d5425a85f22160c9f52362a95021a573ff96bb429bd80a8ec9dcb899971553efe91b6a0fe57c4694c3e8da8dc7f1babd514f2746da1ed06f08664baed853c334e0a3917d0c859b19ee14f448f77e624822070db3b606d5092027ca9ecf7e0243e6c151081021ebb3ddcdd28c1b8f9fdcffebd1962590477675f3d29d52602a1e7e941f772a3bbff42cf2eb6e74a1124bf424f767e294ae6253a21602c7c6b0381743dd6dcd57b025983e0e34375b24f67ac8beb00713d2f70594bbf797d5d94c061cb9965e6458f2f017f983d7ce37361fa13fda372f595dc1f22895ab93339457c72c480fed1c4d1164a242303ff3600").expect("Bytes32 from UTXO should be valid");
let getrawtransaction_bytes = hex::decode("010000000299a43fae581f80baf44e021ddf7b11995f184db282d6e48c22248b9fe79c43bb010000008b483045022100e9db01784dcd879c65d66127c2d52a4f9244321d15796893d6dccadaa1146e3d02203d9bb80f8df8b66692d3d9517f7cbb898d7cc40a30b175432322e1956c9b973a0141048097e4f8bb8ee104d5c2ece11d42f7e4719df8ddba459ecadecaba87d233c81a3c14830371392dd8ed030ef970450c0b7af48d9db4ba20c046896481602eb9dbffffffffb3fd05bc6000a5a0384e9a613d452e9307568df0a964907068e00403a0a0e09e020000006a47304402205bb1cbad4b29421648c1e22cac32a56aa06c933fe655559823fffb71ffebbc0b022021648d970560aa5b2031560b52a0fd06305995a58a457663324c18c1a9b0bd0b8121020d6e50b2660af27933c42bc1395fe93df90ffac5e2a989f6a134919fb8cf8075ffffffff030000000000000000296a2769642b5bb666ac1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e7c150000000000001976a914c1c69d91a5719a23ba2094d89a4df3eadfa4aef788acefea2309000000001976a9144635b1794a22bfbe6c5c5eba17b693f4aaf0e34888ac00000000").expect("Bytes32 from UTXO should be valid");
let dynamic_trusted_root = if ancestry_headers_vec.is_empty() {
let first_sha = sp_io::hashing::sha2_256(&gettxproof_bytes[..80]);
let mut native_le = sp_io::hashing::sha2_256(&first_sha);
native_le.reverse();
H256::from(native_le)
} else {
let last_header = ancestry_headers_vec.last().unwrap();
let first_sha = sp_io::hashing::sha2_256(&last_header[..80]);
let mut native_le = sp_io::hashing::sha2_256(&first_sha);
native_le.reverse();
H256::from(native_le)
};
let dynamic_trusted_root = H256::from(native_le);
LoomStates::<T>::insert(&network_id, &session, dynamic_trusted_root);
let payload = ThreadPayload::new(12, transfer_amount, receiver_account);
let utxo_thread_proof = UtxoThreadProof::new(
1,
0,
transfer_amount,
payload,
gettxproof_bytes,
hex::decode("020000000001029ee4235efd4a3cb183d4c311ce858eeee7748263a28afd3d861bac1cb4a8d368010000000001000000d7c5869bfffaa1877d0ecd37e46dc70c232a16f7955e635d57f926d1b3e640bf0100000000ffffffff02ff490b0000000000160014f635b17877709ba3c5acbe153f38a47a733f5b169525020000000000160014c6e4e0f822c31540b4b05903ce3c67b6cff8e6d40247304402200f2f3763fad59d38b4e101b9675450c0a54d45fe17e2b1babde0d0ce6051ea5b0220643e71d66752c0f1bd30dc0897305da77f223d14c781e86063ac181ad7fb90b6012102aad4cca3dab6427bc754a5a123de32cd037e7177fade9f72d83088b00066cffc0247304402200c8f1ff8230813e5e4ab8ec802c614bc8705c407c5958d64dd79cc8422d85a6202203ea832d581c404200b5974fd1a11abfe1ebac213842083cb02eba3f253c8c2aa012102d70df1d4d58701adab82b4816e0640c27403d1fab859ca9f47cb6eccca3dd96200000000").expect("Bytes32 from UTXO should be valid"),
getrawtransaction_bytes,
ancestry_headers_vec,
);
let thread_proof = ThreadProof::UtxoThreadProof(utxo_thread_proof);
let pulled_thread_key = thread_proof.get_unique_key(session);
let pulled_thread_key = thread_proof.get_unique_key(session, network_id);
assert!(!PulledThreads::<T>::contains_key(&pulled_thread_key));
@ -351,14 +333,15 @@ mod benchmarks {
#[benchmark]
fn pull_evm_thread() -> Result<(), BenchmarkError> {
let session = 69;
let (network_id, _, _) = prepare_pallet::<T>(1);
let session = 0;
let sepolia_network_id = 11155111;
let (network_id, _, _) = prepare_pallet::<T>(1, Some(sepolia_network_id));
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract
// getRoot: session 0, atBlock 11383380
// https://sepolia.etherscan.io/address/0x7fec11e652f4d54e8cf11c4b52c360380332f2bd#readContract
// getRoot: session 0, atBlock 11716130
let root_string =
hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d")
hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129")
.expect("EVM Root hex should be valid");
let expected_root = H256::from_slice(&root_string);
LoomStates::<T>::insert(&network_id, &session, expected_root);
@ -373,49 +356,24 @@ mod benchmarks {
let transfer_amount: BalanceOf<T> = 420u64.unique_saturated_into();
let proof = vec![
H256::from_slice(
&hex::decode("6dfb19f049f557abff273ce9efb9195924d41e5699b1b7755be2c515b25ef7b6")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead")
.expect("Proof should be valid"),
),
H256::from_slice(&hex::decode("7d831a4be9e37572687117870c3f96669a66bf07971e4fd705a36a0a4d6365b0").expect("Proof should be valid")),
H256::from_slice(&hex::decode("9f00b99751b7ccea1903949210408edb3ce50343985708c36a0b1d44e0a7628c").expect("Proof should be valid")),
H256::from_slice(&hex::decode("1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071").expect("Proof should be valid")),
H256::from_slice(&hex::decode("68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040").expect("Proof should be valid")),
];
let slots = vec![H256::from_slice(
&hex::decode("2e474dabf492786b3505d7b7cf0bf07c9dbce92c91982fa7344c77e24fd4310d")
&hex::decode("f6182b8c276793f53d1820ad0405665177516e46e40af45e0dfea73c39a2306e")
.expect("Slot should be valid hex"),
)];
let payload = ThreadPayload::new(1, transfer_amount, receiver_account);
let evm_thread_proof = EvmThreadProof::new(0, payload, proof, slots);
let evm_thread_proof = EvmThreadProof::new(0, 1, receiver_account, transfer_amount, proof, slots);
let thread_proof = ThreadProof::EvmThreadProof(evm_thread_proof);
let pulled_thread_key = thread_proof.get_unique_key(session);
let pulled_thread_key = thread_proof.get_unique_key(session, network_id);
assert!(!PulledThreads::<T>::contains_key(&pulled_thread_key));

View File

@ -25,7 +25,7 @@ use sp_runtime::{
use ghost_helpers::{
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
get_byzantium_threshold,
get_byzantium_quorum_threshold,
networks::{NetworkData, NetworkType},
};
use ghost_traits::{
@ -439,22 +439,21 @@ pub mod pallet {
.ok_or(Error::<T>::NonExistentNetworkId)?;
let root_hash = LoomStates::<T>::get(&network_id, &session);
let pulled_thread_key = thread_proof.get_unique_key(session);
let pulled_thread_key = thread_proof.get_unique_key(session, network_id);
ensure!(
!PulledThreads::<T>::contains_key(&pulled_thread_key),
Error::<T>::ThreadAlreadyPulled,
);
let receiver_account = thread_proof
.verify_proof(root_hash, &network_data.gatekeeper)
let (receiver_account, amount) = thread_proof
.verify_thread_proof(root_hash, network_id, &network_data.gatekeeper)
.ok_or(Error::<T>::InvalidMerkleProof)?;
let receiver_bytes: &[u8; 32] = receiver_account.as_ref();
let receiver = T::AccountId::decode(&mut &receiver_bytes[..])
.map_err(|_| Error::<T>::InvalidReceiverAddress)?;
let amount = thread_proof.amount();
let pure_amount =
T::NetworkDataHandler::register_incoming(&network_id, amount)
.map_err(|_| Error::<T>::CouldNotRegisterIncoming)?;
@ -487,7 +486,7 @@ pub mod pallet {
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 1));
let len = Authorities::<T>::decode_len().unwrap_or_default();
let weaving_threshold = get_byzantium_threshold(len);
let weaving_threshold = get_byzantium_quorum_threshold(len);
let maybe_tapestry_drafts = TapestryDrafts::<T>::get(&network_id);
@ -850,7 +849,7 @@ impl<T: Config> Pallet<T> {
.parse_block_requests(pending_requests, deadline, request_id);
let total_blocks = block_numbers.len();
let weaving_threshold = get_byzantium_threshold(total_blocks);
let weaving_threshold = get_byzantium_quorum_threshold(total_blocks);
let median_block = Self::validate_block_numbers_consensus(
&mut block_numbers,

View File

@ -14,8 +14,11 @@ use sp_runtime::codec::Encode;
use sp_core::{H256, crypto::Pair};
use sp_runtime::{
Perbill,
traits::{Dispatchable, ValidateUnsigned},
transaction_validity::{InvalidTransaction, TransactionSource, TransactionValidityError},
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidityError,
},
};
use sp_core::offchain::{
@ -65,7 +68,9 @@ fn should_successfully_compute_median_and_start_weaving_round() {
let pair3 = AuthorityPair::from_string("//Carol", None).expect("Should be valid seed");
let network_id = 1;
let network_data = NetworkDataBuilder::default().build();
let network_data = NetworkDataBuilder::default()
.with_block_deviation(5)
.build();
let authorities = vec![pair1.clone(), pair2.clone(), pair3.clone()];
let networks = vec![(network_id, network_data, Default::default())];
@ -122,8 +127,8 @@ fn should_apply_cooldown_delay_on_consensus_failure() {
.with_network_id(network_id);
let att1 = context.with_authority_index(0).build_block_attestation(100);
let att2 = context.with_authority_index(1).build_block_attestation(500);
let att3 = context.with_authority_index(2).build_block_attestation(900);
let att2 = context.with_authority_index(1).build_block_attestation(102);
let att3 = context.with_authority_index(2).build_block_attestation(104);
let sig1 = pair1.sign(&att1.encode());
let sig2 = pair2.sign(&att2.encode());
@ -555,7 +560,7 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
fn should_successfully_pull_evm_thread_and_mint_currency() {
let pair = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let network_id = 1;
let network_id = 11155111;
let network_data = NetworkDataBuilder::default()
.with_network_type(NetworkType::Evm)
.build();
@ -563,204 +568,78 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
let authorities = vec![pair.clone()];
let networks = vec![(network_id, network_data, Default::default())];
// Contract address: 0x9a52C28dA655b69d8ddc430d6ebD04630BA0e845
// Contract address: 0x7fec11e652f4d54e8cf11c4b52c360380332f2bd
//
// three inserts
// 69 sfJ9hc4A7r578XM35nd48B3FWcd33XjpVnAbGC5tvtwthMWdU - 0xc477f0a42fdd861485bd854c7a5851614ea163bd295f7844e1c5493d662fdc4e
// 420 sfDxNicCEPts6CVttVMPCs2eoymDpQahu1GUVyGPWu6dhwADJ - 0x0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e
// 1337 sfH6zJ63fcxrFAZ1RmiHbxgy2t3sEufY26WdsBSS359AzJd4w - 0x962a2794f66c89e55d9b9692ce59f67345b99f6fddb51d90462c7eca8b5a7e1b
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract
// getRoot: atBlock 11383380, globalIndexes from 0 to 2
// https://sepolia.etherscan.io/address/0x7fec11e652f4d54e8cf11c4b52c360380332f2bd#readContract
// getRoot: atBlock 11716130, globalIndexes from 0 to 2
let inserts = vec![
(
0,
hex::decode("c477f0a42fdd861485bd854c7a5851614ea163bd295f7844e1c5493d662fdc4e")
.expect("Bytes32 from EVM should be valid"),
hex::decode("c477f0a42fdd861485bd854c7a5851614ea163bd295f7844e1c5493d662fdc4e").expect("Bytes32 from EVM should be valid"),
69u64,
vec![
H256::from_slice(
&hex::decode(
"c7a710c9acbc193692f34ac1f1979013b04240a4c0d02b49b9178f17721350b1",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead",
)
.expect("Proof should be valid"),
),
H256::from_slice(&hex::decode("2d931b11b1652669a04e7b5b50418f5e1e2fabb6791dabc9a196e719ad5cf9c6").expect("Proof should be valid")),
H256::from_slice(&hex::decode("9f00b99751b7ccea1903949210408edb3ce50343985708c36a0b1d44e0a7628c").expect("Proof should be valid")),
H256::from_slice(&hex::decode("1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071").expect("Proof should be valid")),
H256::from_slice(&hex::decode("68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040").expect("Proof should be valid")),
],
vec![H256::from_slice(
&hex::decode("350a78f78c810898b7c60ddba671543bfbe683ffb0ac393df0e132f607b5f738")
.expect("Slot should be valid hex"),
&hex::decode("f494b2065ca61f2af007a3dd5f1f3d228a3ad09f4f7b7c5ef3eeb3aee6718ee2").expect("Slot should be valid hex"),
)],
),
(
1,
hex::decode("0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e")
.expect("Bytes32 from EVM should be valid"),
hex::decode("0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e").expect("Bytes32 from EVM should be valid"),
420u64,
vec![
H256::from_slice(
&hex::decode(
"6dfb19f049f557abff273ce9efb9195924d41e5699b1b7755be2c515b25ef7b6",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead",
)
.expect("Proof should be valid"),
),
H256::from_slice(&hex::decode("7d831a4be9e37572687117870c3f96669a66bf07971e4fd705a36a0a4d6365b0").expect("Proof should be valid")),
H256::from_slice(&hex::decode("9f00b99751b7ccea1903949210408edb3ce50343985708c36a0b1d44e0a7628c").expect("Proof should be valid")),
H256::from_slice(&hex::decode("1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071").expect("Proof should be valid")),
H256::from_slice(&hex::decode("68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040").expect("Proof should be valid")),
],
vec![H256::from_slice(
&hex::decode("2e474dabf492786b3505d7b7cf0bf07c9dbce92c91982fa7344c77e24fd4310d")
.expect("Slot should be valid hex"),
&hex::decode("f6182b8c276793f53d1820ad0405665177516e46e40af45e0dfea73c39a2306e").expect("Slot should be valid hex"),
)],
),
(
2,
hex::decode("962a2794f66c89e55d9b9692ce59f67345b99f6fddb51d90462c7eca8b5a7e1b")
.expect("Bytes32 from EVM should be valid"),
hex::decode("962a2794f66c89e55d9b9692ce59f67345b99f6fddb51d90462c7eca8b5a7e1b").expect("Bytes32 from EVM should be valid"),
1337u64,
vec![
H256::from_slice(
&hex::decode(
"290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"82ca531d09faff937c9b68f53d473c9f2c88bdbc8b1629af894479367881db5c",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7",
)
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead",
)
.expect("Proof should be valid"),
),
H256::from_slice(&hex::decode("f39a869f62e75cf5f0bf914688a6b289caf2049435d8e68c5c5e6d05e44913f3").expect("Proof should be valid")),
H256::from_slice(&hex::decode("385de173dc8295d4153b2de3bd705dd96556c8c316a996b3e56adce0970ffa36").expect("Proof should be valid")),
H256::from_slice(&hex::decode("1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071").expect("Proof should be valid")),
H256::from_slice(&hex::decode("68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f").expect("Proof should be valid")),
H256::from_slice(&hex::decode("de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040").expect("Proof should be valid")),
],
vec![H256::from_slice(
&hex::decode("bde870fce92d701a7a5d6f63003e1cd2cb61c3266fc1be256844b8ca61d9c9ab")
.expect("Slot should be valid hex"),
&hex::decode("2f52c7d2d3f4167c436f3b6cc0a7d944a45846ba87f74998100b83122d5eb0ad").expect("Slot should be valid hex"),
)],
),
];
new_test_ext(authorities, networks).execute_with(|| {
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract
// getRoot: session 0, atBlock 11383380
// https://sepolia.etherscan.io/address/0x7fec11e652f4d54e8cf11c4b52c360380332f2bd#readContract
// getRoot: session 0, atBlock 11716130
let session = 0;
let root_string =
hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d")
hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129")
.expect("Root hex should be valid");
let expected_root = H256::from_slice(&root_string);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root);
@ -777,17 +656,20 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
let receiver_account = sp_runtime::AccountId32::from(receiver_32);
let transfer_amount = insert.2;
let payload =
ThreadPayload::new(proof_index, transfer_amount, receiver_account.clone());
let proof = insert.3;
let slots = insert.4;
let evm_thread_proof = EvmThreadProof::new(slot_index, payload, proof, slots);
let evm_thread_proof = EvmThreadProof::new(
slot_index,
proof_index,
receiver_account.clone(),
transfer_amount,
proof,
slots,
);
let thread_info = ThreadProof::EvmThreadProof(evm_thread_proof);
let pulled_thread_key = thread_info.get_unique_key(session);
let pulled_thread_key = thread_info.get_unique_key(session, network_id);
assert_eq!(Balances::free_balance(&receiver_account), 0);
assert!(!PulledThreads::<TestRuntime>::contains_key(
@ -798,8 +680,7 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
network_id,
session,
thread_info
)
.is_ok());
).is_ok());
assert!(PulledThreads::<TestRuntime>::contains_key(
&pulled_thread_key
@ -826,13 +707,15 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
#[test]
fn should_successfully_pull_utxo_thread_and_mint_currency() {
let pair = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let incoming_share = 500_000_000;
let network_id = 69;
let network_data = NetworkDataBuilder::default()
.with_network_type(NetworkType::Utxo)
.with_incoming_share(incoming_share)
.with_gatekeeper(
// Transaction: https://blockstream.info/tx/c41999f86269d5a22f39e18530b065854f447ef64a48be4013c4a9faa657f418
hex::decode("c6e4e0f822c31540b4b05903ce3c67b6cff8e6d4000000000000000000000000")
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
hex::decode("4635b1794a22bfbe6c5c5eba17b693f4aaf0e348")
.expect("Bytes32 from UTXO should be valid"),
)
.build();
@ -840,62 +723,44 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
let authorities = vec![pair.clone()];
let networks = vec![(network_id, network_data, Default::default())];
// Real world tx at block #960127
// Block: https://blockstream.info/block/0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1
let inserts = vec![
(
// Transaction: https://blockstream.info/tx/c41999f86269d5a22f39e18530b065854f447ef64a48be4013c4a9faa657f418
// We are looking into second output and first witness
// NOTE: would be nice to use on of our txs
hex::decode("6969696969696969696969696969696969696969696969696969696969696969").expect("Bytes32 from UTXO should be valid"),
140_693u64,
1,
0,
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "gettxoutproof", "params": [["c41999f86269d5a22f39e18530b065854f447ef64a48be4013c4a9faa657f418"], "0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1"]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("008008201643a05d24fe1f015bf472fdedd8ef39dd8247568d3901000000000000000000569ac8f0aed0c5b632dd1dcc031f3d826a170e13cd8176430a2873a33d4694c2501f6a6ad43a02178dbac0d66b1600000e88be94dbcedc3f32bce789eb4512c68b39b91b54c4f2913e84baf7bae21bdc466d954b10876c1e8ad6591461027236c9a471883f96c028a7d1f08deb833f56fa18f457a6faa9c41340be484af67e444f8565b03085e1392fa2d56962f89919c4fcfff99fc6e92be067d01028fb54e8dc1e8425f0ef6bf1cc64220a2ba22163666eb01f99648e8c2af1bb1a0e1ba297f0244a09aa6a6ff87092312b02e8d75aef61e5c08f5b59184a45855e987c5ea114e5da61e55337bfae99f3a2e9601e72ae6036633dc97631609813a30d1185f98e7d6effc4a0330db0394da388c0058e9a730a62d8c679c35bf8d270ca8b95cec7d848729cdfd94a284f060f353b379bfc36310ac78306183a056a04156efd7e77661ce7ba50a4ef9f9e6b3681dfb39d8d0161ad0d2ec46834b5f270b67aa63a8a5b69287e8606b8a438785f8a5af66f84a2641438ba4a5fa22029227e9c2ddb6f23867b112c027d2fc2cf031a46c874701768b5c4983959b98d98736a2ed5ab0dd064ee24e49a3a7dac63e8b857ad4c8b5aa0cf22a7eac3180f3a54630b486de928ecf590e807d2e821818e2fe7e86610187d6673a6f95c639af80a7a7710b0088d44057ca61bdf5f4d5ba68d46b88e4304ffb70000").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getrawtransaction", "params": ["c41999f86269d5a22f39e18530b065854f447ef64a48be4013c4a9faa657f418", true]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("020000000001029ee4235efd4a3cb183d4c311ce858eeee7748263a28afd3d861bac1cb4a8d368010000000001000000d7c5869bfffaa1877d0ecd37e46dc70c232a16f7955e635d57f926d1b3e640bf0100000000ffffffff02ff490b0000000000160014f635b17877709ba3c5acbe153f38a47a733f5b169525020000000000160014c6e4e0f822c31540b4b05903ce3c67b6cff8e6d40247304402200f2f3763fad59d38b4e101b9675450c0a54d45fe17e2b1babde0d0ce6051ea5b0220643e71d66752c0f1bd30dc0897305da77f223d14c781e86063ac181ad7fb90b6012102aad4cca3dab6427bc754a5a123de32cd037e7177fade9f72d83088b00066cffc0247304402200c8f1ff8230813e5e4ab8ec802c614bc8705c407c5958d64dd79cc8422d85a6202203ea832d581c404200b5974fd1a11abfe1ebac213842083cb02eba3f253c8c2aa012102d70df1d4d58701adab82b4816e0640c27403d1fab859ca9f47cb6eccca3dd96200000000").expect("Bytes32 from UTXO should be valid"),
// Real world tx at block #481838
// Block https://blockstream.info/block/0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
let payload = (
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "gettxoutproof", "params": [["4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e"], "0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a"]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("00000020375a14cfcf5730daa8ff8d7a5bd2cf59fba797e289c2e0000000000000000000529cd017146361b57e6b7353b16d8d38d32f92ee842039dd779d35bfbfa2f3e53f649e59e93c01187900ea9fb40600000cfbd7b718845d123b1ae1a7fd3bce9c97acba097dc912c4ac67a920395db07ba4eef685960869ba70e0ca38890565c97dc11f1d7ae538e7b509069f81e38eff1a2e4a6b66fc4233d7d0580af4d195c7f0b274849f19d405dfc6b930bb9edd3f4a4c1f1e9fa3a336c50e8ea16039e4db4bef7ff5946e3a019937bdf626c2336d0e3926a83a575218f84e6dc55ffb9d0997a41e4b4d5425a85f22160c9f52362a95021a573ff96bb429bd80a8ec9dcb899971553efe91b6a0fe57c4694c3e8da8dc7f1babd514f2746da1ed06f08664baed853c334e0a3917d0c859b19ee14f448f77e624822070db3b606d5092027ca9ecf7e0243e6c151081021ebb3ddcdd28c1b8f9fdcffebd1962590477675f3d29d52602a1e7e941f772a3bbff42cf2eb6e74a1124bf424f767e294ae6253a21602c7c6b0381743dd6dcd57b025983e0e34375b24f67ac8beb00713d2f70594bbf797d5d94c061cb9965e6458f2f017f983d7ce37361fa13fda372f595dc1f22895ab93339457c72c480fed1c4d1164a242303ff3600").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getrawtransaction", "params": ["4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("010000000299a43fae581f80baf44e021ddf7b11995f184db282d6e48c22248b9fe79c43bb010000008b483045022100e9db01784dcd879c65d66127c2d52a4f9244321d15796893d6dccadaa1146e3d02203d9bb80f8df8b66692d3d9517f7cbb898d7cc40a30b175432322e1956c9b973a0141048097e4f8bb8ee104d5c2ece11d42f7e4719df8ddba459ecadecaba87d233c81a3c14830371392dd8ed030ef970450c0b7af48d9db4ba20c046896481602eb9dbffffffffb3fd05bc6000a5a0384e9a613d452e9307568df0a964907068e00403a0a0e09e020000006a47304402205bb1cbad4b29421648c1e22cac32a56aa06c933fe655559823fffb71ffebbc0b022021648d970560aa5b2031560b52a0fd06305995a58a457663324c18c1a9b0bd0b8121020d6e50b2660af27933c42bc1395fe93df90ffac5e2a989f6a134919fb8cf8075ffffffff030000000000000000296a2769642b5bb666ac1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e7c150000000000001976a914c1c69d91a5719a23ba2094d89a4df3eadfa4aef788acefea2309000000001976a9144635b1794a22bfbe6c5c5eba17b693f4aaf0e34888ac00000000").expect("Bytes32 from UTXO should be valid"),
vec![
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["0000000000000000000084bd8092a07bc89245c2bf05f2cf75851bcc5ef92f70", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("00a01520c1d508977a3787523a0f959e14a8df763dc87927c7910000000000000000000036303f2bcf94f752a1d4b0641d1adbc2d80aa89628feefd7f837a74792e26ab8bb276a6ad43a0217bece2e64").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["000000000000000000016792d9c1460bfa4db07da3b724db76c3dd0640ca5754", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("0400e020702ff95ecc1b8575cff205bfc24592c87ba09280bd84000000000000000000008ca9c40d5ab762f160661635cade50fd14571dcf5fdf8c463b617caf9b07dc5a3b296a6ad43a02171340d661").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["0000000000000000000078eb1af63fe8d57f93079121e19968a8563c05af370e", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("000000345457ca4006ddc376db24b7a37db04dfa0b46c1d99267010000000000000000009409d64eb06a6dd83ae2937f70d07222d75505a07f12e756b61a27189851e3bbfe2c6a6ad43a0217a06575b5").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["000000000000000000e7da64065cc23d5c5a5aa402f088aaaf37c5fe1c3b6b92", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("000000204a9037b3465b3c657997cddcffed602a949ee139cbdc1b010000000000000000669c3e2472384bc95993f154aada38d6b2113d8d32069e32e2724ba40915a1802a669e59e93c011801c8c735").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["00000000000000000063da6d9362c0ac3b87877d1c5ad7afd5bd706571d5b393", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("02000020926b3b1cfec537afaa88f002a45a5a5c3dc25c0664dae7000000000000000000af0f270bf3f868d47089f395197809c360f1506636c960d96afed68bbd72d0b4496d9e59e93c01183ac94b67").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["000000000000000000ec6063eb743366264368be7ff5021727682cb518963026", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("0000002093b3d5716570bdd5afd75a1c7d87873bacc062936dda630000000000000000009ab437e7c66b10a59b67127f290d24f381aba4e03d5e16c062a356eea4d608dea56d9e59e93c0118264f310e").expect("Bytes32 from UTXO should be valid"),
],
)
];
);
new_test_ext(authorities, networks).execute_with(|| {
let session = 0;
let root_string =
hex::decode("0000000000000000000078eb1af63fe8d57f93079121e19968a8563c05af370e")
let block_hash =
hex::decode("000000000000000000ec6063eb743366264368be7ff5021727682cb518963026")
.expect("Bitcoin Root hex should be valid");
let expected_root = H256::from_slice(&root_string);
let expected_root = H256::from_slice(&block_hash);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root);
for insert in inserts.into_iter() {
let receiver_32: [u8; 32] = insert
.0
.try_into()
.expect("Receiver address must be exactly 32 bytes");
let receiver_account = sp_runtime::AccountId32::from(receiver_32);
let transfer_amount = insert.1;
let expected_receiver_bytes = hex::decode("1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e").unwrap();
let mut receiver_raw = [0u8; 32];
receiver_raw.copy_from_slice(&expected_receiver_bytes);
let payload = ThreadPayload::new(12, transfer_amount, receiver_account.clone());
let utxo_thread_proof = UtxoThreadProof::new(
insert.2,
insert.3,
transfer_amount,
payload,
insert.4,
insert.5,
insert.6,
);
let receiver_account = sp_runtime::AccountId32::from(receiver_raw);
let transfer_amount: u64 = 153348847;
let utxo_thread_proof = UtxoThreadProof::new(payload.0, payload.1, payload.2);
let thread_info = ThreadProof::UtxoThreadProof(utxo_thread_proof);
let pulled_thread_key = thread_info.get_unique_key(session);
let pulled_thread_key = thread_info.get_unique_key(session, network_id);
assert_eq!(Balances::free_balance(&receiver_account), 0);
assert!(!PulledThreads::<TestRuntime>::contains_key(
@ -907,15 +772,14 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
network_id,
session,
thread_info
)
.is_ok());
).is_ok());
assert!(PulledThreads::<TestRuntime>::contains_key(
&pulled_thread_key
));
let expected_commission = 0u64;
let expected_pure_amount = transfer_amount - expected_commission;
let curve_share = Perbill::from_parts(incoming_share).mul_ceil(transfer_amount);
let expected_pure_amount = transfer_amount.saturating_sub(curve_share);
assert_eq!(
Balances::free_balance(&receiver_account),
@ -928,12 +792,11 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
receiver: receiver_account,
amount: transfer_amount,
}));
}
});
}
#[test]
fn on_initialize_should_finalize_session_when_threshold_or_timeout_is_reached() {
fn on_initialize_should_finalize_session_when_timeout_is_reached() {
let pair1 = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let pair2 = AuthorityPair::from_string("//Bob", None).expect("Should be valid seed");
let pair3 = AuthorityPair::from_string("//Charlie", None).expect("Should be valid seed");
@ -995,14 +858,12 @@ fn on_initialize_should_finalize_session_when_threshold_or_timeout_is_reached()
let current_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
GhostWeaver::on_initialize(current_substrate_block);
let saved_loot = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_eq!(saved_loot, target_hash);
let saved_loom = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_ne!(saved_loom, target_hash);
assert_eq!(saved_loom, saved_loom);
let updated_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
assert_eq!(updated_session, current_session + 1);
System::set_block_number(15);
GhostWeaver::on_initialize(15);
assert_eq!(updated_session, current_session);
});
}

View File

@ -31,6 +31,11 @@ const UTXO_MAX_HISTORY_DEPTH: u32 = UTXO_MAX_HISTORY_DEPTH_VALUE;
#[cfg(feature = "runtime-benchmarks")]
pub const UTXO_MAX_HISTORY_DEPTH: u32 = UTXO_MAX_HISTORY_DEPTH_VALUE;
#[cfg(any(test, feature = "runtime-benchmarks"))]
const PROTOCOL_PREFIX: [u8; 7] = [0x69, 0x64, 0x2b, 0x5b, 0xb6, 0x66, 0xac];
#[cfg(not(any(test, feature = "runtime-benchmarks")))]
const PROTOCOL_PREFIX: [u8; 7] = [0x47, 0x48, 0x53, 0x54, 0x74, 0x6F, 0x3A];
pub enum WeavingOk<NetworkId> {
Block(ExternalBlockNumber, NetworkId),
Hash(ExternalBlockNumber, H256, WeavingSession, NetworkId),
@ -272,75 +277,51 @@ where
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ThreadPayload<Balance: UniqueSaturatedInto<u128>> {
proof_index: ThreadId,
amount: Balance,
receiver: AccountId32,
}
impl<Balance> ThreadPayload<Balance>
where
Balance: UniqueSaturatedInto<u128>,
{
pub fn new(proof_index: ThreadId, amount: Balance, receiver: AccountId32) -> Self {
Self {
proof_index,
amount,
receiver,
}
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum ThreadProof<Balance: UniqueSaturatedInto<u128>> {
EvmThreadProof(EvmThreadProof<Balance>),
UtxoThreadProof(UtxoThreadProof<Balance>),
UtxoThreadProof(UtxoThreadProof),
}
impl<Balance> ThreadProof<Balance>
where
Balance: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u128>,
u64: UniqueSaturatedInto<Balance>,
{
pub fn verify_thread_proof<NetworkId>(
&self,
root_hash: H256,
network_id: NetworkId,
gatekeeper: &[u8]
) -> Option<(AccountId32, Balance)>
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
pub fn is_evm_proof_correct(&self) -> bool {
match self {
Self::EvmThreadProof(_) => true,
Self::UtxoThreadProof(_) => false,
Self::UtxoThreadProof(utxo_thread) => {
utxo_thread.verify_thread_proof(root_hash, gatekeeper)
},
Self::EvmThreadProof(evm_thread) => {
evm_thread.verify_thread_proof(root_hash, network_id)
},
}
}
pub fn is_utxo_proof_correct(&self) -> bool {
pub fn get_unique_key<NetworkId>(
&self,
session: WeavingSession,
network_id: NetworkId,
) -> H256
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
match self {
Self::UtxoThreadProof(_) => true,
Self::EvmThreadProof(_) => false,
}
}
pub fn amount(&self) -> Balance {
match self {
Self::UtxoThreadProof(utxo_thread) => utxo_thread.amount(),
Self::EvmThreadProof(evm_thread) => evm_thread.amount(),
}
}
pub fn proof_index(&self) -> ThreadId {
match self {
Self::UtxoThreadProof(utxo_thread) => utxo_thread.proof_index(),
Self::EvmThreadProof(evm_thread) => evm_thread.proof_index(),
}
}
pub fn verify_proof(&self, root_hash: H256, gatekeeper: &[u8]) -> Option<AccountId32> {
match self {
Self::UtxoThreadProof(utxo_thread) => utxo_thread.verify_proof(root_hash, gatekeeper),
Self::EvmThreadProof(evm_thread) => evm_thread.verify_proof(root_hash),
}
}
pub fn get_unique_key(&self, session: WeavingSession) -> H256 {
match self {
Self::EvmThreadProof(thread_info) => thread_info.get_unique_key(session),
Self::UtxoThreadProof(utxo_thread) => utxo_thread.get_unique_key(),
Self::EvmThreadProof(thread_info) => {
thread_info.get_unique_key(session, network_id)
},
Self::UtxoThreadProof(utxo_thread) => {
utxo_thread.get_unique_key(network_id)
},
}
}
}
@ -348,7 +329,9 @@ where
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct EvmThreadProof<Balance: UniqueSaturatedInto<u128>> {
slot_index: ThreadId,
payload: ThreadPayload<Balance>,
proof_index: ThreadId,
receiver: AccountId32,
amount: Balance,
proof: BoundedVec<H256, ConstU32<EVM_MERKLE_DEPTH>>,
slots: BoundedVec<H256, ConstU32<EVM_VALUES_DEPTH>>,
}
@ -357,70 +340,112 @@ impl<Balance> EvmThreadProof<Balance>
where
Balance: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u128>,
{
pub fn amount(&self) -> Balance {
self.payload.amount.clone()
}
pub fn proof_index(&self) -> ThreadId {
self.payload.proof_index
}
pub fn verify_proof(&self, root_hash: H256) -> Option<AccountId32> {
if EVM_MERKLE_DEPTH as usize != self.proof.len() {
return None;
}
pub fn verify_thread_proof<NetworkId>(
&self,
root_hash: H256,
network_id: NetworkId,
) -> Option<(AccountId32, Balance)>
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
if EVM_MERKLE_DEPTH as usize != self.proof.len() { return None; }
let proof_valid = verify_tree_proof::<SubstrateKeccakHasher, _>(
&self.get_preimage_slice().to_fixed_bytes(),
&self.get_preimage_slice(network_id).to_fixed_bytes(),
self.proof.as_ref(),
root_hash,
self.payload.proof_index,
self.proof_index,
);
if !proof_valid {
return None;
if !proof_valid { return None; }
Some((self.receiver.clone(), self.amount))
}
Some(self.payload.receiver.clone())
}
fn get_preimage_slice(&self) -> H256 {
fn get_preimage_slice<NetworkId>(&self, network_id: NetworkId) -> H256
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
let slot_index_usize: usize = self.slot_index.unique_saturated_into();
let mut slot_values: Vec<H256> = self.slots.to_vec();
(slot_index_usize < self.slots.len())
.then(|| {
self.slots
.iter()
.enumerate()
.map(|(index, &preimage)| {
if index == slot_index_usize {
self.compute_arguments_hash()
} else {
preimage
match slot_values.get_mut(slot_index_usize) {
Some(slot) => {
*slot = self.compute_arguments_hash(network_id);
cumulative_hash::<SubstrateKeccakHasher, _>(slot_values)
}
None => {
SubstrateKeccakHasher::empty()
}
}
})
.collect::<Vec<_>>()
})
.map(|slots| cumulative_hash::<SubstrateKeccakHasher, _>(slots))
.unwrap_or(SubstrateKeccakHasher::empty())
}
pub fn get_unique_key(&self, session: WeavingSession) -> H256 {
pub fn get_unique_key<NetworkId>(
&self,
session: WeavingSession,
network_id: NetworkId,
) -> H256
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
let session_bytes = (session as u64).to_be_bytes();
let slot_bytes = (self.slot_index as u64).to_be_bytes();
let proof_bytes = (self.payload.proof_index as u64).to_be_bytes();
let slot_index_bytes = (self.slot_index as u64).to_be_bytes();
let proof_index_bytes = (self.proof_index as u64).to_be_bytes();
let network_id_u64: u64 = network_id.unique_saturated_into();
let network_id_bytes = network_id_u64.to_be_bytes();
let mut h1 = [0u8; 32]; h1[24..32].copy_from_slice(&session_bytes);
let mut h2 = [0u8; 32]; h2[24..32].copy_from_slice(&slot_bytes);
let mut h3 = [0u8; 32]; h3[24..32].copy_from_slice(&proof_bytes);
let mut h2 = [0u8; 32]; h2[24..32].copy_from_slice(&slot_index_bytes);
let mut h3 = [0u8; 32]; h3[24..32].copy_from_slice(&proof_index_bytes);
let mut h4 = [0u8; 32]; h4[24..32].copy_from_slice(&network_id_bytes);
let metadata: [H256; 3] = [H256::from(h1), H256::from(h2), H256::from(h3)];
let metadata: [H256; 4] = [
H256::from(h1),
H256::from(h2),
H256::from(h3),
H256::from(h4),
];
sequential_hash::<SubstrateBlake2Hasher, _>(metadata)
}
fn compute_arguments_hash<NetworkId>(&self, network_id: NetworkId) -> H256
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
let amount_u128: u128 = self.amount.unique_saturated_into();
let slot_index_u64: u64 = self.slot_index.unique_saturated_into();
let network_id_u64: u64 = network_id.unique_saturated_into();
let amount_u256 = U256::from(amount_u128);
let slot_index_u256 = U256::from(slot_index_u64);
let network_id_u256 = U256::from(network_id_u64);
let mut padded_amount = [0u8; 32];
amount_u256.to_big_endian(&mut padded_amount);
let mut padded_slot_index = [0u8; 32];
slot_index_u256.to_big_endian(&mut padded_slot_index);
let mut padded_network_id = [0u8; 32];
network_id_u256.to_big_endian(&mut padded_network_id);
let inputs: [&[u8]; 4] = [
padded_amount.as_ref(),
self.receiver.as_ref(),
padded_slot_index.as_ref(),
padded_network_id.as_ref(),
];
sequential_hash::<SubstrateKeccakHasher, _>(inputs)
}
pub fn new(
slot_index: ThreadId,
payload: ThreadPayload<Balance>,
proof_index: ThreadId,
receiver: AccountId32,
amount: Balance,
proof_vec: Vec<H256>,
slots_vec: Vec<H256>,
) -> Self {
@ -432,41 +457,17 @@ where
Self {
slot_index,
payload,
proof_index,
receiver,
amount,
proof,
slots,
}
}
fn compute_arguments_hash(&self) -> H256 {
let amount_u128: u128 = self.amount().unique_saturated_into();
let amount_u256 = U256::from(amount_u128);
let slot_index_u64: u64 = self.slot_index.unique_saturated_into();
let slot_index_u256 = U256::from(slot_index_u64);
let mut padded_amount = [0u8; 32];
amount_u256.to_big_endian(&mut padded_amount);
let mut padded_slot_index = [0u8; 32];
slot_index_u256.to_big_endian(&mut padded_slot_index);
let inputs: [&[u8]; 3] = [
padded_amount.as_ref(),
self.payload.receiver.as_ref(),
padded_slot_index.as_ref(),
];
sequential_hash::<SubstrateKeccakHasher, _>(inputs)
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct UtxoThreadProof<Balance: UniqueSaturatedInto<u128>> {
o_index: ThreadId,
w_index: ThreadId,
expected_amount: Balance,
payload: ThreadPayload<Balance>,
pub struct UtxoThreadProof {
gettxproof_bytes: BoundedVec<u8, ConstU32<UTXO_MAX_PROOF_BYTES>>,
getrawtransaction_bytes: BoundedVec<u8, ConstU32<UTXO_MAX_TX_BYTES>>,
ancestry_headers: BoundedVec<
@ -475,24 +476,20 @@ pub struct UtxoThreadProof<Balance: UniqueSaturatedInto<u128>> {
>,
}
impl<Balance> UtxoThreadProof<Balance>
where
Balance: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u128>,
{
impl UtxoThreadProof {
pub fn ancestry_headers_len(&self) -> u32 {
self.ancestry_headers.len() as u32
}
pub fn amount(&self) -> Balance {
self.expected_amount
}
pub fn receiver(&self) -> AccountId32 {
self.payload.receiver.clone()
}
pub fn proof_index(&self) -> ThreadId {
self.payload.proof_index
}
pub fn verify_proof(&self, root_hash: H256, gatekeeper_ref: &[u8]) -> Option<AccountId32> {
pub fn verify_thread_proof<Balance>(
&self,
root_hash: H256,
gatekeeper_ref: &[u8],
) -> Option<(AccountId32, Balance)>
where
Balance: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u128>,
u64: UniqueSaturatedInto<Balance>,
{
use ghost_helpers::utxo::{Header, Transaction};
if self.gettxproof_bytes.len() < 85 { return None; }
@ -506,48 +503,47 @@ where
if !is_chain_valid { return None; }
let transaction =
Transaction::<UtxoSha2Hasher>::try_from(self.getrawtransaction_bytes.as_ref()).ok()?;
let header = Header::<UtxoSha2Hasher>::try_from(self.gettxproof_bytes.as_ref()).ok()?;
let transaction = Transaction::<UtxoSha2Hasher>::try_from(
self.getrawtransaction_bytes.as_ref()
).ok()?;
let header = Header::<UtxoSha2Hasher>::try_from(
self.gettxproof_bytes.as_ref(),
).ok()?;
let txid_preimage = transaction.compute_txid()?;
let root_hash = header.extract_proof(txid_preimage).ok()?;
if root_hash != header.merkle_root { return None; }
if root_hash != header.merkle_root {
return None;
#[cfg(not(any(test, feature = "runtime-benchmarks")))]
let destination_script = Transaction::<UtxoSha2Hasher>::convert_into_p2tr(gatekeeper_ref)?;
#[cfg(any(test, feature = "runtime-benchmarks"))]
let destination_script = Transaction::<UtxoSha2Hasher>::convert_into_p2pkh(gatekeeper_ref)?;
transaction.parse_receiver_and_amount(&destination_script, &PROTOCOL_PREFIX)
.map(|(receiver, amount)| (
AccountId32::new(receiver),
amount.unique_saturated_into(),
))
}
let account_id_bytes = transaction.validate(
self.o_index,
self.w_index,
gatekeeper_ref,
self.expected_amount,
)?;
pub fn get_unique_key<NetworkId>(&self, network_id: NetworkId) -> H256
where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{
let network_id_u64: u64 = network_id.unique_saturated_into();
let network_id_bytes = network_id_u64.to_be_bytes();
Some(AccountId32::new(account_id_bytes))
}
pub fn get_unique_key(&self) -> H256 {
let o_index_bytes = (self.o_index as u64).to_be_bytes();
let mut o_index_raw = [0u8; 32];
o_index_raw[24..32].copy_from_slice(&o_index_bytes);
let o_index_hash = H256::from(o_index_raw);
let metadata: &[&[u8]] = &[
self.gettxproof_bytes.as_ref(),
self.getrawtransaction_bytes.as_ref(),
o_index_hash.as_ref(),
network_id_bytes.as_ref(),
];
sequential_hash::<SubstrateBlake2Hasher, _>(metadata)
}
pub fn new(
o_index: ThreadId,
w_index: ThreadId,
expected_amount: Balance,
payload: ThreadPayload<Balance>,
gettxproof_vec: Vec<u8>,
getrawtransaction_vec: Vec<u8>,
ancestry_headers_vec: Vec<Vec<u8>>,
@ -571,17 +567,12 @@ where
let ancestry_headers = BoundedVec::<
BoundedVec<u8, ConstU32<UTXO_MAX_HEADER_BYTES>>,
ConstU32<UTXO_MAX_HISTORY_DEPTH>,
>::try_from(inner_bounded_headers)
.expect("Should be valud UTXO headers");
>::try_from(inner_bounded_headers).expect("Should be valud UTXO headers");
Self {
o_index,
w_index,
expected_amount,
gettxproof_bytes,
getrawtransaction_bytes,
ancestry_headers,
payload,
}
}
}

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_weaver`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -69,11 +69,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)`
// Estimated: `3687 + a * (32 ±0)`
// Minimum execution time: 209_842_000 picoseconds.
Weight::from_parts(214_884_530, 0)
// Minimum execution time: 211_189_000 picoseconds.
Weight::from_parts(215_074_977, 0)
.saturating_add(Weight::from_parts(0, 3687))
// Standard Error: 1_520
.saturating_add(Weight::from_parts(45_535, 0).saturating_mul(a.into()))
// Standard Error: 1_505
.saturating_add(Weight::from_parts(46_499, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -95,11 +95,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)`
// Estimated: `3982 + a * (32 ±0)`
// Minimum execution time: 241_362_000 picoseconds.
Weight::from_parts(244_695_658, 0)
// Minimum execution time: 241_010_000 picoseconds.
Weight::from_parts(245_378_305, 0)
.saturating_add(Weight::from_parts(0, 3982))
// Standard Error: 1_644
.saturating_add(Weight::from_parts(30_947, 0).saturating_mul(a.into()))
// Standard Error: 1_583
.saturating_add(Weight::from_parts(32_330, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -119,12 +119,12 @@ impl WeightInfo for () {
/// The range of component `a` is `[1, 144]`.
fn pull_utxo_thread(a: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `358`
// Estimated: `3823`
// Minimum execution time: 174_434_000 picoseconds.
Weight::from_parts(173_218_259, 0)
// Standard Error: 4_108
.saturating_add(Weight::from_parts(3_390_809, 0).saturating_mul(a.into()))
// Measured: `346`
// Estimated: `3811`
// Minimum execution time: 170_701_000 picoseconds.
Weight::from_parts(170_150_850, 0)
// Standard Error: 4_368
.saturating_add(Weight::from_parts(3_363_930, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3))
}
@ -142,11 +142,11 @@ impl WeightInfo for () {
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn pull_evm_thread() -> Weight {
// Proof Size summary in bytes:
// Measured: `358`
// Estimated: `3823`
// Minimum execution time: 131_682_000 picoseconds.
Weight::from_parts(132_731_000, 0)
.saturating_add(Weight::from_parts(0, 3823))
// Measured: `3446`
// Estimated: `3811`
// Minimum execution time: 138_215_000 picoseconds.
Weight::from_parts(140_004_000, 0)
.saturating_add(Weight::from_parts(0, 3811))
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3))
}

View File

@ -1,6 +1,6 @@
[package]
name = "casper-runtime"
version = "4.0.6"
version = "4.0.11"
build = "build.rs"
description = "Runtime of the Casper Network"
edition.workspace = true

View File

@ -11,7 +11,7 @@ use grandpa_primitives::AuthorityId as GrandpaId;
use hex_literal::hex;
use sp_core::crypto::UncheckedFrom;
use sp_core::{sr25519, Pair, Public};
use sp_core::{sr25519, H256, Pair, Public};
use sp_runtime::{traits::IdentifyAccount, Perbill};
#[cfg(not(feature = "std"))]
use sp_std::alloc::format;
@ -22,7 +22,7 @@ use crate::{Balance, NetworkId};
use ghost_weaver::sr25519::AuthorityId as WeaverId;
use ghost_exodus::sr25519::AuthorityId as ExodusId;
use ghost_helpers::networks::{NetworkClaim, NetworkInitiation};
use ghost_helpers::networks::{NetworkClaim, NetworkClaimBuilder, NetworkInitiation};
#[derive(Clone)]
pub struct AuthorityInitiation {
@ -188,6 +188,28 @@ fn casper_staging_initial_authorities() -> Vec<AuthorityInitiation> {
.with_exodus_id(ExodusId::unchecked_from(hex!("7277c176a3eb110ea2e259d09994c1bce303ca028b8daccc5b10d8d8f1baea40")))
.build(),
// sfHcJxw5cgkvukZZyxcNUMCdbm9e7773orByLrGgAREka81TK
AuthorityInitiationBuilder::default()
.with_stash(AccountId::new(hex!("8a0d0b66e827bf20e79f9a499317e73925ce4f422371067edfab690e43857f13")))
.with_account(AccountId::new(hex!("ac871e8bab00dd56ba3a1c0bd289357203dcaf10010b0b04ad7472870cd22a3c")))
.with_babe_id(BabeId::unchecked_from(hex!("e0f0a776ecc9fa5e1f22e2fa001fe3fba5aea52b9444bc894b45589d42132475")))
.with_grandpa_id(GrandpaId::unchecked_from(hex!("b63c5a0cf342b9b04931bc8ed74d7d0165ab99ab5f8a4514797d4b299a4501fe")))
.with_authority_id(AuthorityDiscoveryId::unchecked_from(hex!("2cf69452e9f2a8457119139408884941ed50f590c0fc0f2b044c4d82c69e4245")))
.with_weaver_id(WeaverId::unchecked_from(hex!("22bc96d10ca7c04ba72a649db6b4c8f499744bb718d67b6fb7694f4bc2b46010")))
.with_exodus_id(ExodusId::unchecked_from(hex!("c20f8dbc1f86ae1520e6f9dc65239ec7e1e1e80eea7b5c1de2035a684e25ea48")))
.build(),
// sfFD7KSRi2aSREJWc9X75sVTN4a5pbPM5VSSJefmPvMuiVbPr
AuthorityInitiationBuilder::default()
.with_stash(AccountId::new(hex!("6c0283f4c688f0e75ad546c790bbd5961c1a6931543aa589f368f8272c44b758")))
.with_account(AccountId::new(hex!("425ccd7bda4f5c76788ba23bc0381d7a2e496179c93301208c57501c80a4232a")))
.with_babe_id(BabeId::unchecked_from(hex!("74fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e")))
.with_grandpa_id(GrandpaId::unchecked_from(hex!("236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf547218")))
.with_authority_id(AuthorityDiscoveryId::unchecked_from(hex!("00946618c353e4c6546b87f9ca1089b846b0ea4658ee8e6d9d1200c24cb5ee27")))
.with_weaver_id(WeaverId::unchecked_from(hex!("10ab42a4a2e41d7921ded8138f406b15a61cf98147c43f9cdd5dd0b4e854ca0f")))
.with_exodus_id(ExodusId::unchecked_from(hex!("76aba3841978e8a4c5f22172c0cd0c34bedee4ebfcc8d93e1e44201ee013081b")))
.build(),
// sfH29zyFYtoFj6fSXskAEXG5XyyMCa5YycahJkYMwSUz8CcEU
AuthorityInitiationBuilder::default()
.with_stash(AccountId::new(hex!("b24feb55b2cac4b365a9245c2a97525b01bd1a594d2d42b91f6bc38c9c2e6517")))
@ -198,6 +220,17 @@ fn casper_staging_initial_authorities() -> Vec<AuthorityInitiation> {
.with_weaver_id(WeaverId::unchecked_from(hex!("78efbd73e499bea76bf9bab1cb136fbe51aee389b1a8d151496e30bd55888429")))
.with_exodus_id(ExodusId::unchecked_from(hex!("b469187ddf8f7458d67262328e236a252bd2db9e2f87e8313e7e3b322aecdc3e")))
.build(),
// sfEwRjyvEQcpRQ1qbCZum27nEkTggKEt7DtqxwyYQULt9UuUN
AuthorityInitiationBuilder::default()
.with_stash(AccountId::new(hex!("a0ba0196e6ee7e6b5b0553035c5cb5c04e9725001b5732839d0529cbc00c9600")))
.with_account(AccountId::new(hex!("3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203")))
.with_babe_id(BabeId::unchecked_from(hex!("6c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e49")))
.with_grandpa_id(GrandpaId::unchecked_from(hex!("8f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d")))
.with_authority_id(AuthorityDiscoveryId::unchecked_from(hex!("90db5ed339a559ed157995a48d781f44c7df972dfba4bc855e4b59fa46438e17")))
.with_weaver_id(WeaverId::unchecked_from(hex!("5e7fdff77b38a07fad6dbc1cbfed9bb08b0c670459e6d4962f37c46a145f2214")))
.with_exodus_id(ExodusId::unchecked_from(hex!("bcb1948a376a30a92bc6ecce17d95185775b51a655dc1d4d393ed5c26b8b201e")))
.build(),
]
}
@ -211,8 +244,8 @@ pub fn testnet_config_genesis(
initial_networks: Vec<NetworkInitiation<NetworkId, Balance>>,
initial_claims: Vec<NetworkClaim<NetworkId, Balance>>,
) -> serde_json::Value {
const ENDOWMENT: u128 = 31 * CSPR;
const STASH_BOND: u128 = 68 * CSPR;
const ENDOWMENT: u128 = 34 * CSPR;
const STASH_BOND: u128 = 34 * CSPR;
const STASH_FEES: u128 = 1 * CSPR;
let sudo = initial_authorities.first().map(|x| x.account.clone()).unwrap();
@ -262,7 +295,7 @@ pub fn testnet_config_genesis(
"babe": { "epochConfig": Some(crate::BABE_GENESIS_EPOCH_CONFIG) },
"ghostNominationPools": {
"minJoinBond": 10 * STNK,
"minCreateBond": 50 * CSPR,
"minCreateBond": 42 * CSPR,
"maxPools": 256u32,
"maxMembersPerPool": 1024u32,
"maxMembers": 20000u32,
@ -314,11 +347,27 @@ pub fn casper_local_config_genesis() -> serde_json::Value {
}
pub fn casper_staging_config_genesis() -> serde_json::Value {
let initial_preclaims = vec![
NetworkClaimBuilder::default()
.with_amount(1922500000000000000000u128.into())
.with_network_id(11155111)
.with_merkle_root(H256::from_slice(&hex!("f8d8c981f214958cc2738bf068807c0f504f0e9a1d61ba5c8cd01bc7f9cc6d26")))
.with_proof_size(8)
.build(),
NetworkClaimBuilder::default()
.with_amount(18550200000000000000000u128.into())
.with_network_id(7001)
.with_merkle_root(H256::from_slice(&hex!("793b300540c6530c24be7cc40b1ba91f0a586da8e180b98e91b055a9a12f45e2")))
.with_proof_size(11)
.build(),
];
testnet_config_genesis(
casper_staging_initial_authorities(),
Default::default(),
casper_testnet_networks(),
Default::default(),
initial_preclaims,
)
}

View File

@ -123,8 +123,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("casper"),
impl_name: create_runtime_str!("casper-svengali"),
authoring_version: 0,
spec_version: 6,
impl_version: 4,
spec_version: 7,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 1,

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_exodus`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -60,8 +60,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `351`
// Estimated: `3816`
// Minimum execution time: 239_348_000 picoseconds.
Weight::from_parts(241_125_000, 0)
// Minimum execution time: 239_012_000 picoseconds.
Weight::from_parts(241_388_000, 0)
.saturating_add(Weight::from_parts(0, 3816))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
@ -83,11 +83,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)`
// Minimum execution time: 1_372_279_000 picoseconds.
Weight::from_parts(1_268_130_469, 0)
// Minimum execution time: 1_339_828_000 picoseconds.
Weight::from_parts(1_212_625_790, 0)
.saturating_add(Weight::from_parts(0, 3856))
// Standard Error: 17_178
.saturating_add(Weight::from_parts(32_976_202, 0).saturating_mul(c.into()))
// Standard Error: 11_831
.saturating_add(Weight::from_parts(33_120_868, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
@ -107,11 +107,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 253_301_000 picoseconds.
Weight::from_parts(255_449_282, 0)
// Minimum execution time: 251_478_000 picoseconds.
Weight::from_parts(254_144_621, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 25_029
.saturating_add(Weight::from_parts(6_615_251, 0).saturating_mul(c.into()))
// Standard Error: 24_613
.saturating_add(Weight::from_parts(6_595_819, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -131,11 +131,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 238_882_000 picoseconds.
Weight::from_parts(242_930_556, 0)
// Minimum execution time: 236_155_000 picoseconds.
Weight::from_parts(242_055_898, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 13_070
.saturating_add(Weight::from_parts(426_492, 0).saturating_mul(c.into()))
// Standard Error: 15_623
.saturating_add(Weight::from_parts(360_411, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -155,11 +155,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 250_894_000 picoseconds.
Weight::from_parts(252_272_982, 0)
// Minimum execution time: 249_507_000 picoseconds.
Weight::from_parts(248_505_595, 0)
.saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 17_703
.saturating_add(Weight::from_parts(4_110_522, 0).saturating_mul(c.into()))
// Standard Error: 20_302
.saturating_add(Weight::from_parts(4_191_526, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -180,8 +180,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `448`
// Estimated: `3913`
// Minimum execution time: 265_392_000 picoseconds.
Weight::from_parts(268_799_000, 0)
// Minimum execution time: 264_214_000 picoseconds.
Weight::from_parts(266_905_000, 0)
.saturating_add(Weight::from_parts(0, 3913))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
@ -205,11 +205,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `0 + c * (770 ±0)`
// Estimated: `1551 + c * (1019 ±27)`
// Minimum execution time: 282_654_000 picoseconds.
Weight::from_parts(286_257_905, 0)
// Minimum execution time: 281_073_000 picoseconds.
Weight::from_parts(280_778_987, 0)
.saturating_add(Weight::from_parts(0, 1551))
// Standard Error: 92_951
.saturating_add(Weight::from_parts(2_838_516, 0).saturating_mul(c.into()))
// Standard Error: 79_426
.saturating_add(Weight::from_parts(3_449_552, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
@ -236,8 +236,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `588`
// Estimated: `4053`
// Minimum execution time: 296_169_000 picoseconds.
Weight::from_parts(298_267_000, 0)
// Minimum execution time: 291_972_000 picoseconds.
Weight::from_parts(295_122_000, 0)
.saturating_add(Weight::from_parts(0, 4053))
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(5))
@ -266,8 +266,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `968`
// Estimated: `4433`
// Minimum execution time: 332_972_000 picoseconds.
Weight::from_parts(336_569_000, 0)
// Minimum execution time: 331_528_000 picoseconds.
Weight::from_parts(333_974_000, 0)
.saturating_add(Weight::from_parts(0, 4433))
.saturating_add(T::DbWeight::get().reads(10))
.saturating_add(T::DbWeight::get().writes(4))
@ -306,8 +306,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `11259`
// Estimated: `14724`
// Minimum execution time: 3_842_259_000 picoseconds.
Weight::from_parts(3_853_670_000, 0)
// Minimum execution time: 3_980_585_000 picoseconds.
Weight::from_parts(3_985_167_000, 0)
.saturating_add(Weight::from_parts(0, 14724))
.saturating_add(T::DbWeight::get().reads(14))
.saturating_add(T::DbWeight::get().writes(8))
@ -326,11 +326,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_evm_bridge_out_exodus() -> Weight {
// Proof Size summary in bytes:
// Measured: `447`
// Estimated: `3912`
// Minimum execution time: 138_303_000 picoseconds.
Weight::from_parts(139_968_000, 0)
.saturating_add(Weight::from_parts(0, 3912))
// Measured: `448`
// Estimated: `3913`
// Minimum execution time: 137_053_000 picoseconds.
Weight::from_parts(138_693_000, 0)
.saturating_add(Weight::from_parts(0, 3913))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(4))
}

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_governance`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -59,11 +59,11 @@ impl<T: frame_system::Config> ghost_governance::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `283`
// Estimated: `3748`
// Minimum execution time: 216_605_000 picoseconds.
Weight::from_parts(217_612_486, 0)
// Minimum execution time: 214_208_000 picoseconds.
Weight::from_parts(216_987_870, 0)
.saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 23_409
.saturating_add(Weight::from_parts(1_544_250, 0).saturating_mul(p.into()))
// Standard Error: 30_856
.saturating_add(Weight::from_parts(1_702_104, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(4))
}

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_networks`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -61,15 +61,15 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3471`
// Minimum execution time: 60_167_000 picoseconds.
Weight::from_parts(56_015_384, 0)
// Minimum execution time: 57_039_000 picoseconds.
Weight::from_parts(53_368_140, 0)
.saturating_add(Weight::from_parts(0, 3471))
// Standard Error: 1_219
.saturating_add(Weight::from_parts(6_146, 0).saturating_mul(i.into()))
// Standard Error: 617
.saturating_add(Weight::from_parts(37_564, 0).saturating_mul(j.into()))
// Standard Error: 8_216
.saturating_add(Weight::from_parts(864_641, 0).saturating_mul(k.into()))
// Standard Error: 874
.saturating_add(Weight::from_parts(8_884, 0).saturating_mul(i.into()))
// Standard Error: 442
.saturating_add(Weight::from_parts(32_778, 0).saturating_mul(j.into()))
// Standard Error: 5_890
.saturating_add(Weight::from_parts(833_630, 0).saturating_mul(k.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(4))
}
@ -77,11 +77,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_selector() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_957_000 picoseconds.
Weight::from_parts(44_541_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_680_000 picoseconds.
Weight::from_parts(43_454_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -90,13 +90,13 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// The range of component `n` is `[1, 128]`.
fn update_network_endpoint(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 44_092_000 picoseconds.
Weight::from_parts(45_181_439, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Standard Error: 402
.saturating_add(Weight::from_parts(16_387, 0).saturating_mul(n.into()))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_265_000 picoseconds.
Weight::from_parts(43_357_887, 0)
.saturating_add(Weight::from_parts(0, 3632))
// Standard Error: 457
.saturating_add(Weight::from_parts(13_745, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -104,11 +104,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_finality_delay() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_603_000 picoseconds.
Weight::from_parts(46_116_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 43_008_000 picoseconds.
Weight::from_parts(43_917_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -116,11 +116,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_rate_limit_delay() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_607_000 picoseconds.
Weight::from_parts(46_615_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 43_104_000 picoseconds.
Weight::from_parts(43_981_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -128,11 +128,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_block_deviation() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_510_000 picoseconds.
Weight::from_parts(43_840_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 41_734_000 picoseconds.
Weight::from_parts(42_645_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -140,11 +140,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_type() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 43_737_000 picoseconds.
Weight::from_parts(44_347_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_080_000 picoseconds.
Weight::from_parts(42_404_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -156,23 +156,35 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::NetworkCurvesVec` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn update_network_curve() -> Weight {
// Proof Size summary in bytes:
// Measured: `179`
// Estimated: `6119`
// Minimum execution time: 85_784_000 picoseconds.
Weight::from_parts(86_768_000, 0)
.saturating_add(Weight::from_parts(0, 6119))
// Measured: `180`
// Estimated: `6120`
// Minimum execution time: 82_975_000 picoseconds.
Weight::from_parts(84_583_000, 0)
.saturating_add(Weight::from_parts(0, 6120))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: `GhostNetworks::Networks` (r:1 w:1)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_decimals() -> Weight {
// Proof Size summary in bytes:
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 41_602_000 picoseconds.
Weight::from_parts(42_603_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: `GhostNetworks::Networks` (r:1 w:1)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_network_gatekeeper() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 44_801_000 picoseconds.
Weight::from_parts(45_240_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_643_000 picoseconds.
Weight::from_parts(43_190_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -180,11 +192,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_incoming_network_share() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_404_000 picoseconds.
Weight::from_parts(46_161_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 42_978_000 picoseconds.
Weight::from_parts(44_002_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -192,11 +204,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn update_outgoing_network_share() -> Weight {
// Proof Size summary in bytes:
// Measured: `166`
// Estimated: `3631`
// Minimum execution time: 45_476_000 picoseconds.
Weight::from_parts(46_312_000, 0)
.saturating_add(Weight::from_parts(0, 3631))
// Measured: `167`
// Estimated: `3632`
// Minimum execution time: 43_436_000 picoseconds.
Weight::from_parts(44_193_000, 0)
.saturating_add(Weight::from_parts(0, 3632))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
@ -210,11 +222,11 @@ impl<T: frame_system::Config> ghost_networks::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::NetworkCurvesVec` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn remove_network() -> Weight {
// Proof Size summary in bytes:
// Measured: `179`
// Estimated: `3644`
// Minimum execution time: 78_480_000 picoseconds.
Weight::from_parts(79_105_000, 0)
.saturating_add(Weight::from_parts(0, 3644))
// Measured: `180`
// Estimated: `3645`
// Minimum execution time: 75_969_000 picoseconds.
Weight::from_parts(77_610_000, 0)
.saturating_add(Weight::from_parts(0, 3645))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(4))
}

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_weaver`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2026-09-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -61,11 +61,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)`
// Estimated: `3687 + a * (32 ±0)`
// Minimum execution time: 209_842_000 picoseconds.
Weight::from_parts(214_884_530, 0)
// Minimum execution time: 211_189_000 picoseconds.
Weight::from_parts(215_074_977, 0)
.saturating_add(Weight::from_parts(0, 3687))
// Standard Error: 1_520
.saturating_add(Weight::from_parts(45_535, 0).saturating_mul(a.into()))
// Standard Error: 1_505
.saturating_add(Weight::from_parts(46_499, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -87,11 +87,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)`
// Estimated: `3982 + a * (32 ±0)`
// Minimum execution time: 241_362_000 picoseconds.
Weight::from_parts(244_695_658, 0)
// Minimum execution time: 241_010_000 picoseconds.
Weight::from_parts(245_378_305, 0)
.saturating_add(Weight::from_parts(0, 3982))
// Standard Error: 1_644
.saturating_add(Weight::from_parts(30_947, 0).saturating_mul(a.into()))
// Standard Error: 1_583
.saturating_add(Weight::from_parts(32_330, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -111,13 +111,13 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// The range of component `a` is `[1, 144]`.
fn pull_utxo_thread(a: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `358`
// Estimated: `3823`
// Minimum execution time: 174_434_000 picoseconds.
Weight::from_parts(173_218_259, 0)
.saturating_add(Weight::from_parts(0, 3823))
// Standard Error: 4_108
.saturating_add(Weight::from_parts(3_390_809, 0).saturating_mul(a.into()))
// Measured: `346`
// Estimated: `3811`
// Minimum execution time: 170_701_000 picoseconds.
Weight::from_parts(170_150_850, 0)
.saturating_add(Weight::from_parts(0, 3811))
// Standard Error: 4_368
.saturating_add(Weight::from_parts(3_363_930, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
}
@ -135,11 +135,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn pull_evm_thread() -> Weight {
// Proof Size summary in bytes:
// Measured: `358`
// Estimated: `3823`
// Minimum execution time: 131_682_000 picoseconds.
Weight::from_parts(132_731_000, 0)
.saturating_add(Weight::from_parts(0, 3823))
// Measured: `346`
// Estimated: `3811`
// Minimum execution time: 138_215_000 picoseconds.
Weight::from_parts(140_004_000, 0)
.saturating_add(Weight::from_parts(0, 3811))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
}

View File

@ -1,6 +1,6 @@
[package]
name = "runtime-common"
version = "0.4.8"
version = "0.4.9"
authors.workspace = true
edition.workspace = true
homepage.workspace = true

View File

@ -30,14 +30,14 @@ pub use sp_runtime::BuildStorage;
pub use impls::ToAuthor;
/// We assume that an on-initialize consumes 10% of the weight on average, hence
/// We assume that an on-initialize consumes 15% of the weight on average, hence
/// a single extrinsic will not be allowed to consume more than
/// `AvailableBlockRatio - 10%`.
pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
/// `AvailableBlockRatio - 15%`.
pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(15);
/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can
/// We allow `Normal` extrinsics to fill up the block up to 70%, the rest can
/// be used by `Operational` extrinsics.
pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(70);
/// We allow for 4 seconds of compute with a 12 seconds average block time.
/// The storage proof size is not limited so far.

4
rust-toolchain.toml Normal file
View File

@ -0,0 +1,4 @@
[toolchain]
channel = "1.93.1"
components = ["rustfmt", "clippy", "rust-src"]
targets = ["wasm32-unknown-unknown"]

View File

@ -17,15 +17,21 @@ MORDOR_RPC=(
"https://rpc.mordor.etccooperative.org"
)
HOODI_RPC=(
"https://rpc.hoodi.ethpandaops.io"
"https://0xrpc.io/hoodi"
"https://ethereum-hoodi.gateway.tatum.io"
"https://rpc.sentio.xyz/hoodi"
)
show_help() {
cat << EOF
Usage: $(basename "$0") [OPTIONS]
Options:
--chain <name> Specify network: 'sepolia' or 'mordor'. If omitted, checks both.
--chain <name> Specify network: 'sepolia', 'hoodi' or 'mordor'. If omitted, checks all.
--output <dir> Base directory to save results. Creates a subfolder with Unix Timestamp.
--rpcs <urls> Comma-separated list of custom RPC endpoints.
Disables default Sepolia/Mordor lists.
--rpcs <urls> Comma-separated list of custom RPC endpoints. Disables default networks lists.
--help Show this help message.
Example:
@ -129,8 +135,11 @@ else
check_network "Sepolia" "${SEPOLIA_RPC[@]}"
elif [ "$CHOSEN_CHAIN" == "mordor" ]; then
check_network "Mordor" "${MORDOR_RPC[@]}"
elif [ "$CHOSEN_CHAIN" == "hoodi" ]; then
check_network "Hoodi" "${HOODI_RPC[@]}"
else
check_network "Sepolia" "${SEPOLIA_RPC[@]}"
check_network "Mordor" "${MORDOR_RPC[@]}"
check_network "Hoodi" "${HOODI_RPC[@]}"
fi
fi

View File

@ -85,30 +85,16 @@ extract_seed() {
echo $seed
}
upgrade_compiler_if_needed() {
UPDATE_STATUS=$(rustup check 2>&1)
apply_patches_if_needed() {
local PATCH_STATUS=0
"$SCRIPT_FOLDER"/patch.sh || PATCH_STATUS=$?
if echo "$UPDATE_STATUS" | grep -q "Update available" || [ $PATCH_STATUS -eq 2 ]; then
if [ "$PATCH_STATUS" -eq 2 ]; then
echo "[+] clean all cache on the current system"
cargo install cargo-cache
cargo clean
cargo install cargo-cache
cargo cache --remove-dir all
echo "[+] upgrading rustc compiler version to latest"
rustup update
toolchain_name=$(rustup show | grep default | head -n 1 | cut -d' ' -f1)
if ! rustup target list --installed | grep -q "wasm32-unknown-unknown"; then
echo "[+] installing wasm32-unknown-unknown target"
rustup target add wasm32-unknown-unknown
fi
if ! rustup component list --installed | grep -q "rust-src"; then
echo "[+] installing rust-src component"
rustup component add rust-src
fi
echo "[+] fetching all dependencies"
cargo fetch
@ -240,7 +226,7 @@ if [[ $HARD_RESET = true ]]; then
exit 1
fi
upgrade_compiler_if_needed
apply_patches_if_needed
echo "[+] trying to stop current ghost-node"
sudo systemctl stop ghost-node
@ -349,12 +335,12 @@ if [[ $SET_ENVIRONMENT = true ]]; then
if prompt "[?] setup the rust environment (e.g. WASM support)?"; then
rustup default stable
upgrade_compiler_if_needed
apply_patches_if_needed
fi
fi
if [[ ! -z $RELEASE ]]; then
upgrade_compiler_if_needed
apply_patches_if_needed
if prompt "[?] 'cargo build $RELEASE $FEATURES' is what you want?"; then
cd $PROJECT_FOLDER
echo "[+] starting build in 3 seconds..."
@ -599,7 +585,7 @@ if [ "$GENERATE_GENESIS" = true ]; then
echo " - $WALLET_KEY_PATH"
echo " - $STASH_KEY_PATH"
echo ""
echo "IMPORTANT: Create a pool request to include these keys in the upcoming genesis."
echo "IMPORTANT: Create a pull request to include these keys in the upcoming genesis."
echo "DO NOT DELETE THESE KEYS OR THE GENESIS WILL BE INVALID!"
echo "Submit a PR with these keys to: https://git.ghostchain.io/ghostchain/ghost-node"
echo ""

File diff suppressed because one or more lines are too long