From a6e7ac0ef26438b3d3c6794449785df4f45f9651 Mon Sep 17 00:00:00 2001 From: Sergei Boiko <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 3 Jan 2025 23:15:52 +0700 Subject: [PATCH] [BTC]: Add support for Babylon Staking transactions (#4165) * Kotlin multiplatform leaking memory (#4037) * Add deinit for KMP iOS and JVM targets * Add deinit for JS target * Add deinit for JS target * Fix JVM native name * Reuse one thread on JVM --------- Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> * [KMP] Fix issue: memory leak found in Base58.decode in iOS (#4031) * Fix kmp issue: memory leak found in Base58.decode in iOS * Remove unused functions * Fix failed test cases * Revert "Fix failed test cases" This reverts commit 57eee395df508d95a5e570b1f54143b7de20eb31. * Revert val -> value argument name refactoring * Output better indentation * Revert changes in TWEthereumAbiFunction.h * Fix inconsistent naming --------- Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> * [TON]: Add support for TON 24-words mnemonic (#3998) * feat(ton): Add support for TON 24-words mnemonic in Rust * feat(ton): Add tw_ton_wallet FFIs * feat(ton): Add TWTONWallet FFI in C++ * feat(ton): Add tonMnemonic StoredKey type * feat(ton): Add StoredKey TON tests * feat(ton): Add TWStoredKey TON tests * feat(ton): Add TONWallet support in Swift * TODO add iOS tests * feat(ton): Add `KeyStore` iOS tests * feat(ton): Add TONWallet support in JavaScript * Add `KeyStore` TypeScript tests * feat(ton): Remove `TonMnemonic` structure, replace with a `validate_mnemonic_words` function * [CI] Trigger CI * feat(ton): Fix rustfmt * feat(ton): Fix C++ build * feat(ton): Fix C++ build * feat(ton): Fix C++ build * feat(ton): Fix C++ address analyzer * feat(ton): Fix C++ tests * feat(ton): Add Android tests * feat(ton): Bump `actions/upload-artifact` to v4 * Bump `dawidd6/action-download-artifact` to v6 * feat(eth): Fix PR comments * Fix Java JVM leak (#4092) * [Chore]: Fix Android bindings (#4095) * [Chore]: Add GenericPhantomReference.java * [Chore]: Fix unnecessary null assertion in WalletCoreLibLoader.kt * Fix memory lead found in public key in kmp binding (#4097) * Update Callisto explorer (#4131) * [Babylon]: Add Babylon Staking Inputs and Outputs to BitcoinV2.proto * [Babylon]: Add `covenant_committee_signatures` * Revert "[TON]: Add support for TON 24-words mnemonic (#3998)" (#4148) This reverts commit 0b167710b325d299398ca82ef57ee8e630e54633. * [Babylon]: Add timelock, unbonding, slashing condition scripts * Test taproot merkle root generator * [BTC]: Add Babylon Staking output * [BTC]: Add Babylon Staking OP_RETURN * [BTC]: Minor changes * [BTC]: Add Babylon Staking UTXO * [BTC]: Add Babylon Unbonding UTXO * [BTC]: Refactor by adding `BabylonStaking.proto` * [BTC]: Add Babylon Staking transaction test * [BTC]: Add spending of Staking output via Unbonding path * [BTC]: Withdraw Unbonding transaction via timelock path * [BTC]: Withdraw Staking transaction via timelock path * [BTC]: Fix tests, minor improves * [BTC]: Fix TODOs * [CI] Trigger CI * [BTC]: Fix Rust sample --------- Co-authored-by: Viacheslav Kulish <32914239+vcoolish@users.noreply.github.com> Co-authored-by: 10gic --- rust/Cargo.lock | 5 + rust/chains/tw_bitcoin/Cargo.toml | 4 +- .../tw_bitcoin/src/babylon/conditions.rs | 153 ++++ rust/chains/tw_bitcoin/src/babylon/mod.rs | 10 + .../src/babylon/multi_sig_ordered.rs | 134 ++++ .../src/babylon/proto_builder/mod.rs | 78 ++ .../babylon/proto_builder/output_protobuf.rs | 72 ++ .../babylon/proto_builder/utxo_protobuf.rs | 91 +++ .../tw_bitcoin/src/babylon/spending_data.rs | 55 ++ .../tw_bitcoin/src/babylon/spending_info.rs | 167 ++++ .../tw_bitcoin/src/babylon/tx_builder/mod.rs | 19 + .../src/babylon/tx_builder/output.rs | 81 ++ .../tw_bitcoin/src/babylon/tx_builder/utxo.rs | 116 +++ rust/chains/tw_bitcoin/src/lib.rs | 1 + .../src/modules/tx_builder/output_protobuf.rs | 6 + .../src/modules/tx_builder/utxo_protobuf.rs | 22 +- .../tw_bitcoin/tests/babylon_staking.rs | 118 +++ .../data/babylon_staking_merkle_roots.json | 722 ++++++++++++++++++ .../data/babylon_staking_transactions.json | 213 ++++++ rust/chains/tw_bitcoincash/src/address.rs | 2 +- .../src/cash_address/unchecked.rs | 2 +- rust/chains/tw_pactus/src/encoder/error.rs | 2 +- .../src/modules/message_decompiler.rs | 2 +- .../src/address/user_friendly_address.rs | 2 +- .../tw_ton_sdk/src/boc/binary_writer.rs | 2 +- .../tw_ton_sdk/src/boc/boc_to_raw_boc.rs | 2 +- .../tw_ton_sdk/src/cell/cell_builder.rs | 2 +- .../tw_ton_sdk/src/cell/cell_parser.rs | 2 +- rust/frameworks/tw_ton_sdk/src/cell/mod.rs | 2 +- rust/frameworks/tw_utxo/src/context.rs | 2 +- .../tw_utxo/src/modules/sighash_computer.rs | 19 +- rust/frameworks/tw_utxo/src/script/mod.rs | 36 + .../src/script/standard_script/claims.rs | 17 +- .../src/script/standard_script/opcodes.rs | 15 +- rust/frameworks/tw_utxo/src/signature.rs | 1 + .../src/spending_data/standard_constructor.rs | 2 +- .../frameworks/tw_utxo/src/transaction/mod.rs | 8 +- .../standard_transaction/builder/output.rs | 4 + .../standard_transaction/builder/utxo.rs | 221 ++++-- .../transaction/standard_transaction/mod.rs | 2 +- rust/tw_keypair/src/schnorr/public.rs | 1 + rust/tw_proto/src/lib.rs | 2 + .../bitcoin/bitcoin_sign/babylon_staking.rs | 356 +++++++++ .../tests/chains/bitcoin/bitcoin_sign/mod.rs | 1 + .../tests/chains/common/bitcoin/babylon.rs | 102 +++ .../tests/chains/common/bitcoin/mod.rs | 1 + samples/rust/src/build.rs | 1 + src/proto/BabylonStaking.proto | 94 +++ src/proto/BitcoinV2.proto | 30 + 49 files changed, 2914 insertions(+), 88 deletions(-) create mode 100644 rust/chains/tw_bitcoin/src/babylon/conditions.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/mod.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/multi_sig_ordered.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/proto_builder/mod.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/proto_builder/output_protobuf.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/proto_builder/utxo_protobuf.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/spending_data.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/spending_info.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/tx_builder/mod.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/tx_builder/output.rs create mode 100644 rust/chains/tw_bitcoin/src/babylon/tx_builder/utxo.rs create mode 100644 rust/chains/tw_bitcoin/tests/babylon_staking.rs create mode 100644 rust/chains/tw_bitcoin/tests/data/babylon_staking_merkle_roots.json create mode 100644 rust/chains/tw_bitcoin/tests/data/babylon_staking_transactions.json create mode 100644 rust/tw_tests/tests/chains/bitcoin/bitcoin_sign/babylon_staking.rs create mode 100644 rust/tw_tests/tests/chains/common/bitcoin/babylon.rs create mode 100644 src/proto/BabylonStaking.proto diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 0c16a96d581..3b6ce25bf99 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -198,6 +198,7 @@ dependencies = [ "bitcoin_hashes", "hex_lit", "secp256k1", + "serde", ] [[package]] @@ -213,6 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" dependencies = [ "bitcoin-private", + "serde", ] [[package]] @@ -1389,6 +1391,7 @@ dependencies = [ "bitcoin_hashes", "rand", "secp256k1-sys", + "serde", ] [[package]] @@ -1785,6 +1788,8 @@ name = "tw_bitcoin" version = "0.1.0" dependencies = [ "bitcoin", + "itertools", + "lazy_static", "secp256k1", "serde", "serde_json", diff --git a/rust/chains/tw_bitcoin/Cargo.toml b/rust/chains/tw_bitcoin/Cargo.toml index 1d5571814b0..ce4101b5407 100644 --- a/rust/chains/tw_bitcoin/Cargo.toml +++ b/rust/chains/tw_bitcoin/Cargo.toml @@ -4,7 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -bitcoin = { version = "0.30.0", features = ["rand-std"] } +bitcoin = { version = "0.30.0", features = ["rand-std", "serde"] } +itertools = "0.10.5" +lazy_static = "1.4.0" secp256k1 = { version = "0.27.0", features = ["global-context", "rand-std"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/rust/chains/tw_bitcoin/src/babylon/conditions.rs b/rust/chains/tw_bitcoin/src/babylon/conditions.rs new file mode 100644 index 00000000000..e31b97ddb92 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/conditions.rs @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::multi_sig_ordered::MultiSigOrderedKeys; +use tw_hash::H32; +use tw_keypair::schnorr; +use tw_utxo::script::standard_script::opcodes::*; +use tw_utxo::script::Script; + +const VERIFY: bool = true; +const NO_VERIFY: bool = false; + +/// https://github.com/babylonchain/babylon/blob/dev/docs/transaction-impl-spec.md#op_return-output-description +/// ```txt +/// OP_RETURN OP_DATA_71 +/// ``` +pub fn new_op_return_script( + tag: &H32, + version: u8, + staker_key: &schnorr::XOnlyPublicKey, + finality_provider_key: &schnorr::XOnlyPublicKey, + locktime: u16, +) -> Script { + let mut buf = Vec::with_capacity(71); + buf.extend_from_slice(tag.as_slice()); + buf.push(version); + buf.extend_from_slice(staker_key.bytes().as_slice()); + buf.extend_from_slice(finality_provider_key.bytes().as_slice()); + buf.extend_from_slice(&locktime.to_be_bytes()); + + let mut s = Script::new(); + s.push(OP_RETURN); + s.push_slice(&buf); + s +} + +/// The timelock path locks the staker's Bitcoin for a pre-determined number of Bitcoin blocks. +/// https://github.com/babylonchain/babylon/blob/dev/docs/staking-script.md#1-timelock-path +/// +/// ```txt +/// OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY +/// ``` +pub fn new_timelock_script(staker_key: &schnorr::XOnlyPublicKey, locktime: u16) -> Script { + let mut s = Script::with_capacity(64); + append_single_sig(&mut s, staker_key, VERIFY); + s.push_int(locktime as i64); + s.push(OP_CHECKSEQUENCEVERIFY); + s +} + +/// The unbonding path allows the staker to on-demand unlock their locked Bitcoin before the timelock expires. +/// https://github.com/babylonchain/babylon/blob/dev/docs/staking-script.md#2-unbonding-path +/// +/// ```txt +/// OP_CHECKSIGVERIFY +/// OP_CHECKSIG OP_CHECKSIGADD ... OP_CHECKSIGADD +/// OP_NUMEQUAL +/// ``` +pub fn new_unbonding_script( + staker_key: &schnorr::XOnlyPublicKey, + covenants: &MultiSigOrderedKeys, +) -> Script { + let mut s = Script::with_capacity(64); + append_single_sig(&mut s, staker_key, VERIFY); + // Covenant multisig is always last in script so we do not run verify and leave + // last value on the stack. If we do not leave at least one element on the stack + // script will always error. + append_multi_sig( + &mut s, + covenants.public_keys_ordered(), + covenants.quorum(), + NO_VERIFY, + ); + s +} + +/// The slashing path is utilized for punishing finality providers and their delegators in the case of double signing. +/// https://github.com/babylonchain/babylon/blob/dev/docs/staking-script.md#3-slashing-path +/// +/// ```txt +/// OP_CHECKSIGVERIFY +/// OP_CHECKSIG OP_CHECKSIGADD ... OP_CHECKSIGADD +/// 1 OP_NUMEQUAL +/// OP_CHECKSIG OP_CHECKSIGADD ... OP_CHECKSIGADD +/// OP_NUMEQUAL +/// ``` +pub fn new_slashing_script( + staker_key: &schnorr::XOnlyPublicKey, + finality_providers_keys: &MultiSigOrderedKeys, + covenants: &MultiSigOrderedKeys, +) -> Script { + let mut s = Script::with_capacity(64); + append_single_sig(&mut s, staker_key, VERIFY); + // We need to run verify to clear the stack, as finality provider multisig is in the middle of the script. + append_multi_sig( + &mut s, + finality_providers_keys.public_keys_ordered(), + finality_providers_keys.quorum(), + VERIFY, + ); + // Covenant multisig is always last in script so we do not run verify and leave + // last value on the stack. If we do not leave at least one element on the stack + // script will always error. + append_multi_sig( + &mut s, + covenants.public_keys_ordered(), + covenants.quorum(), + NO_VERIFY, + ); + s +} + +fn append_single_sig(dst: &mut Script, key: &schnorr::XOnlyPublicKey, verify: bool) { + dst.push_slice(key.bytes().as_slice()); + if verify { + dst.push(OP_CHECKSIGVERIFY); + } else { + dst.push(OP_CHECKSIG); + } +} + +/// Creates a multisig script with given keys and signer threshold to +/// successfully execute script. +/// It validates whether threshold is not greater than number of keys. +/// If there is only one key provided it will return single key sig script. +/// Note: It is up to the caller to ensure that the keys are unique and sorted. +fn append_multi_sig( + dst: &mut Script, + pubkeys: &[schnorr::XOnlyPublicKey], + quorum: u32, + verify: bool, +) { + if pubkeys.len() == 1 { + return append_single_sig(dst, &pubkeys[0], verify); + } + + for (i, pk_xonly) in pubkeys.iter().enumerate() { + dst.push_slice(pk_xonly.bytes().as_slice()); + if i == 0 { + dst.push(OP_CHECKSIG); + } else { + dst.push(OP_CHECKSIGADD); + } + } + + dst.push_int(quorum as i64); + if verify { + dst.push(OP_NUMEQUALVERIFY); + } else { + dst.push(OP_NUMEQUAL); + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/mod.rs b/rust/chains/tw_bitcoin/src/babylon/mod.rs new file mode 100644 index 00000000000..c59063ae77d --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/mod.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod conditions; +pub mod multi_sig_ordered; +pub mod proto_builder; +pub mod spending_data; +pub mod spending_info; +pub mod tx_builder; diff --git a/rust/chains/tw_bitcoin/src/babylon/multi_sig_ordered.rs b/rust/chains/tw_bitcoin/src/babylon/multi_sig_ordered.rs new file mode 100644 index 00000000000..9faa7256f18 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/multi_sig_ordered.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use itertools::Itertools; +use std::collections::BTreeMap; +use tw_coin_entry::error::prelude::{ + OrTWError, ResultContext, SigningError, SigningErrorType, SigningResult, +}; +use tw_keypair::schnorr; +use tw_utxo::signature::BitcoinSchnorrSignature; + +type OptionalSignature = Option; +type PubkeySigMap = BTreeMap; + +pub struct MultiSigOrderedKeys { + pks: Vec, + quorum: u32, +} + +impl MultiSigOrderedKeys { + /// Sort the keys in lexicographical order. + pub fn new(mut pks: Vec, quorum: u32) -> SigningResult { + if pks.is_empty() { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No public keys provided"); + } + + if pks.len() < quorum as usize { + return SigningError::err(SigningErrorType::Error_invalid_params).context( + "Required number of valid signers is greater than number of provided keys", + ); + } + + // TODO it's not optimal to use a `HashSet` because the keys are sorted already. + if !pks.iter().all_unique() { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Public keys must be unique"); + } + + pks.sort(); + Ok(MultiSigOrderedKeys { pks, quorum }) + } + + pub fn public_keys_ordered(&self) -> &[schnorr::XOnlyPublicKey] { + &self.pks + } + + pub fn quorum(&self) -> u32 { + self.quorum + } + + pub fn with_partial_signatures<'a, I>(self, sigs: I) -> SigningResult + where + I: IntoIterator, + { + let mut pk_sig_map = MultiSigOrdered::checked(self.pks, self.quorum); + pk_sig_map.set_partial_signatures(sigs)?; + pk_sig_map.check_quorum()?; + Ok(pk_sig_map) + } +} + +#[derive(Clone, Debug)] +pub struct MultiSigOrdered { + pk_sig_map: PubkeySigMap, + quorum: u32, +} + +impl MultiSigOrdered { + /// `pk_sig_map` and `quorum` must be checked at [`MultiSigOrderedKeys::new`] already. + fn checked(pks: Vec, quorum: u32) -> Self { + let mut pk_sig_map = PubkeySigMap::new(); + + // Initialize the map with public keys and null signatures first. + for pk in pks { + pk_sig_map.insert(pk, None); + } + + MultiSigOrdered { pk_sig_map, quorum } + } + + fn set_partial_signatures<'a, I>(&mut self, sigs: I) -> SigningResult<()> + where + I: IntoIterator, + { + // Set the signatures for the specific public keys. + // There can be less signatures than public keys, but not less than `quorum`. + for (pk, sig) in sigs { + // Find the signature of the corresponding public key. + let pk_sig = self + .pk_sig_map + .get_mut(pk) + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Signature provided for an unknown public key")?; + + // Only one signature per public key allowed. + if pk_sig.is_some() { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Duplicate public key"); + } + *pk_sig = Some(sig.clone()); + } + Ok(()) + } + + fn check_quorum(&self) -> SigningResult<()> { + let sig_num = self.pk_sig_map.values().filter(|sig| sig.is_some()).count(); + if sig_num < self.quorum as usize { + return SigningError::err(SigningErrorType::Error_invalid_params).context(format!( + "Number of signatures '{sig_num}' is less than quorum '{}'", + self.quorum + )); + } + Ok(()) + } + + /// Get signatures sorted by corresponding public keys in reverse lexicographical order + /// because the script interpreter will pop the left-most byte-array as the first stack element, + /// the second-left-most byte array as the second stack element, and so on. + /// + /// In other words, + /// `[SigN] [SigN-1] ... [Sig0]` + /// where the list `Sig0 ... SigN` are the Schnorr signatures corresponding to the public keys `Pk0 ... PkN`. + /// + /// https://gnusha.org/pi/bitcoindev/20220820134850.ofvz7225zwcyffit@artanis (=== Spending K-of-N Multisig outputs ===) + pub fn get_signatures_reverse_order(&self) -> Vec { + self.pk_sig_map + .iter() + .rev() + .map(|(_pk, sig)| sig.clone()) + .collect() + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/proto_builder/mod.rs b/rust/chains/tw_bitcoin/src/babylon/proto_builder/mod.rs new file mode 100644 index 00000000000..8f3f6224bd6 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/proto_builder/mod.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::multi_sig_ordered::MultiSigOrderedKeys; +use crate::babylon::tx_builder::BabylonStakingParams; +use std::borrow::Cow; +use tw_coin_entry::error::prelude::*; +use tw_keypair::schnorr; +use tw_proto::BabylonStaking::Proto; +use tw_utxo::sighash::SighashType; +use tw_utxo::signature::BitcoinSchnorrSignature; + +pub mod output_protobuf; +pub mod utxo_protobuf; + +/// We always require only one finality provider to sign, +/// even if there are multiple finality providers in the script. +const FINALITY_PROVIDERS_QUORUM: u32 = 1; + +pub fn staking_params_from_proto( + params: &Option, +) -> SigningResult { + let params = params + .as_ref() + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No 'StakingInfo' params provided")?; + + let staker = schnorr::PublicKey::try_from(params.staker_public_key.as_ref()) + .into_tw() + .context("Invalid stakerPublicKey")?; + let staking_locktime: u16 = params + .staking_time + .try_into() + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("stakingTime cannot be greater than 65535")?; + + let covenants_pks = parse_schnorr_pks(¶ms.covenant_committee_public_keys) + .context("Invalid covenantCommitteePublicKeys")?; + let covenants = MultiSigOrderedKeys::new(covenants_pks, params.covenant_quorum) + .context("Invalid covenantCommitteePublicKeys")?; + + let finality_provider = parse_schnorr_pk(¶ms.finality_provider_public_key) + .context("Invalid finalityProviderPublicKey")?; + let finality_providers = + MultiSigOrderedKeys::new(vec![finality_provider], FINALITY_PROVIDERS_QUORUM) + .context("Invalid finalityProviderPublicKey")?; + + Ok(BabylonStakingParams { + staker, + staking_locktime, + finality_providers, + covenants, + }) +} + +pub fn parse_schnorr_pk(bytes: T) -> SigningResult +where + T: AsRef<[u8]>, +{ + schnorr::XOnlyPublicKey::try_from(bytes.as_ref()).into_tw() +} + +pub fn parse_schnorr_pks(pks: &[Cow<[u8]>]) -> SigningResult> { + pks.iter().map(parse_schnorr_pk).collect() +} + +pub fn parse_schnorr_pubkey_sig( + pubkey_sig: &Proto::PublicKeySignature, + sighash_ty: SighashType, +) -> SigningResult<(schnorr::XOnlyPublicKey, BitcoinSchnorrSignature)> { + let pk = parse_schnorr_pk(pubkey_sig.public_key.as_ref())?; + let sig = schnorr::Signature::try_from(pubkey_sig.signature.as_ref()) + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid signature")?; + let btc_sign = BitcoinSchnorrSignature::new(sig, sighash_ty)?; + Ok((pk, btc_sign)) +} diff --git a/rust/chains/tw_bitcoin/src/babylon/proto_builder/output_protobuf.rs b/rust/chains/tw_bitcoin/src/babylon/proto_builder/output_protobuf.rs new file mode 100644 index 00000000000..f7be9a9dbc9 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/proto_builder/output_protobuf.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::proto_builder::{parse_schnorr_pk, staking_params_from_proto}; +use crate::babylon::tx_builder::output::BabylonOutputBuilder; +use crate::modules::tx_builder::output_protobuf::OutputProtobuf; +use tw_coin_entry::error::prelude::*; +use tw_hash::H32; +use tw_proto::BabylonStaking::Proto; +use tw_utxo::context::UtxoContext; +use tw_utxo::transaction::standard_transaction::TransactionOutput; + +pub trait BabylonOutputProtobuf { + fn babylon_staking( + &self, + timelock: &Proto::mod_OutputBuilder::StakingOutput, + ) -> SigningResult; + + fn babylon_staking_op_return( + &self, + timelock: &Proto::mod_OutputBuilder::OpReturn, + ) -> SigningResult; + + fn babylon_unbonding( + &self, + unbonding: &Proto::mod_OutputBuilder::UnbondingOutput, + ) -> SigningResult; +} + +impl<'a, Context: UtxoContext> BabylonOutputProtobuf for OutputProtobuf<'a, Context> { + fn babylon_staking( + &self, + staking: &Proto::mod_OutputBuilder::StakingOutput, + ) -> SigningResult { + let params = staking_params_from_proto(&staking.params)?; + self.prepare_builder()?.babylon_staking(params) + } + + fn babylon_staking_op_return( + &self, + op_return: &Proto::mod_OutputBuilder::OpReturn, + ) -> SigningResult { + let tag = H32::try_from(op_return.tag.as_ref()) + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Expected exactly 4 bytes tag")?; + let staker = + parse_schnorr_pk(&op_return.staker_public_key).context("Invalid stakerPublicKey")?; + let staking_locktime: u16 = op_return + .staking_time + .try_into() + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("stakingTime cannot be greater than 65535")?; + let finality_provider = &parse_schnorr_pk(&op_return.finality_provider_public_key) + .context("Invalid finalityProviderPublicKeys")?; + + Ok(self.prepare_builder()?.babylon_staking_op_return( + &tag, + &staker, + finality_provider, + staking_locktime, + )) + } + + fn babylon_unbonding( + &self, + unbonding: &Proto::mod_OutputBuilder::UnbondingOutput, + ) -> SigningResult { + let params = staking_params_from_proto(&unbonding.params)?; + self.prepare_builder()?.babylon_unbonding(params) + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/proto_builder/utxo_protobuf.rs b/rust/chains/tw_bitcoin/src/babylon/proto_builder/utxo_protobuf.rs new file mode 100644 index 00000000000..bc12f4f9157 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/proto_builder/utxo_protobuf.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::proto_builder::{parse_schnorr_pubkey_sig, staking_params_from_proto}; +use crate::babylon::tx_builder::utxo::BabylonUtxoBuilder; +use crate::modules::tx_builder::utxo_protobuf::UtxoProtobuf; +use tw_coin_entry::error::prelude::*; +use tw_proto::BabylonStaking::Proto; +use tw_utxo::context::UtxoContext; +use tw_utxo::transaction::standard_transaction::TransactionInput; +use tw_utxo::transaction::UtxoToSign; + +pub trait BabylonUtxoProtobuf { + fn babylon_staking_timelock( + &self, + timelock: &Proto::mod_InputBuilder::StakingTimelockPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + fn babylon_staking_unbonding( + &self, + unbonding: &Proto::mod_InputBuilder::StakingUnbondingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + fn babylon_staking_slashing( + &self, + slashing: &Proto::mod_InputBuilder::StakingSlashingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + fn babylon_unbonding_timelock( + &self, + timelock: &Proto::mod_InputBuilder::UnbondingTimelockPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + fn babylon_unbonding_slashing( + &self, + slashing: &Proto::mod_InputBuilder::UnbondingSlashingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; +} + +impl<'a, Context: UtxoContext> BabylonUtxoProtobuf for UtxoProtobuf<'a, Context> { + fn babylon_staking_timelock( + &self, + timelock: &Proto::mod_InputBuilder::StakingTimelockPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let params = staking_params_from_proto(&timelock.params)?; + self.prepare_builder()? + .babylon_staking_timelock_path(params) + } + + fn babylon_staking_unbonding( + &self, + unbonding: &Proto::mod_InputBuilder::StakingUnbondingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let params = staking_params_from_proto(&unbonding.params)?; + let sighash_ty = self.sighash_ty()?; + + let covenant_signatures = unbonding + .covenant_committee_signatures + .iter() + .map(|pk_sig| parse_schnorr_pubkey_sig(pk_sig, sighash_ty)) + .collect::>>()?; + self.prepare_builder()? + .babylon_staking_unbonding_path(params, &covenant_signatures) + } + + fn babylon_staking_slashing( + &self, + _slashing: &Proto::mod_InputBuilder::StakingSlashingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + SigningError::err(SigningErrorType::Error_not_supported) + .context("'babylonStakingSlashing' is not supported at the moment") + } + + fn babylon_unbonding_timelock( + &self, + timelock: &Proto::mod_InputBuilder::UnbondingTimelockPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let params = staking_params_from_proto(&timelock.params)?; + self.prepare_builder()? + .babylon_unbonding_timelock_path(params) + } + + fn babylon_unbonding_slashing( + &self, + _slashing: &Proto::mod_InputBuilder::UnbondingSlashingPath, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + SigningError::err(SigningErrorType::Error_not_supported) + .context("'babylonUnbondingSlashing' is not supported at the moment") + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/spending_data.rs b/rust/chains/tw_bitcoin/src/babylon/spending_data.rs new file mode 100644 index 00000000000..5b50ad66e0b --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/spending_data.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::multi_sig_ordered::MultiSigOrdered; +use tw_memory::Data; +use tw_utxo::script::standard_script::claims; +use tw_utxo::script::Script; +use tw_utxo::signature::BitcoinSchnorrSignature; +use tw_utxo::spending_data::{SchnorrSpendingDataConstructor, SpendingData}; + +#[derive(Clone, Debug)] +pub struct BabylonUnbondingPath { + unbonding_script: Script, + control_block: Data, + /// Signatures signed by covenant committees. + /// Sorted by covenant committees public keys in reverse order. + covenant_committee_signatures: MultiSigOrdered, +} + +impl BabylonUnbondingPath { + pub fn new( + unbonding_script: Script, + control_block: Data, + covenant_committee_signatures: MultiSigOrdered, + ) -> Self { + BabylonUnbondingPath { + unbonding_script, + control_block, + covenant_committee_signatures, + } + } +} + +impl SchnorrSpendingDataConstructor for BabylonUnbondingPath { + fn get_spending_data(&self, sig: &BitcoinSchnorrSignature) -> SpendingData { + // Covenant committee signatures are sorted by corresponding public keys in reverse lexicographical order. + // That's because the script interpreter will pop the left-most byte-array as the first stack element, + // the second-left-most byte array as the second stack element, and so on. + let mut unbonding_sigs = self + .covenant_committee_signatures + .get_signatures_reverse_order(); + // User's signature is always last. + unbonding_sigs.push(Some(sig.clone())); + + SpendingData { + script_sig: Script::default(), + witness: claims::new_p2tr_script_path( + &unbonding_sigs, + self.unbonding_script.clone(), + self.control_block.clone(), + ), + } + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/spending_info.rs b/rust/chains/tw_bitcoin/src/babylon/spending_info.rs new file mode 100644 index 00000000000..9257803e34d --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/spending_info.rs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::conditions; +use crate::babylon::tx_builder::BabylonStakingParams; +use bitcoin::hashes::Hash; +use lazy_static::lazy_static; +use tw_coin_entry::error::prelude::*; +use tw_hash::{H256, H264}; +use tw_keypair::schnorr; +use tw_utxo::script::Script; + +lazy_static! { + /// Point with unknown discrete logarithm defined in: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs + /// using it as internal public key effectively disables taproot key spends. + pub static ref UNSPENDABLE_KEY_PATH_BYTES: H264 = + H264::from("0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"); + pub static ref UNSPENDABLE_KEY_PATH: schnorr::PublicKey = + schnorr::PublicKey::try_from(UNSPENDABLE_KEY_PATH_BYTES.as_slice()) + .expect("Expected a valid unspendable key path"); + pub static ref UNSPENDABLE_KEY_PATH_XONLY: bitcoin::key::UntweakedPublicKey = + bitcoin::key::UntweakedPublicKey::from_slice(UNSPENDABLE_KEY_PATH.x_only().bytes().as_slice()) + .expect("Expected a valid unspendable key path"); +} + +pub struct StakingSpendInfo { + timelock_script: Script, + unbonding_script: Script, + slashing_script: Script, + spend_info: bitcoin::taproot::TaprootSpendInfo, +} + +impl StakingSpendInfo { + pub fn new(params: &BabylonStakingParams) -> SigningResult { + let staker_xonly = params.staker.x_only(); + + let timelock_script = + conditions::new_timelock_script(&staker_xonly, params.staking_locktime); + let unbonding_script = conditions::new_unbonding_script(&staker_xonly, ¶ms.covenants); + let slashing_script = conditions::new_slashing_script( + &staker_xonly, + ¶ms.finality_providers, + ¶ms.covenants, + ); + + // IMPORTANT - order and leaf depths are important! + let spend_info = bitcoin::taproot::TaprootBuilder::new() + .add_leaf(2, timelock_script.clone().into()) + .expect("Leaf added at a valid depth") + .add_leaf(2, unbonding_script.clone().into()) + .expect("Leaf added at a valid depth") + .add_leaf(1, slashing_script.clone().into()) + .expect("Leaf added at a valid depth") + .finalize(secp256k1::SECP256K1, *UNSPENDABLE_KEY_PATH_XONLY) + .expect("Expected a valid Taproot tree"); + + Ok(StakingSpendInfo { + timelock_script, + unbonding_script, + slashing_script, + spend_info, + }) + } + + pub fn merkle_root(&self) -> SigningResult { + merkle_root(&self.spend_info) + } + + pub fn timelock_script(&self) -> &Script { + &self.timelock_script + } + + pub fn unbonding_script(&self) -> &Script { + &self.unbonding_script + } + + pub fn slashing_script(&self) -> &Script { + &self.slashing_script + } + + pub fn timelock_control_block(&self) -> SigningResult { + control_block(&self.spend_info, &self.timelock_script) + } + + pub fn unbonding_control_block(&self) -> SigningResult { + control_block(&self.spend_info, &self.unbonding_script) + } + + pub fn slashing_control_block(&self) -> SigningResult { + control_block(&self.spend_info, &self.slashing_script) + } +} + +pub struct UnbondingSpendInfo { + timelock_script: Script, + slashing_script: Script, + spend_info: bitcoin::taproot::TaprootSpendInfo, +} + +impl UnbondingSpendInfo { + pub fn new(params: &BabylonStakingParams) -> SigningResult { + let staker_xonly = params.staker.x_only(); + + let timelock_script = + conditions::new_timelock_script(&staker_xonly, params.staking_locktime); + let slashing_script = conditions::new_slashing_script( + &staker_xonly, + ¶ms.finality_providers, + ¶ms.covenants, + ); + + // IMPORTANT - order and leaf depths are important! + let spend_info = bitcoin::taproot::TaprootBuilder::new() + .add_leaf(1, slashing_script.clone().into()) + .expect("Leaf added at a valid depth") + .add_leaf(1, timelock_script.clone().into()) + .expect("Leaf added at a valid depth") + .finalize(secp256k1::SECP256K1, *UNSPENDABLE_KEY_PATH_XONLY) + .expect("Expected a valid Taproot tree"); + + Ok(UnbondingSpendInfo { + timelock_script, + slashing_script, + spend_info, + }) + } + + pub fn merkle_root(&self) -> SigningResult { + merkle_root(&self.spend_info) + } + + pub fn timelock_script(&self) -> &Script { + &self.timelock_script + } + + pub fn slashing_script(&self) -> &Script { + &self.slashing_script + } + + pub fn timelock_control_block(&self) -> SigningResult { + control_block(&self.spend_info, &self.timelock_script) + } + + pub fn slashing_control_block(&self) -> SigningResult { + control_block(&self.spend_info, &self.slashing_script) + } +} + +fn control_block( + spend_info: &bitcoin::taproot::TaprootSpendInfo, + script: &Script, +) -> SigningResult { + let script = bitcoin::script::ScriptBuf::from_bytes(script.to_vec()); + spend_info + .control_block(&(script, bitcoin::taproot::LeafVersion::TapScript)) + .or_tw_err(SigningErrorType::Error_internal) + .context("'TaprootSpendInfo::control_block' is None") +} + +fn merkle_root(spend_info: &bitcoin::taproot::TaprootSpendInfo) -> SigningResult { + spend_info + .merkle_root() + .map(|root| H256::from(root.to_byte_array())) + .or_tw_err(SigningErrorType::Error_internal) + .context("No merkle root of the Babylon Staking transaction spend info") +} diff --git a/rust/chains/tw_bitcoin/src/babylon/tx_builder/mod.rs b/rust/chains/tw_bitcoin/src/babylon/tx_builder/mod.rs new file mode 100644 index 00000000000..f2ad565b831 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/tx_builder/mod.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon::multi_sig_ordered::MultiSigOrderedKeys; +use tw_keypair::schnorr; + +pub mod output; +pub mod utxo; + +/// Unbonding parameters are the same as Staking except `staking_locktime` means an unbonding timelock. +pub type BabylonUnbondingParams = BabylonStakingParams; + +pub struct BabylonStakingParams { + pub staker: schnorr::PublicKey, + pub staking_locktime: u16, + pub finality_providers: MultiSigOrderedKeys, + pub covenants: MultiSigOrderedKeys, +} diff --git a/rust/chains/tw_bitcoin/src/babylon/tx_builder/output.rs b/rust/chains/tw_bitcoin/src/babylon/tx_builder/output.rs new file mode 100644 index 00000000000..e8b9d61ba1f --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/tx_builder/output.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon; +use crate::babylon::tx_builder::{BabylonStakingParams, BabylonUnbondingParams}; +use tw_coin_entry::error::prelude::*; +use tw_hash::H32; +use tw_keypair::schnorr; +use tw_utxo::script::standard_script::conditions; +use tw_utxo::transaction::standard_transaction::builder::OutputBuilder; +use tw_utxo::transaction::standard_transaction::TransactionOutput; + +pub const VERSION: u8 = 0; + +/// An extension of the [`OutputBuilder`] with Babylon BTC Staking outputs. +pub trait BabylonOutputBuilder: Sized { + /// Create a Staking Output. + fn babylon_staking(self, params: BabylonStakingParams) -> SigningResult; + + /// Creates an OP_RETURN output used to identify the staking transaction among other transactions in the Bitcoin ledger. + fn babylon_staking_op_return( + self, + tag: &H32, + staker_key: &schnorr::XOnlyPublicKey, + finality_provider_key: &schnorr::XOnlyPublicKey, + staking_locktime: u16, + ) -> TransactionOutput; + + fn babylon_unbonding(self, params: BabylonUnbondingParams) -> SigningResult; +} + +impl BabylonOutputBuilder for OutputBuilder { + fn babylon_staking(self, params: BabylonStakingParams) -> SigningResult { + let spend_info = babylon::spending_info::StakingSpendInfo::new(¶ms)?; + let merkle_root = spend_info.merkle_root()?; + + Ok(TransactionOutput { + value: self.get_amount(), + script_pubkey: conditions::new_p2tr_script_path( + // Using an unspendable key as a P2TR internal public key effectively disables taproot key spends. + &babylon::spending_info::UNSPENDABLE_KEY_PATH.compressed(), + &merkle_root, + ), + }) + } + + fn babylon_staking_op_return( + self, + tag: &H32, + staker_key: &schnorr::XOnlyPublicKey, + finality_provider_key: &schnorr::XOnlyPublicKey, + staking_locktime: u16, + ) -> TransactionOutput { + let op_return = babylon::conditions::new_op_return_script( + tag, + VERSION, + staker_key, + finality_provider_key, + staking_locktime, + ); + TransactionOutput { + value: self.get_amount(), + script_pubkey: op_return, + } + } + + fn babylon_unbonding(self, params: BabylonUnbondingParams) -> SigningResult { + let spend_info = babylon::spending_info::UnbondingSpendInfo::new(¶ms)?; + let merkle_root = spend_info.merkle_root()?; + + Ok(TransactionOutput { + value: self.get_amount(), + script_pubkey: conditions::new_p2tr_script_path( + // Using an unspendable key as a P2TR internal public key effectively disables taproot key spends. + &babylon::spending_info::UNSPENDABLE_KEY_PATH.compressed(), + &merkle_root, + ), + }) + } +} diff --git a/rust/chains/tw_bitcoin/src/babylon/tx_builder/utxo.rs b/rust/chains/tw_bitcoin/src/babylon/tx_builder/utxo.rs new file mode 100644 index 00000000000..293dc160241 --- /dev/null +++ b/rust/chains/tw_bitcoin/src/babylon/tx_builder/utxo.rs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::babylon; +use crate::babylon::spending_data; +use crate::babylon::spending_info::UNSPENDABLE_KEY_PATH; +use crate::babylon::tx_builder::{BabylonStakingParams, BabylonUnbondingParams}; +use tw_coin_entry::error::prelude::*; +use tw_keypair::schnorr; +use tw_utxo::signature::BitcoinSchnorrSignature; +use tw_utxo::spending_data::SpendingDataConstructor; +use tw_utxo::transaction::standard_transaction::builder::UtxoBuilder; +use tw_utxo::transaction::standard_transaction::TransactionInput; +use tw_utxo::transaction::UtxoToSign; + +/// An extension of the [`UtxoBuilder`] with Babylon BTC Staking outputs. +pub trait BabylonUtxoBuilder: Sized { + /// Spend a Staking Output via timelock path (staking time expired). + /// In other words, create a Withdraw transaction. + fn babylon_staking_timelock_path( + self, + params: BabylonStakingParams, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + /// Spend a Staking Output via unbonding path. + /// In other words, create an Unbonding transaction. + fn babylon_staking_unbonding_path( + self, + params: BabylonStakingParams, + covenant_committee_signatures: &[(schnorr::XOnlyPublicKey, BitcoinSchnorrSignature)], + ) -> SigningResult<(TransactionInput, UtxoToSign)>; + + fn babylon_unbonding_timelock_path( + self, + params: BabylonUnbondingParams, + ) -> SigningResult<(TransactionInput, UtxoToSign)>; +} + +impl BabylonUtxoBuilder for UtxoBuilder { + fn babylon_staking_timelock_path( + self, + params: BabylonStakingParams, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let spend_info = babylon::spending_info::StakingSpendInfo::new(¶ms)?; + + let control_block = spend_info.timelock_control_block()?.serialize(); + let merkle_root = spend_info.merkle_root()?; + let timelock_script = spend_info.timelock_script().clone(); + + self.p2tr_script_path() + .reveal_script_pubkey(timelock_script) + // Staker is responsible to sign the UTXO. + .spender_public_key(¶ms.staker) + // Babylon Staking or Unbonding output was created using an unspendable internal public key, + // that means taproot key spends is disabled. + .restore_prevout_script_pubkey(&UNSPENDABLE_KEY_PATH, &merkle_root) + .control_block(control_block) + .build() + } + + fn babylon_staking_unbonding_path( + self, + params: BabylonStakingParams, + covenant_committee_signatures: &[(schnorr::XOnlyPublicKey, BitcoinSchnorrSignature)], + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let spend_info = babylon::spending_info::StakingSpendInfo::new(¶ms)?; + let signatures = params + .covenants + .with_partial_signatures(covenant_committee_signatures)?; + let unbonding_script = spend_info.unbonding_script(); + + let unbonding_control_block = spend_info.unbonding_control_block()?.serialize(); + let spending_data_ctor = + SpendingDataConstructor::schnorr(spending_data::BabylonUnbondingPath::new( + unbonding_script.clone(), + unbonding_control_block.clone(), + signatures, + )); + + let merkle_root = spend_info.merkle_root()?; + + self.p2tr_script_path() + .reveal_script_pubkey(unbonding_script.clone()) + // Staker is responsible to sign the UTXO. + .spender_public_key(¶ms.staker) + // Babylon Staking or Unbonding output was created using an unspendable internal public key, + // that means taproot key spends is disabled. + .restore_prevout_script_pubkey(&UNSPENDABLE_KEY_PATH, &merkle_root) + .control_block(unbonding_control_block) + // For Babylon Unbonding path we use a custom spending data constructor. + .custom_spending_data_ctor(spending_data_ctor) + .build() + } + + fn babylon_unbonding_timelock_path( + self, + params: BabylonUnbondingParams, + ) -> SigningResult<(TransactionInput, UtxoToSign)> { + let spend_info = babylon::spending_info::UnbondingSpendInfo::new(¶ms)?; + + let control_block = spend_info.timelock_control_block()?.serialize(); + let merkle_root = spend_info.merkle_root()?; + let timelock_script = spend_info.timelock_script().clone(); + + self.p2tr_script_path() + .reveal_script_pubkey(timelock_script.clone()) + // Staker is responsible to sign the UTXO. + .spender_public_key(¶ms.staker) + // Babylon Staking or Unbonding output was created using an unspendable internal public key, + // that means taproot key spends is disabled. + .restore_prevout_script_pubkey(&UNSPENDABLE_KEY_PATH, &merkle_root) + .control_block(control_block) + .build() + } +} diff --git a/rust/chains/tw_bitcoin/src/lib.rs b/rust/chains/tw_bitcoin/src/lib.rs index 6f1db4b73ee..d4f6d0206ee 100644 --- a/rust/chains/tw_bitcoin/src/lib.rs +++ b/rust/chains/tw_bitcoin/src/lib.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +pub mod babylon; pub mod context; pub mod entry; pub mod modules; diff --git a/rust/chains/tw_bitcoin/src/modules/tx_builder/output_protobuf.rs b/rust/chains/tw_bitcoin/src/modules/tx_builder/output_protobuf.rs index 7b503a0f925..fcdb1801da5 100644 --- a/rust/chains/tw_bitcoin/src/modules/tx_builder/output_protobuf.rs +++ b/rust/chains/tw_bitcoin/src/modules/tx_builder/output_protobuf.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +use crate::babylon::proto_builder::output_protobuf::BabylonOutputProtobuf; use crate::modules::tx_builder::BitcoinChainInfo; use std::marker::PhantomData; use std::str::FromStr; @@ -50,6 +51,11 @@ impl<'a, Context: UtxoContext> OutputProtobuf<'a, Context> { }, BuilderType::brc20_inscribe(ref inscription) => self.brc20_inscribe(inscription), BuilderType::op_return(ref data) => self.op_return(data), + BuilderType::babylon_staking(ref staking) => self.babylon_staking(staking), + BuilderType::babylon_staking_op_return(ref op_return) => { + self.babylon_staking_op_return(op_return) + }, + BuilderType::babylon_unbonding(ref unbonding) => self.babylon_unbonding(unbonding), BuilderType::None => SigningError::err(SigningErrorType::Error_invalid_params) .context("No Output Builder type provided"), }, diff --git a/rust/chains/tw_bitcoin/src/modules/tx_builder/utxo_protobuf.rs b/rust/chains/tw_bitcoin/src/modules/tx_builder/utxo_protobuf.rs index aa21a02f41b..d64c1cbe08d 100644 --- a/rust/chains/tw_bitcoin/src/modules/tx_builder/utxo_protobuf.rs +++ b/rust/chains/tw_bitcoin/src/modules/tx_builder/utxo_protobuf.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +use crate::babylon::proto_builder::utxo_protobuf::BabylonUtxoProtobuf; use crate::modules::tx_builder::public_keys::PublicKeys; use crate::modules::tx_builder::script_parser::{StandardScript, StandardScriptParser}; use crate::modules::tx_builder::BitcoinChainInfo; @@ -56,6 +57,21 @@ impl<'a, Context: UtxoContext> UtxoProtobuf<'a, Context> { BuilderType::p2tr_key_path(ref key_path) => self.p2tr_key_path(key_path), // BuilderType::p2tr_script_path(ref script) => self.p2tr_script_path(script), BuilderType::brc20_inscribe(ref inscription) => self.brc20_inscribe(inscription), + BuilderType::babylon_staking_timelock_path(ref timelock) => { + self.babylon_staking_timelock(timelock) + }, + BuilderType::babylon_staking_unbonding_path(ref unbonding) => { + self.babylon_staking_unbonding(unbonding) + }, + BuilderType::babylon_staking_slashing_path(ref slashing) => { + self.babylon_staking_slashing(slashing) + }, + BuilderType::babylon_unbonding_timelock_path(ref timelock) => { + self.babylon_unbonding_timelock(timelock) + }, + BuilderType::babylon_unbonding_slashing_path(ref slashing) => { + self.babylon_unbonding_slashing(slashing) + }, BuilderType::None => SigningError::err(SigningErrorType::Error_invalid_params) .context("No Input Builder type provided"), }, @@ -174,7 +190,7 @@ impl<'a, Context: UtxoContext> UtxoProtobuf<'a, Context> { pub fn prepare_builder(&self) -> SigningResult { let OutPoint { hash, index } = parse_out_point(&self.input.out_point)?; - let sighash_ty = SighashType::from_u32(self.input.sighash_type)?; + let sighash_ty = self.sighash_ty()?; if self.input.value < 0 { return SigningError::err(SigningErrorType::Error_invalid_utxo_amount) @@ -197,6 +213,10 @@ impl<'a, Context: UtxoContext> UtxoProtobuf<'a, Context> { .sighash_type(sighash_ty)) } + pub fn sighash_ty(&self) -> SigningResult { + SighashType::from_u32(self.input.sighash_type) + } + /// Tries to convert [`Proto::PublicKeyOrHash`] to [`Hash`]. /// Please note `P2PKH` and `P2WPKH` use the same `ripemd(sha256(x))` hash function. fn get_ecdsa_pubkey_from_proto( diff --git a/rust/chains/tw_bitcoin/tests/babylon_staking.rs b/rust/chains/tw_bitcoin/tests/babylon_staking.rs new file mode 100644 index 00000000000..801d7cfc4fe --- /dev/null +++ b/rust/chains/tw_bitcoin/tests/babylon_staking.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use bitcoin::hashes::Hash; +use bitcoin::taproot::TaprootBuilder; +use bitcoin::ScriptBuf; +use secp256k1::{PublicKey, SECP256K1}; +use tw_bitcoin::babylon::conditions; +use tw_bitcoin::babylon::multi_sig_ordered::MultiSigOrderedKeys; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_keypair::schnorr; + +const BABYLON_MERKLE_ROOTS: &str = include_str!("data/babylon_staking_merkle_roots.json"); +const BABYLON_TRANSACTIONS: &str = include_str!("data/babylon_staking_transactions.json"); + +fn parse_pk(value: &serde_json::Value) -> schnorr::XOnlyPublicKey { + let pk = value.as_str().unwrap().decode_hex().unwrap(); + schnorr::PublicKey::try_from(pk.as_slice()) + .unwrap() + .x_only() +} + +fn parse_pks(value: &serde_json::Value) -> Vec { + value.as_array().unwrap().iter().map(parse_pk).collect() +} + +#[test] +fn test_babylon_scripts() { + let input: serde_json::Value = serde_json::from_str(BABYLON_TRANSACTIONS).unwrap(); + + for test in input.as_array().unwrap() { + let name = test["name"].as_str().unwrap(); + let params = &test["parameters"]; + let expected = &test["expected"]; + + let covenant_public_keys = parse_pks(¶ms["covenant_public_keys"]); + let convenant_quorum = params["covenant_quorum"].as_u64().unwrap() as u32; + let finality_provider_public_keys = parse_pks(¶ms["finality_provider_public_keys"]); + let staker_public_key = parse_pk(¶ms["staker_public_key"]); + let staker_time = params["staking_time"].as_u64().unwrap() as u16; + + let covenants = MultiSigOrderedKeys::new(covenant_public_keys, convenant_quorum).unwrap(); + let finality_providers = + MultiSigOrderedKeys::new(finality_provider_public_keys, 1).unwrap(); + + let expected_timelock_script = expected["staking_transaction_timelock_script_hex"] + .as_str() + .unwrap(); + let expected_unbonding_script = expected["staking_transaction_unbonding_script_hex"] + .as_str() + .unwrap(); + let expected_slashing_script = expected["staking_transaction_slashing_script_hex"] + .as_str() + .unwrap(); + + let timelock_script = conditions::new_timelock_script(&staker_public_key, staker_time); + let unbonding_script = conditions::new_unbonding_script(&staker_public_key, &covenants); + let slashing_script = + conditions::new_slashing_script(&staker_public_key, &finality_providers, &covenants); + + assert_eq!( + timelock_script.as_slice().to_hex(), + expected_timelock_script, + "Test '{}' Invalid timelock script", + name + ); + assert_eq!( + unbonding_script.as_slice().to_hex(), + expected_unbonding_script, + "Test '{}' Invalid unbonding script", + name + ); + assert_eq!( + slashing_script.as_slice().to_hex(), + expected_slashing_script, + "Test '{}' Invalid slashing script", + name + ); + } +} + +#[test] +fn test_babylon_staking_taproot_tree() { + let input: serde_json::Value = serde_json::from_str(BABYLON_MERKLE_ROOTS).unwrap(); + + // Any public key. + let pubkey = "024000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafd" + .decode_hex() + .unwrap(); + let pubkey = PublicKey::from_slice(&pubkey).unwrap(); + let (xonly, _) = pubkey.x_only_public_key(); + + for test in input.as_array().unwrap() { + let timelock = test["timelock"].as_str().unwrap().decode_hex().unwrap(); + let unbond = test["unbond"].as_str().unwrap().decode_hex().unwrap(); + let slash = test["slash"].as_str().unwrap().decode_hex().unwrap(); + let root_hash = test["root_hash"].as_str().unwrap(); + + let timelock = ScriptBuf::from_bytes(timelock); + let unbond = ScriptBuf::from_bytes(unbond); + let slash = ScriptBuf::from_bytes(slash); + + let spend_info = TaprootBuilder::new() + .add_leaf(2, timelock) + .unwrap() + .add_leaf(2, unbond) + .unwrap() + .add_leaf(1, slash) + .unwrap() + .finalize(&SECP256K1, xonly) + .unwrap(); + + let mut actual_root = spend_info.merkle_root().unwrap().to_byte_array(); + actual_root.reverse(); + assert_eq!(actual_root.to_hex(), root_hash); + } +} diff --git a/rust/chains/tw_bitcoin/tests/data/babylon_staking_merkle_roots.json b/rust/chains/tw_bitcoin/tests/data/babylon_staking_merkle_roots.json new file mode 100644 index 00000000000..4e53cbf6eb3 --- /dev/null +++ b/rust/chains/tw_bitcoin/tests/data/babylon_staking_merkle_roots.json @@ -0,0 +1,722 @@ +[ + { + "timelock": "200ccdf306ec819daa0e37c612b60ac3ed7161da8c71bd879e21b0c4d89cf17655ad02e803b2", + "unbond": "200ccdf306ec819daa0e37c612b60ac3ed7161da8c71bd879e21b0c4d89cf17655ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200ccdf306ec819daa0e37c612b60ac3ed7161da8c71bd879e21b0c4d89cf17655ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "909f7b753a4c8dc1e6d5236c59dd5ec870f46b853bd7473f75621505df8c8897" + }, + { + "timelock": "2039d43dd42cf3080dd75c95076e8909ac631d3251b6c0bec0c280ac93066e63d7ad02e803b2", + "unbond": "2039d43dd42cf3080dd75c95076e8909ac631d3251b6c0bec0c280ac93066e63d7ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2039d43dd42cf3080dd75c95076e8909ac631d3251b6c0bec0c280ac93066e63d7ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "4f988ae405b11d7f9116127f55168850b74a8ec15c73402283baa2c6ccf1c41e" + }, + { + "timelock": "2005c4a8f23df120b503e6dea8f281ced6d8e750c8a28f349742fd830be9f12cf3ad02e803b2", + "unbond": "2005c4a8f23df120b503e6dea8f281ced6d8e750c8a28f349742fd830be9f12cf3ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2005c4a8f23df120b503e6dea8f281ced6d8e750c8a28f349742fd830be9f12cf3ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "db97fb09eaf3547b1837c806c74546e7b7d99fccaf1131ba1adc699bf5a684ec" + }, + { + "timelock": "2005ca6027527ba006ce23fba369daa3cfa412b8ce755931b123ec1dc3f73a4770ad02e803b2", + "unbond": "2005ca6027527ba006ce23fba369daa3cfa412b8ce755931b123ec1dc3f73a4770ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2005ca6027527ba006ce23fba369daa3cfa412b8ce755931b123ec1dc3f73a4770ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "f63181ca1755460952ed5205e4ea2d617e4b22456d79c5e19b03f9a97bbd2d45" + }, + { + "timelock": "2013ca6721d19c4e43257aae38455bd4af2a1146304724c2bd3c8105064420cb06ad02e803b2", + "unbond": "2013ca6721d19c4e43257aae38455bd4af2a1146304724c2bd3c8105064420cb06ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2013ca6721d19c4e43257aae38455bd4af2a1146304724c2bd3c8105064420cb06ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "414fa954443067e41ea4e2c6b58dd26ae407421e09fd2b53cd540f57e3dd42fc" + }, + { + "timelock": "207ff701ec3f3a00b1677b5ac57892032be689fb227963be484cafd39d50540dadad02e803b2", + "unbond": "207ff701ec3f3a00b1677b5ac57892032be689fb227963be484cafd39d50540dadad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "207ff701ec3f3a00b1677b5ac57892032be689fb227963be484cafd39d50540dadad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "89c30df4587e8749dbb287c91cf49bfb9104128db331c08113d555fb79f06297" + }, + { + "timelock": "20ce447259457b41e38d7d5ac0229ff27aa19087c2e664e89d34645bbd1c6cf822ad02e803b2", + "unbond": "20ce447259457b41e38d7d5ac0229ff27aa19087c2e664e89d34645bbd1c6cf822ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ce447259457b41e38d7d5ac0229ff27aa19087c2e664e89d34645bbd1c6cf822ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "321338a5ed106fcc00b778668beb0769697836a972e8929fe82fa8d2adcaef0c" + }, + { + "timelock": "20481e18a05e725cfcc1e9fb107268bd90f3dd5bea476dbddee94978c15f35c8a5ad02e803b2", + "unbond": "20481e18a05e725cfcc1e9fb107268bd90f3dd5bea476dbddee94978c15f35c8a5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20481e18a05e725cfcc1e9fb107268bd90f3dd5bea476dbddee94978c15f35c8a5ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "4dda164d280edc383144e3c985070f363d416ad7c8b53836388409bb74afae4f" + }, + { + "timelock": "20cc6a4ccf8f9a0fda5a5c9298d77730452a4ab305321e2f48f3662494fdc603cead02e803b2", + "unbond": "20cc6a4ccf8f9a0fda5a5c9298d77730452a4ab305321e2f48f3662494fdc603cead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20cc6a4ccf8f9a0fda5a5c9298d77730452a4ab305321e2f48f3662494fdc603cead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "73a7e10f7c5c2749e78a7d5c14b5a0d4c79579a47b621f7ea4c4308861b371ce" + }, + { + "timelock": "2019721d18cf18d53a151a41898f186ca7583debebb58b3dab947075de51844304ad02e803b2", + "unbond": "2019721d18cf18d53a151a41898f186ca7583debebb58b3dab947075de51844304ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2019721d18cf18d53a151a41898f186ca7583debebb58b3dab947075de51844304ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "8b5c55c50148edf3c3e16686eb909c4c02b04af0ab1f99ff6e5d9f78c57050b2" + }, + { + "timelock": "20566ff807867dfb4edad6c102cb0f3fac3b780e461a37fcf905d8d938dbb9a59dad02e803b2", + "unbond": "20566ff807867dfb4edad6c102cb0f3fac3b780e461a37fcf905d8d938dbb9a59dad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20566ff807867dfb4edad6c102cb0f3fac3b780e461a37fcf905d8d938dbb9a59dad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "6d256a204489db25fdd4e72d90a535b01a9a493e2d0a656dcb7709ac1befd73f" + }, + { + "timelock": "206a5ce87906ff32eff00096fb4dcf565d1e3b4583178d963202192b5b2d11f479ad02e803b2", + "unbond": "206a5ce87906ff32eff00096fb4dcf565d1e3b4583178d963202192b5b2d11f479ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "206a5ce87906ff32eff00096fb4dcf565d1e3b4583178d963202192b5b2d11f479ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "ec9797c32470b13cffdf057a8b592ed49969b3a04185df8234624a6ecb15677a" + }, + { + "timelock": "209f16b1f696c04b7eee01dd6d8fee62c35a091aae3aa1b5914abae91d7d9f2b52ad02e803b2", + "unbond": "209f16b1f696c04b7eee01dd6d8fee62c35a091aae3aa1b5914abae91d7d9f2b52ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "209f16b1f696c04b7eee01dd6d8fee62c35a091aae3aa1b5914abae91d7d9f2b52ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "388bc73a00ffdd56a692e1f5f1b3f65e013579d1dff09ea7f3a00a19f5831ac6" + }, + { + "timelock": "200bd371d96e48efdd3c405f027b08341c9278e2652fb1ad7edc2f8985389c1530ad02e803b2", + "unbond": "200bd371d96e48efdd3c405f027b08341c9278e2652fb1ad7edc2f8985389c1530ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200bd371d96e48efdd3c405f027b08341c9278e2652fb1ad7edc2f8985389c1530ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "9915a9b5c8ebab144a6bcd19385fb748011de7b88ee8db432ec2408a7462676a" + }, + { + "timelock": "20dedc2e51daaaa19b337341b5fcd779c57b59bec18a31cf883fb2f489cf27c4b4ad02e803b2", + "unbond": "20dedc2e51daaaa19b337341b5fcd779c57b59bec18a31cf883fb2f489cf27c4b4ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20dedc2e51daaaa19b337341b5fcd779c57b59bec18a31cf883fb2f489cf27c4b4ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "8f0e14deaee7ea3d82f247e9e9f07c3767dc4857a1425aaf16979307e4154b2c" + }, + { + "timelock": "20190af905f87d3e035b1b8cf27fe6ba9f13512d446cdf98de2702f4c753555de7ad02e803b2", + "unbond": "20190af905f87d3e035b1b8cf27fe6ba9f13512d446cdf98de2702f4c753555de7ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20190af905f87d3e035b1b8cf27fe6ba9f13512d446cdf98de2702f4c753555de7ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "35a41b18bc48fe25e97c8960e37df41d9651af883b3582b1f61d4b86ed47f401" + }, + { + "timelock": "20f90ed4b62d8e09c8ec2c518caea33d59553b20ed942b7d01d197b5592eb32b11ad02e803b2", + "unbond": "20f90ed4b62d8e09c8ec2c518caea33d59553b20ed942b7d01d197b5592eb32b11ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20f90ed4b62d8e09c8ec2c518caea33d59553b20ed942b7d01d197b5592eb32b11ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5cecdc132b60cba551a315217757412cd2c49d22e24113367305ed39edbf3235" + }, + { + "timelock": "209f6b3a98d800353c4daffa635fe7fc3ed48750680534a8cbb9f65aec090ba5fead02e803b2", + "unbond": "209f6b3a98d800353c4daffa635fe7fc3ed48750680534a8cbb9f65aec090ba5fead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "209f6b3a98d800353c4daffa635fe7fc3ed48750680534a8cbb9f65aec090ba5fead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b3d2ad47db5cb358d517fd6a2e8d94ceb5252e41d1fa4e94974ee4affe5d90bf" + }, + { + "timelock": "20e24d0d721201a665f776e7a27ee378426843735599870adaa620fbd96a19fdc2ad02e803b2", + "unbond": "20e24d0d721201a665f776e7a27ee378426843735599870adaa620fbd96a19fdc2ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20e24d0d721201a665f776e7a27ee378426843735599870adaa620fbd96a19fdc2ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "7ef011c1b261b2653e65c7389e873e9661b8a0efa5b84f3ec9da41304f63b3c6" + }, + { + "timelock": "2079113365be732b6e3b09778728ed611d65c8e0bbbdaaa6e4655bfd92bc15b892ad02e803b2", + "unbond": "2079113365be732b6e3b09778728ed611d65c8e0bbbdaaa6e4655bfd92bc15b892ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2079113365be732b6e3b09778728ed611d65c8e0bbbdaaa6e4655bfd92bc15b892ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "1b7bffc2d328f90b21b1bc1d9e30d6425c74075258b3ac374b7e9f95777d876f" + }, + { + "timelock": "20baa1c3537d978c3656be7b6d69388ae8e8b33aefb2118a034cf688367f84d349ad02e803b2", + "unbond": "20baa1c3537d978c3656be7b6d69388ae8e8b33aefb2118a034cf688367f84d349ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20baa1c3537d978c3656be7b6d69388ae8e8b33aefb2118a034cf688367f84d349ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "ffe53943d93305cab5beb20adc2ace52981d1d561eaf93ac1aff70e1d81c1101" + }, + { + "timelock": "2022fe44bb002da47c44defa93bf6349691a2287a3feadecc96b19bfc404deddb1ad02e803b2", + "unbond": "2022fe44bb002da47c44defa93bf6349691a2287a3feadecc96b19bfc404deddb1ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2022fe44bb002da47c44defa93bf6349691a2287a3feadecc96b19bfc404deddb1ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "81ab658818dc213ca6802dbbf81dee2bde245749dc0e64c5e9166cee326ba285" + }, + { + "timelock": "20eaaebdf6f55e80dce34f0d22079d43b8a7b99d287a8959b3ab12fc23520738faad02e803b2", + "unbond": "20eaaebdf6f55e80dce34f0d22079d43b8a7b99d287a8959b3ab12fc23520738faad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20eaaebdf6f55e80dce34f0d22079d43b8a7b99d287a8959b3ab12fc23520738faad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "53f2604209cddf55b4ecdd8c2312d2180d8aa1a43c52000fdf2eb1adc420911b" + }, + { + "timelock": "20901a9a6088ed587093a0f180b0340a46d8b834820bfefe495fad1499170b8a82ad02e803b2", + "unbond": "20901a9a6088ed587093a0f180b0340a46d8b834820bfefe495fad1499170b8a82ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20901a9a6088ed587093a0f180b0340a46d8b834820bfefe495fad1499170b8a82ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "ca4c62a585d2509e4b58f9583037276e9a5b2815e79908ed7537b7f25faffa92" + }, + { + "timelock": "205c92c077fead6d6ef11a0f2b017d1b33c997ff54ffeb0e9e1aca50a12f3def37ad02e803b2", + "unbond": "205c92c077fead6d6ef11a0f2b017d1b33c997ff54ffeb0e9e1aca50a12f3def37ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "205c92c077fead6d6ef11a0f2b017d1b33c997ff54ffeb0e9e1aca50a12f3def37ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "74033064aa217cc572a55febe4f41ca649d6e9114336504403afb855d07e046b" + }, + { + "timelock": "2010e47e55f91b254c88df1cabb3aa1fc082892fbf6c55dbcb7a132a4f902a47a7ad02e803b2", + "unbond": "2010e47e55f91b254c88df1cabb3aa1fc082892fbf6c55dbcb7a132a4f902a47a7ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2010e47e55f91b254c88df1cabb3aa1fc082892fbf6c55dbcb7a132a4f902a47a7ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "9aeb6e1232b227872713306a76925e7fcc44f08e7cb27fe8f5e3ba9096556f68" + }, + { + "timelock": "2028ff0add7eab2e779c1ef9dc2553c37559b4fc7f746d1fb641e15f1125aea404ad02e803b2", + "unbond": "2028ff0add7eab2e779c1ef9dc2553c37559b4fc7f746d1fb641e15f1125aea404ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2028ff0add7eab2e779c1ef9dc2553c37559b4fc7f746d1fb641e15f1125aea404ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "0d2a34244379760fba4eec97b6e7eda1b9f6e6e61a32c484498b56654860e196" + }, + { + "timelock": "20f4155e5efd6dacabbf3d8a046fc12db3bae3b6ec0e92f91c6e38a9a9f79ba12cad02e803b2", + "unbond": "20f4155e5efd6dacabbf3d8a046fc12db3bae3b6ec0e92f91c6e38a9a9f79ba12cad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20f4155e5efd6dacabbf3d8a046fc12db3bae3b6ec0e92f91c6e38a9a9f79ba12cad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "46abeddfd47a942e214e345d31c6d301d9dd0bca98cf9882ea3fcc2d9515f23c" + }, + { + "timelock": "200d7aa0b6f2edad783937aa602c9f14ea7fc45ed8c1f68c530223ba77074705a5ad02e803b2", + "unbond": "200d7aa0b6f2edad783937aa602c9f14ea7fc45ed8c1f68c530223ba77074705a5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200d7aa0b6f2edad783937aa602c9f14ea7fc45ed8c1f68c530223ba77074705a5ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "d76f4b2e77dd81e336bf44d2477a1ed26b1382371b57c31fd70d7dbadc8726dc" + }, + { + "timelock": "206743aa184a5b5c8443fe9fb0a4403911712efb5694e807c59b31e19100770591ad02e803b2", + "unbond": "206743aa184a5b5c8443fe9fb0a4403911712efb5694e807c59b31e19100770591ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "206743aa184a5b5c8443fe9fb0a4403911712efb5694e807c59b31e19100770591ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "727e7fc99d217d51479fb8ede481031e2129f7daf77379f9b91bfdac15d423a6" + }, + { + "timelock": "2029b9404f75307c067270d1f3d9200cb6dfc5754814a2a168d212764984c5a3f3ad02e803b2", + "unbond": "2029b9404f75307c067270d1f3d9200cb6dfc5754814a2a168d212764984c5a3f3ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2029b9404f75307c067270d1f3d9200cb6dfc5754814a2a168d212764984c5a3f3ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "bc44c6d623f1be68406442ed9bd1d851fd42e11dc164b61db423155a93483856" + }, + { + "timelock": "2043e13092bccd4fcf1baa175c4392fe83e26e4e831d867e6ecaf3872843fa9effad02e803b2", + "unbond": "2043e13092bccd4fcf1baa175c4392fe83e26e4e831d867e6ecaf3872843fa9effad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2043e13092bccd4fcf1baa175c4392fe83e26e4e831d867e6ecaf3872843fa9effad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "a7243cef3f66a5d21740f9f208d174f0cfd677fc203f1b96060d62bd7e694c48" + }, + { + "timelock": "20cae6dae566624eef92152a2d3c3754e09fdbf5cded31efb9355dd18376c7e907ad02e803b2", + "unbond": "20cae6dae566624eef92152a2d3c3754e09fdbf5cded31efb9355dd18376c7e907ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20cae6dae566624eef92152a2d3c3754e09fdbf5cded31efb9355dd18376c7e907ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "84c92fdb0bb15ae24c6e84b40ac8f17abe9d2951cc2a3dbfc09658d2c37d9826" + }, + { + "timelock": "20ea24599975c726b4db869351bd413f4300bcea7e8006e74730e6ba17b516bbc0ad02e803b2", + "unbond": "20ea24599975c726b4db869351bd413f4300bcea7e8006e74730e6ba17b516bbc0ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ea24599975c726b4db869351bd413f4300bcea7e8006e74730e6ba17b516bbc0ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "1d07ea64db8a780a94ee0d3598462ac179349dc9fb7c68177f9d06987c319edf" + }, + { + "timelock": "206795d66b42f2c9e46158a90d7f33e1e3c13009d30c67da371041f252a796440ead02e803b2", + "unbond": "206795d66b42f2c9e46158a90d7f33e1e3c13009d30c67da371041f252a796440ead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "206795d66b42f2c9e46158a90d7f33e1e3c13009d30c67da371041f252a796440ead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b9f447e2534232ce777d87199ef32e26f2e7c7745faac986d5262b016ff23176" + }, + { + "timelock": "206f487d35f5fd28ba9959db650d8583846cccc795f59b79f233eb7fd36e2b3009ad02e803b2", + "unbond": "206f487d35f5fd28ba9959db650d8583846cccc795f59b79f233eb7fd36e2b3009ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "206f487d35f5fd28ba9959db650d8583846cccc795f59b79f233eb7fd36e2b3009ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "d8ec8cd4af186cc9c399ef834597ec002e3528894111902601e96606032449bc" + }, + { + "timelock": "208eedbf83f4bf8ba6adeb82868b5c76b48e98bec4c6770d3f76d7c68b729c5f04ad02e803b2", + "unbond": "208eedbf83f4bf8ba6adeb82868b5c76b48e98bec4c6770d3f76d7c68b729c5f04ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "208eedbf83f4bf8ba6adeb82868b5c76b48e98bec4c6770d3f76d7c68b729c5f04ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "416603edfd59abf4360ad14e3502773541e4161ee160e0c7d9015e8be53a22d8" + }, + { + "timelock": "20c83a76b4ef4f706d87905d38f7c390044be80404a19770f94acc8be4e29df7b4ad02e803b2", + "unbond": "20c83a76b4ef4f706d87905d38f7c390044be80404a19770f94acc8be4e29df7b4ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c83a76b4ef4f706d87905d38f7c390044be80404a19770f94acc8be4e29df7b4ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "694a9803da4741009384580e8e12f1ec209652ba030c70a859e768060a3c15d2" + }, + { + "timelock": "202ef0eed98f37e381327230b15401387b0f9d7bd412aeea7c39a45bb0d10d4d66ad02e803b2", + "unbond": "202ef0eed98f37e381327230b15401387b0f9d7bd412aeea7c39a45bb0d10d4d66ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "202ef0eed98f37e381327230b15401387b0f9d7bd412aeea7c39a45bb0d10d4d66ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "2e7367a5284dcd6d883914e0753ee04b885f137a231400c1d9679f1ff55691dc" + }, + { + "timelock": "20341aa635ad233ed00a025bf13f07e32e67f5bb87f716e1bb6af4f8854b9d00b3ad02e803b2", + "unbond": "20341aa635ad233ed00a025bf13f07e32e67f5bb87f716e1bb6af4f8854b9d00b3ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20341aa635ad233ed00a025bf13f07e32e67f5bb87f716e1bb6af4f8854b9d00b3ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "751dc74292eb7eb66308ea5ec023696226af4fca5ddcfb252bdf36690b5a0481" + }, + { + "timelock": "2046026a0dd1ba1969deb63ab168cc0a367760a2c87957942326f6816f2c4d441ead02e803b2", + "unbond": "2046026a0dd1ba1969deb63ab168cc0a367760a2c87957942326f6816f2c4d441ead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2046026a0dd1ba1969deb63ab168cc0a367760a2c87957942326f6816f2c4d441ead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "c4a58a8c7cf3a9dc40025a1658b3cde6026734331c0e4da76f42e93ecbd29049" + }, + { + "timelock": "201a70f30c3c96cf01145b3e292c7aaf7295f5276f15886bbbd6765da3753b91ebad02e803b2", + "unbond": "201a70f30c3c96cf01145b3e292c7aaf7295f5276f15886bbbd6765da3753b91ebad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "201a70f30c3c96cf01145b3e292c7aaf7295f5276f15886bbbd6765da3753b91ebad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5e5d536edc35df1d6041c20d0373fdb56f3b9f00c8b0b349bc914a7dcdf99b98" + }, + { + "timelock": "207e0baafad59419810bb9b36ca2c2c05d46750d14082229ad88dee603321ba362ad02e803b2", + "unbond": "207e0baafad59419810bb9b36ca2c2c05d46750d14082229ad88dee603321ba362ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "207e0baafad59419810bb9b36ca2c2c05d46750d14082229ad88dee603321ba362ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5c68141be3722b43a59b21f640b5f41c80df877b88b106f6ca9714b4b55c6b4f" + }, + { + "timelock": "201edb96768b6fd3ffc9999173021f3d0ee325b163387d145f167d67f8c78b5796ad02e803b2", + "unbond": "201edb96768b6fd3ffc9999173021f3d0ee325b163387d145f167d67f8c78b5796ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "201edb96768b6fd3ffc9999173021f3d0ee325b163387d145f167d67f8c78b5796ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5950054d2bdbfac845a967034cd6dd11cc27cd960ad4a88666402025757f191f" + }, + { + "timelock": "20d2c359a97c7c97087171de640ecf23a972588fec3a5dc96f91693a270e4fe283ad02e803b2", + "unbond": "20d2c359a97c7c97087171de640ecf23a972588fec3a5dc96f91693a270e4fe283ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20d2c359a97c7c97087171de640ecf23a972588fec3a5dc96f91693a270e4fe283ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "98ccea5e2e76ce34b1258234e3d498b6031189012a8cbb6740576e8835bc4ea7" + }, + { + "timelock": "206025af02f133d46d2a24b48a6c8629d314954af86e0a1745fb714518bc3d16cfad02e803b2", + "unbond": "206025af02f133d46d2a24b48a6c8629d314954af86e0a1745fb714518bc3d16cfad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "206025af02f133d46d2a24b48a6c8629d314954af86e0a1745fb714518bc3d16cfad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "912eb58313f73efe47047e845a6fc399aca28083c6cb7fe35354d61655e81015" + }, + { + "timelock": "20ba384ee73dbd536547ffb2014cddcb0b99dec2899ebecbd0ec3d810279777d01ad02e803b2", + "unbond": "20ba384ee73dbd536547ffb2014cddcb0b99dec2899ebecbd0ec3d810279777d01ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ba384ee73dbd536547ffb2014cddcb0b99dec2899ebecbd0ec3d810279777d01ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "304114c44f24035cc5d410e087958d6052effbd68541ec9e14c927ff6252396a" + }, + { + "timelock": "2082b427c82b033567bf583614df46204fc8278c19df948c0df20d99c2b1dcd96dad02e803b2", + "unbond": "2082b427c82b033567bf583614df46204fc8278c19df948c0df20d99c2b1dcd96dad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2082b427c82b033567bf583614df46204fc8278c19df948c0df20d99c2b1dcd96dad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5725fb86df1d7abdb5ea2fddda53b64731f2a5ea882398a58193399fea92f668" + }, + { + "timelock": "20bd807fca2f7a53f279c47b5a6703878698edeb3ed06c53e93ba5a1c3f4ac79f2ad02e803b2", + "unbond": "20bd807fca2f7a53f279c47b5a6703878698edeb3ed06c53e93ba5a1c3f4ac79f2ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20bd807fca2f7a53f279c47b5a6703878698edeb3ed06c53e93ba5a1c3f4ac79f2ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "6a13a484773a35e18d6417fb504f28a25c3c1251fdfdc947d1baa2b664c563d6" + }, + { + "timelock": "20d544ff2ec9a5a31bb3b4f7d2c39602108ac29490c0c1c0581b5430eb175dd415ad02e803b2", + "unbond": "20d544ff2ec9a5a31bb3b4f7d2c39602108ac29490c0c1c0581b5430eb175dd415ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20d544ff2ec9a5a31bb3b4f7d2c39602108ac29490c0c1c0581b5430eb175dd415ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "c4883eef52f04756b54c396ab47034a49934641465c9dbbda71ea73462f331c0" + }, + { + "timelock": "20182e7e50bb4c0ec1c8987d941f9f3abf3d21ddc895570d42c03ce3c48e0ed5e1ad02e803b2", + "unbond": "20182e7e50bb4c0ec1c8987d941f9f3abf3d21ddc895570d42c03ce3c48e0ed5e1ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20182e7e50bb4c0ec1c8987d941f9f3abf3d21ddc895570d42c03ce3c48e0ed5e1ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "549753118ac562c7792f65d9da0ac26e1164cb73b4560a3b8fbb5159b0b30121" + }, + { + "timelock": "201d67a16e967f3d68f61fe531f305df4ef3fcbaa31a905fc7969a5f88c162dc63ad02e803b2", + "unbond": "201d67a16e967f3d68f61fe531f305df4ef3fcbaa31a905fc7969a5f88c162dc63ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "201d67a16e967f3d68f61fe531f305df4ef3fcbaa31a905fc7969a5f88c162dc63ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b4cac5715f103bcf6d8256925a1217fb0d9d465b704b435163df0d6f7a3a611a" + }, + { + "timelock": "208449339b33f3c9866c55b2fa7b3ff864998fc2337f1ee94032118b0b187d4536ad02e803b2", + "unbond": "208449339b33f3c9866c55b2fa7b3ff864998fc2337f1ee94032118b0b187d4536ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "208449339b33f3c9866c55b2fa7b3ff864998fc2337f1ee94032118b0b187d4536ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "8c6a1b51e5e4eab5ac6034d99f5d56d3c6611508e84c01f91da00cca3116d42a" + }, + { + "timelock": "20c9475411e0b3330390d41506dda19ccd7702925081c72097ba60912bf26f85e6ad02e803b2", + "unbond": "20c9475411e0b3330390d41506dda19ccd7702925081c72097ba60912bf26f85e6ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c9475411e0b3330390d41506dda19ccd7702925081c72097ba60912bf26f85e6ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "e26e57e640d6fe0c96bdeb26076565c080248ea3bc30482b892d544235cd32d0" + }, + { + "timelock": "20822dd09895f723b5312224c77ac8e30883be9ae5249bbb4a70fecbf4ccfa0a71ad02e803b2", + "unbond": "20822dd09895f723b5312224c77ac8e30883be9ae5249bbb4a70fecbf4ccfa0a71ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20822dd09895f723b5312224c77ac8e30883be9ae5249bbb4a70fecbf4ccfa0a71ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "2da3a683eab38dc1e986891e19c434af8b329919e3dac51283a9d47e5eff4de2" + }, + { + "timelock": "20a92dd068dd1f91a10a35df4baa29ff61a40b138b5dbcb153ca8cad649e664686ad02e803b2", + "unbond": "20a92dd068dd1f91a10a35df4baa29ff61a40b138b5dbcb153ca8cad649e664686ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20a92dd068dd1f91a10a35df4baa29ff61a40b138b5dbcb153ca8cad649e664686ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b6f94b6960cdcd46c6ad4a24e46125f1e4499b45624c210d80119d61e88fa822" + }, + { + "timelock": "20f4562571f946d3c85f396e461802d062712c95416431f81356407d032da20332ad02e803b2", + "unbond": "20f4562571f946d3c85f396e461802d062712c95416431f81356407d032da20332ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20f4562571f946d3c85f396e461802d062712c95416431f81356407d032da20332ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "aee1a13a81927cf88a8942813bb6e3a8b9ae43d7d93173d5f4d61a83f35e466c" + }, + { + "timelock": "20e5dbd23459fd79ff78356042922b7511452dfae59212468b71a491498c0cd708ad02e803b2", + "unbond": "20e5dbd23459fd79ff78356042922b7511452dfae59212468b71a491498c0cd708ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20e5dbd23459fd79ff78356042922b7511452dfae59212468b71a491498c0cd708ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b2d4b96f75031d635813a2ec8fc871ca47bf1d1fe3890cbe04f6de42331c55ae" + }, + { + "timelock": "204c0b46f2dc46bd1ff04d98d96a3778ecde96cbc4312ef1f91873a90fbfea9e66ad02e803b2", + "unbond": "204c0b46f2dc46bd1ff04d98d96a3778ecde96cbc4312ef1f91873a90fbfea9e66ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "204c0b46f2dc46bd1ff04d98d96a3778ecde96cbc4312ef1f91873a90fbfea9e66ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "08dc413075fb9b98dc98e51a2123890668d953e41b3863b0fecd8fab1d652ce9" + }, + { + "timelock": "200ff99a01ce795cd27bc210d35286d45464edf4d61c6f7f640cc2e06f8420f0c2ad02e803b2", + "unbond": "200ff99a01ce795cd27bc210d35286d45464edf4d61c6f7f640cc2e06f8420f0c2ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200ff99a01ce795cd27bc210d35286d45464edf4d61c6f7f640cc2e06f8420f0c2ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "da0631fed323f79a150446974fdb5ba8a10ae7e901e26480bd9915996ee6db6d" + }, + { + "timelock": "2095af137b7036ad1720fca6a9061ae58f071992f59c9aec7c74591964ac5aea2fad02e803b2", + "unbond": "2095af137b7036ad1720fca6a9061ae58f071992f59c9aec7c74591964ac5aea2fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2095af137b7036ad1720fca6a9061ae58f071992f59c9aec7c74591964ac5aea2fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "a09dd506b287cef36a27b62e605ea68d8db7df68c948b775751567a92544e337" + }, + { + "timelock": "20a4193ca5a1d0a7facbaf9d8dbe23d46aa8a8b9f5dff0bfd1f6a3e2eab3cba5d1ad02e803b2", + "unbond": "20a4193ca5a1d0a7facbaf9d8dbe23d46aa8a8b9f5dff0bfd1f6a3e2eab3cba5d1ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20a4193ca5a1d0a7facbaf9d8dbe23d46aa8a8b9f5dff0bfd1f6a3e2eab3cba5d1ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "95d0b995c558e41e999818f0ebacf7204f5aa28146942c25f02c4e67e34d0b1e" + }, + { + "timelock": "20c7b7ed8c05693a7057bd7d86ae866cc7449dbd8a9949678c7f3ed8e2174b2b16ad02e803b2", + "unbond": "20c7b7ed8c05693a7057bd7d86ae866cc7449dbd8a9949678c7f3ed8e2174b2b16ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c7b7ed8c05693a7057bd7d86ae866cc7449dbd8a9949678c7f3ed8e2174b2b16ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "0ffdfae72eab9e36619feac1696bc0cf8c2f6db4f420f49c0a5d4e6da0836e8f" + }, + { + "timelock": "20eb088f08256453773028ea184446d2b16c3ae0b788045a19915a776465757aedad02e803b2", + "unbond": "20eb088f08256453773028ea184446d2b16c3ae0b788045a19915a776465757aedad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20eb088f08256453773028ea184446d2b16c3ae0b788045a19915a776465757aedad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "21f428703c6685d1161b7765db7695eb759bc170a55fef959dc56a5081f440ed" + }, + { + "timelock": "20ee875a0d9901fbd7a2af56ffa872911fbf22f75ba7638727f9e0a158913f17e1ad02e803b2", + "unbond": "20ee875a0d9901fbd7a2af56ffa872911fbf22f75ba7638727f9e0a158913f17e1ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ee875a0d9901fbd7a2af56ffa872911fbf22f75ba7638727f9e0a158913f17e1ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "534df4a7aa7c229241229d7b911ddc1628257451cdac1a2afeb080503fb7d94a" + }, + { + "timelock": "20849a374a587f92193f6ef88a6a99ceed99e2ee417bb2be6ebc3d8b411e4b12eaad02e803b2", + "unbond": "20849a374a587f92193f6ef88a6a99ceed99e2ee417bb2be6ebc3d8b411e4b12eaad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20849a374a587f92193f6ef88a6a99ceed99e2ee417bb2be6ebc3d8b411e4b12eaad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "0bf80d7cab1381fa7aef4aceb5861510e94dad54072816cb28f42cd9dde37728" + }, + { + "timelock": "20db319dbeee541226322e1d2904e3438e2ab660d82f1aa138e219096fa4867655ad02e803b2", + "unbond": "20db319dbeee541226322e1d2904e3438e2ab660d82f1aa138e219096fa4867655ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20db319dbeee541226322e1d2904e3438e2ab660d82f1aa138e219096fa4867655ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "a44f9a356f79d9663d184bfb2448fa7c748c1ca9fe400785da83872826bda7f9" + }, + { + "timelock": "20c76fa80c517a594bd4772ceb4bfe5ce529364244d313333d91f4527306ae94f4ad02e803b2", + "unbond": "20c76fa80c517a594bd4772ceb4bfe5ce529364244d313333d91f4527306ae94f4ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c76fa80c517a594bd4772ceb4bfe5ce529364244d313333d91f4527306ae94f4ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "17ba509be3a762bcd7c1830129f77f40437447cc759f5a9b528f92e447d0b3e0" + }, + { + "timelock": "20c5f05c1c87e5289e3e80085bcd25319eb5a55348039372934517ab1e7e0f2ac2ad02e803b2", + "unbond": "20c5f05c1c87e5289e3e80085bcd25319eb5a55348039372934517ab1e7e0f2ac2ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c5f05c1c87e5289e3e80085bcd25319eb5a55348039372934517ab1e7e0f2ac2ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b9a7eeecf6818d705ed8e4a94a235a8a88390813437e8cc4fa32369e6e8a6a8a" + }, + { + "timelock": "201be14a77fb2d6ad3e25551fd59010174a008d9f97939db408b6a0f40f31eb49fad02e803b2", + "unbond": "201be14a77fb2d6ad3e25551fd59010174a008d9f97939db408b6a0f40f31eb49fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "201be14a77fb2d6ad3e25551fd59010174a008d9f97939db408b6a0f40f31eb49fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b571b0be2b7129f757cae6f515b43ec3a7830324425ee2637bffed5370195264" + }, + { + "timelock": "20492829f913a2f7d6533aca4e512c5ed64f2a06d8d01a2a5268e36a90aa6f306ead02e803b2", + "unbond": "20492829f913a2f7d6533aca4e512c5ed64f2a06d8d01a2a5268e36a90aa6f306ead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20492829f913a2f7d6533aca4e512c5ed64f2a06d8d01a2a5268e36a90aa6f306ead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "150aac474ffd78471646f9a8c68fa7079a57bf1a4ef7758fe541108c680b4cc9" + }, + { + "timelock": "20b37582c6d2572294d0d0ef6571f238295848a495ffb71a30dd928383d736624ead02e803b2", + "unbond": "20b37582c6d2572294d0d0ef6571f238295848a495ffb71a30dd928383d736624ead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20b37582c6d2572294d0d0ef6571f238295848a495ffb71a30dd928383d736624ead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "4b2ebfc7347bb794dc1e1e38718bed5fa83fc6749355fbbfc64121abed48358e" + }, + { + "timelock": "2099e0589d02d3201f8a22eb7979585e7ea1218a44f6cf8ffa3addfc69f35116e0ad02e803b2", + "unbond": "2099e0589d02d3201f8a22eb7979585e7ea1218a44f6cf8ffa3addfc69f35116e0ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2099e0589d02d3201f8a22eb7979585e7ea1218a44f6cf8ffa3addfc69f35116e0ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "99a6dc36a82b0e849ddede3cf6389d617d89d4761cda82eb41d01b44114d3439" + }, + { + "timelock": "202d1e02a2097d3842d503d65992b77079356c0c503ecd5c4db27a2dbaa76dba95ad02e803b2", + "unbond": "202d1e02a2097d3842d503d65992b77079356c0c503ecd5c4db27a2dbaa76dba95ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "202d1e02a2097d3842d503d65992b77079356c0c503ecd5c4db27a2dbaa76dba95ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5181c3daecbf92261d47426b6e7ab0ae1e04f2913a6bb0d3c9c85450f63c4040" + }, + { + "timelock": "20792876213a5814c453280cda9b11e0a296e3c267fe300afff7694c2e88f4e25dad02e803b2", + "unbond": "20792876213a5814c453280cda9b11e0a296e3c267fe300afff7694c2e88f4e25dad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20792876213a5814c453280cda9b11e0a296e3c267fe300afff7694c2e88f4e25dad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "ba9cec13e07363c3b6da13c5227634d023fee0d7735b69360a36da8c255aa09f" + }, + { + "timelock": "209fb297561380f69edd242fe0010f74dc2e37446b340087d996e3b281a61e57d3ad02e803b2", + "unbond": "209fb297561380f69edd242fe0010f74dc2e37446b340087d996e3b281a61e57d3ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "209fb297561380f69edd242fe0010f74dc2e37446b340087d996e3b281a61e57d3ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "42523ba352f12c28fa6805afc5373a79a9a95f8f40baef85667c14a8ae145f75" + }, + { + "timelock": "2010a58de6bb0e40ab507aa6f42389ef93f9a9ff724f2a258ce03feee18c77b87fad02e803b2", + "unbond": "2010a58de6bb0e40ab507aa6f42389ef93f9a9ff724f2a258ce03feee18c77b87fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2010a58de6bb0e40ab507aa6f42389ef93f9a9ff724f2a258ce03feee18c77b87fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "84529decdd062b884f4abd56c5a3b0015b7ed905a2b66734d6ce00d69ee8ad1c" + }, + { + "timelock": "2064e5034beb7eacdc3eedc0cce2dd729a6c472c09708bd836bd6ae920e90320bcad02e803b2", + "unbond": "2064e5034beb7eacdc3eedc0cce2dd729a6c472c09708bd836bd6ae920e90320bcad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2064e5034beb7eacdc3eedc0cce2dd729a6c472c09708bd836bd6ae920e90320bcad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "fecb1240acba8bb080486a8ea976071c86486838d95ba39ae08d7476b332f867" + }, + { + "timelock": "205a649b806260c6e830296a33b19bd95d67032a62533d1cdd659c194d0814f9dbad02e803b2", + "unbond": "205a649b806260c6e830296a33b19bd95d67032a62533d1cdd659c194d0814f9dbad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "205a649b806260c6e830296a33b19bd95d67032a62533d1cdd659c194d0814f9dbad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "6294417a7c7cd166472f35f256908549f639f31ab9e10c3090c3d03cd6dd6ad0" + }, + { + "timelock": "20ba1a1c15c2d98ac68bddd390e3bb88c9857685e0522f41f1b2e93e2b27804284ad02e803b2", + "unbond": "20ba1a1c15c2d98ac68bddd390e3bb88c9857685e0522f41f1b2e93e2b27804284ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ba1a1c15c2d98ac68bddd390e3bb88c9857685e0522f41f1b2e93e2b27804284ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "cd6b669affca8716a5d7761c6759e08c845a08426a99861c5f8f96a9917afa43" + }, + { + "timelock": "2087ebb21e31bb16872ac5501fc953ad088f9b1335ea9d660e763c6814495882b0ad02e803b2", + "unbond": "2087ebb21e31bb16872ac5501fc953ad088f9b1335ea9d660e763c6814495882b0ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2087ebb21e31bb16872ac5501fc953ad088f9b1335ea9d660e763c6814495882b0ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "cf6cf5426a55f7258f3a3fbed046cf751e5c2b2208dd644ed81b9f0c212362e9" + }, + { + "timelock": "207d3a6f16875cccc9043874f7d4a49acc95e6836455d2f7e64f4ddab08130b99ead02e803b2", + "unbond": "207d3a6f16875cccc9043874f7d4a49acc95e6836455d2f7e64f4ddab08130b99ead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "207d3a6f16875cccc9043874f7d4a49acc95e6836455d2f7e64f4ddab08130b99ead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "cc9d9ace8ce2e0aad7b783e3f97e5355bfe17cdc02589a404bfe8c5d2d28cf83" + }, + { + "timelock": "20944a8d87ea60ca1fa09daa9144962f532774ddf89899d2c579d22374f24de042ad02e803b2", + "unbond": "20944a8d87ea60ca1fa09daa9144962f532774ddf89899d2c579d22374f24de042ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20944a8d87ea60ca1fa09daa9144962f532774ddf89899d2c579d22374f24de042ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "e7e92e0d4a60db9a399442044aa80249312fed480b7cd9b26b365f08ed383e40" + }, + { + "timelock": "2088b72c0ad71ff42807c0d724002eb24c1fe306c24a31b32733fa5a22665f2060ad02e803b2", + "unbond": "2088b72c0ad71ff42807c0d724002eb24c1fe306c24a31b32733fa5a22665f2060ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2088b72c0ad71ff42807c0d724002eb24c1fe306c24a31b32733fa5a22665f2060ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "242ff3f67dd3465013fb05aaafca1b591b1bf9d12db12d0f86818055dd8662e3" + }, + { + "timelock": "2008fbdc74407489c3c5df5b53215dd3f819ba517b2d306fd6aa583a3e60788194ad02e803b2", + "unbond": "2008fbdc74407489c3c5df5b53215dd3f819ba517b2d306fd6aa583a3e60788194ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2008fbdc74407489c3c5df5b53215dd3f819ba517b2d306fd6aa583a3e60788194ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "5657ad0e65d211a3e56d413fa4f7f2bdb1bf74735e41a7c6ab35296a5b0b2328" + }, + { + "timelock": "205cd53ee7b240d245d40915100d32323039f208fcbff4ed37726ccbb28af0bde8ad02e803b2", + "unbond": "205cd53ee7b240d245d40915100d32323039f208fcbff4ed37726ccbb28af0bde8ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "205cd53ee7b240d245d40915100d32323039f208fcbff4ed37726ccbb28af0bde8ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "935dddf8786d8701abd1058dd2978a0ba0d70a3c6c9d54e0b3d00c5ef81fdf07" + }, + { + "timelock": "2064b0dfe3626671bc39ac2f6a366cc33c0eb7b2b3df17470187d5fa0b033b6b0fad02e803b2", + "unbond": "2064b0dfe3626671bc39ac2f6a366cc33c0eb7b2b3df17470187d5fa0b033b6b0fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2064b0dfe3626671bc39ac2f6a366cc33c0eb7b2b3df17470187d5fa0b033b6b0fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "85e991858e8cb1a5a051e4b76d429d746e4bcb1df02eccf32825dd4ada5b6de1" + }, + { + "timelock": "200b2874cfe4a613191a652940300042cdc43c6e71d5c82031878296d5e8a9b139ad02e803b2", + "unbond": "200b2874cfe4a613191a652940300042cdc43c6e71d5c82031878296d5e8a9b139ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200b2874cfe4a613191a652940300042cdc43c6e71d5c82031878296d5e8a9b139ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "f3a415527d0a20b9b3b9955ea463a1ce74b1ab7a7035499a84d123cafb1d1075" + }, + { + "timelock": "20e81505ffa5ff2e6f4c63c561576f6a9d033713bf72a51e7546ca73c9a158d3e4ad02e803b2", + "unbond": "20e81505ffa5ff2e6f4c63c561576f6a9d033713bf72a51e7546ca73c9a158d3e4ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20e81505ffa5ff2e6f4c63c561576f6a9d033713bf72a51e7546ca73c9a158d3e4ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "02a50b1ac1ba93cefefb9e19b4542574f54334eed443495d1008835f9f9e8ef8" + }, + { + "timelock": "204c13540405a69f47e9afcb8393db7a9c25163e3fd210dcd4037e8af8d6081926ad02e803b2", + "unbond": "204c13540405a69f47e9afcb8393db7a9c25163e3fd210dcd4037e8af8d6081926ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "204c13540405a69f47e9afcb8393db7a9c25163e3fd210dcd4037e8af8d6081926ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "6e8793882a1a66f0112995f4b748556e2a8cc2fa1d0f7d320b5df16d4406d2ee" + }, + { + "timelock": "204e4ab90943a4f25dff9ec0cc26d6b83ccec0d29eb9b0c7f8a8aed88cb1220f01ad02e803b2", + "unbond": "204e4ab90943a4f25dff9ec0cc26d6b83ccec0d29eb9b0c7f8a8aed88cb1220f01ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "204e4ab90943a4f25dff9ec0cc26d6b83ccec0d29eb9b0c7f8a8aed88cb1220f01ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "3b6adc645809d461693d23b32e1f025d48389fab1749d54253426414b5cedd28" + }, + { + "timelock": "20d86168fc3ae1b8fde9d4311a693cdfed221f63f681a6953065b38a7785c25a23ad02e803b2", + "unbond": "20d86168fc3ae1b8fde9d4311a693cdfed221f63f681a6953065b38a7785c25a23ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20d86168fc3ae1b8fde9d4311a693cdfed221f63f681a6953065b38a7785c25a23ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "cd1aa6e3e191d3023c39ff0953ccfb304702b6b29d124fd324c602637f34fe83" + }, + { + "timelock": "20744b20d7813cdb1597a6796db452c890c6918e8fd27c92a5eeaabd37fd0bbf2dad02e803b2", + "unbond": "20744b20d7813cdb1597a6796db452c890c6918e8fd27c92a5eeaabd37fd0bbf2dad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20744b20d7813cdb1597a6796db452c890c6918e8fd27c92a5eeaabd37fd0bbf2dad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "d1d9823a32c8c8abc751ed17345000a2ba3b50c1a2b1222c9708b97a574d3b28" + }, + { + "timelock": "20a3ce645632976176d258aa8c9b689feacf6f5d4c9026e3819b6309b12ad074a9ad02e803b2", + "unbond": "20a3ce645632976176d258aa8c9b689feacf6f5d4c9026e3819b6309b12ad074a9ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20a3ce645632976176d258aa8c9b689feacf6f5d4c9026e3819b6309b12ad074a9ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "eb0b3d77b328413a9eddaac177eeced2b001dcd7a0dc6ba653be7a01b35619da" + }, + { + "timelock": "20d4052afc0074b7c8badfed57d7e8e059315514eaaa189a07d59e38acb2c6eacfad02e803b2", + "unbond": "20d4052afc0074b7c8badfed57d7e8e059315514eaaa189a07d59e38acb2c6eacfad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20d4052afc0074b7c8badfed57d7e8e059315514eaaa189a07d59e38acb2c6eacfad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "421b242a686942d1129b7cba7b6ce1f1c5d27562a8635ec7c67bee9fff7e5b07" + }, + { + "timelock": "2022c7652dc8cea2bce20db60e29cd7e57f7444c82456eb0a15eee53174e502c7bad02e803b2", + "unbond": "2022c7652dc8cea2bce20db60e29cd7e57f7444c82456eb0a15eee53174e502c7bad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2022c7652dc8cea2bce20db60e29cd7e57f7444c82456eb0a15eee53174e502c7bad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "52d37d201884823dcb66477d4a76a91479907471b3771df9b8b13126e9fc6ec8" + }, + { + "timelock": "203b02158b4453adf1f39589b873bce873e11c4e1439d246847d25611acbce63e0ad02e803b2", + "unbond": "203b02158b4453adf1f39589b873bce873e11c4e1439d246847d25611acbce63e0ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "203b02158b4453adf1f39589b873bce873e11c4e1439d246847d25611acbce63e0ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "a77cb217817b3a27c90b420a0c0e2b2e7e62b65f18c87c9e11b86f8065931abd" + }, + { + "timelock": "20486a1a18aedb35b15f10cff4da4890979748944aad337dc328d497296cc1c4d6ad02e803b2", + "unbond": "20486a1a18aedb35b15f10cff4da4890979748944aad337dc328d497296cc1c4d6ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20486a1a18aedb35b15f10cff4da4890979748944aad337dc328d497296cc1c4d6ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "805b33fef92eb1b81bda0cf7bef159a8ab313502e8653e9f829de51438239c0a" + }, + { + "timelock": "20c8b70664224d3839c9302c9bfa86ef0d5b040901f3b55e6af6d66d496dda9509ad02e803b2", + "unbond": "20c8b70664224d3839c9302c9bfa86ef0d5b040901f3b55e6af6d66d496dda9509ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20c8b70664224d3839c9302c9bfa86ef0d5b040901f3b55e6af6d66d496dda9509ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "3d5a0e9ff97d70ff3f2dfa045f85a20ce26a982946f4c7d8d2b7dbdf4def5f8f" + }, + { + "timelock": "20f53677b2abdb111cd922cd47a692440e969e2a45de60be0d8d877142d7ccc0dfad02e803b2", + "unbond": "20f53677b2abdb111cd922cd47a692440e969e2a45de60be0d8d877142d7ccc0dfad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20f53677b2abdb111cd922cd47a692440e969e2a45de60be0d8d877142d7ccc0dfad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "9b8ed41999a9b73e54288c336ce6d1f00fe66d58b3cf2bccf9a2df2b64b17434" + }, + { + "timelock": "20b14fc7eca519812bfe50a5fe2a048072d87fc53b81971bc8f126962afa28d92fad02e803b2", + "unbond": "20b14fc7eca519812bfe50a5fe2a048072d87fc53b81971bc8f126962afa28d92fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20b14fc7eca519812bfe50a5fe2a048072d87fc53b81971bc8f126962afa28d92fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "c9800bd59f0573cc461358f78257d44bf42d84db955ae705302a2e6c182e3b04" + }, + { + "timelock": "20f17518b03760fe452b7ec720df35183dbafa040dc8fcc35b33a08f285a0059abad02e803b2", + "unbond": "20f17518b03760fe452b7ec720df35183dbafa040dc8fcc35b33a08f285a0059abad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20f17518b03760fe452b7ec720df35183dbafa040dc8fcc35b33a08f285a0059abad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b9b367cd58b3ca643e90f7e0fcd13d96fd9b7915ef47f864e0cbb66072d80298" + }, + { + "timelock": "205483f9994c13872bf0dc143f29b9fc1bbcc9322ea1e4d92b2fadb9211a87fa56ad02e803b2", + "unbond": "205483f9994c13872bf0dc143f29b9fc1bbcc9322ea1e4d92b2fadb9211a87fa56ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "205483f9994c13872bf0dc143f29b9fc1bbcc9322ea1e4d92b2fadb9211a87fa56ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "3fd1d2f564cd806bf61dfa4655e30b47c4025dfcc676e7c0d54f9076b96f9a8e" + }, + { + "timelock": "2052a0a6b0cf1e0d32aba9af2ab82cdae1922c4e761e2d7e78d9bfb898f5ed0258ad02e803b2", + "unbond": "2052a0a6b0cf1e0d32aba9af2ab82cdae1922c4e761e2d7e78d9bfb898f5ed0258ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2052a0a6b0cf1e0d32aba9af2ab82cdae1922c4e761e2d7e78d9bfb898f5ed0258ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "1dcc8ee68ba3b1203cefcb6d94dece16568f17ac959cd4ccf8fe369ba4fb9b2d" + }, + { + "timelock": "20677a553654f6556d8c4e93e282d83e4f1ea8116925df7c22e29d174d2cac5256ad02e803b2", + "unbond": "20677a553654f6556d8c4e93e282d83e4f1ea8116925df7c22e29d174d2cac5256ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20677a553654f6556d8c4e93e282d83e4f1ea8116925df7c22e29d174d2cac5256ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "66de5cf0a58b21d00145e883a855e948494d7ae89e73ef6fd0b68ea6ca93721c" + }, + { + "timelock": "207efca69268c86a7a04d8ee6782b9f7b3a701d854283103a0f01d656e76aec1c9ad02e803b2", + "unbond": "207efca69268c86a7a04d8ee6782b9f7b3a701d854283103a0f01d656e76aec1c9ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "207efca69268c86a7a04d8ee6782b9f7b3a701d854283103a0f01d656e76aec1c9ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "61b22e12f123e9a4de298b8379ddcdf2d2bf37732128caa794efa2ad25b629ee" + }, + { + "timelock": "2012864ed4041fa22d8f360c8f142b0563439fe3279d7829505ea3d94ea76f9617ad02e803b2", + "unbond": "2012864ed4041fa22d8f360c8f142b0563439fe3279d7829505ea3d94ea76f9617ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2012864ed4041fa22d8f360c8f142b0563439fe3279d7829505ea3d94ea76f9617ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b17bee0d25b5d3610b389e416ea9b916bf2a25e409cfa8ff749fe2993f3be1e8" + }, + { + "timelock": "204976f8fb9aa3dcdb6cbeabe1148ddfec6dcc88e28dec97565fe265f30cc5b983ad02e803b2", + "unbond": "204976f8fb9aa3dcdb6cbeabe1148ddfec6dcc88e28dec97565fe265f30cc5b983ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "204976f8fb9aa3dcdb6cbeabe1148ddfec6dcc88e28dec97565fe265f30cc5b983ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "30fad5d1ac265e1993c7b340619d27f2343e712c6b1ce46dd6de9be55e1fa799" + }, + { + "timelock": "20b00fdbe77efd81005f1792e0f15dd9df59d99c2fc8d2e85721b0cb71123d5cedad02e803b2", + "unbond": "20b00fdbe77efd81005f1792e0f15dd9df59d99c2fc8d2e85721b0cb71123d5cedad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20b00fdbe77efd81005f1792e0f15dd9df59d99c2fc8d2e85721b0cb71123d5cedad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "b50a97970ec1c1bc04c8c9b42a818ddde62f33cb6e88c288f0a8a0f7d118a1a2" + }, + { + "timelock": "20406edf60fa07480479a245fbfbb76dd6ca4d38f68fce634e63f4210a759be27fad02e803b2", + "unbond": "20406edf60fa07480479a245fbfbb76dd6ca4d38f68fce634e63f4210a759be27fad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20406edf60fa07480479a245fbfbb76dd6ca4d38f68fce634e63f4210a759be27fad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "2b21df7b937bc4cee6e7f53c89581859fde4a47e0b16a2e9aa6a60e8404b6666" + }, + { + "timelock": "20b8a47b9c4cb7714bc414fccc52696ea6c32cf33c8369c077b74ba6a4af7f1426ad02e803b2", + "unbond": "20b8a47b9c4cb7714bc414fccc52696ea6c32cf33c8369c077b74ba6a4af7f1426ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20b8a47b9c4cb7714bc414fccc52696ea6c32cf33c8369c077b74ba6a4af7f1426ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "922f1fb6d545f29409119d017398a92a1634fd02f8ea52d1c9d42500b868d387" + }, + { + "timelock": "20ba8f4f5e67a07d31d071d5f8cf1ccdc786569e11df9b3da4ec15c9602ede3cb0ad02e803b2", + "unbond": "20ba8f4f5e67a07d31d071d5f8cf1ccdc786569e11df9b3da4ec15c9602ede3cb0ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20ba8f4f5e67a07d31d071d5f8cf1ccdc786569e11df9b3da4ec15c9602ede3cb0ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "db8eeef4e4734f43cf3c7e4d15954d7df86456534e8045b334a1d4f06c620e44" + }, + { + "timelock": "205d246bcd7958db5f25af672367a1c26cb1416d52280f0df5413cf794e23fb8d3ad02e803b2", + "unbond": "205d246bcd7958db5f25af672367a1c26cb1416d52280f0df5413cf794e23fb8d3ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "205d246bcd7958db5f25af672367a1c26cb1416d52280f0df5413cf794e23fb8d3ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "386f339f381d4b7b50a2f0d5d45ddd1ea71f5a2d9b2dfa5b83bfcf9d48ae50a7" + }, + { + "timelock": "20d6dc28290aceb39b73c3757e7851fc7fac805afaba8621bec516a1d5777d2c93ad02e803b2", + "unbond": "20d6dc28290aceb39b73c3757e7851fc7fac805afaba8621bec516a1d5777d2c93ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20d6dc28290aceb39b73c3757e7851fc7fac805afaba8621bec516a1d5777d2c93ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "002721fb5c2e5024eebf30a8274b4709f5895b79c9a5458d0c846cc817a2c784" + }, + { + "timelock": "20312020a8ec1238ff8723970899ceea440af08b0de66999c5d5382011ab6cffa8ad02e803b2", + "unbond": "20312020a8ec1238ff8723970899ceea440af08b0de66999c5d5382011ab6cffa8ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20312020a8ec1238ff8723970899ceea440af08b0de66999c5d5382011ab6cffa8ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "1683cc9be899b4a92374d02d4f50cdee7c2046081bdd47a4752c9d47aa083238" + }, + { + "timelock": "20707a8ea294045d6d70d9d7481c47b644c9814d2b8a84f3c2c6110a154b52b2dead02e803b2", + "unbond": "20707a8ea294045d6d70d9d7481c47b644c9814d2b8a84f3c2c6110a154b52b2dead204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20707a8ea294045d6d70d9d7481c47b644c9814d2b8a84f3c2c6110a154b52b2dead2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "8600c43ee66851a8467b307d5b36dcf5ef164aa7558c9bddaa2f8aef0f4dedbe" + }, + { + "timelock": "200440239c52b1c1a014644a3751ac98ed7585fbefcf8404239074a621864cb471ad02e803b2", + "unbond": "200440239c52b1c1a014644a3751ac98ed7585fbefcf8404239074a621864cb471ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "200440239c52b1c1a014644a3751ac98ed7585fbefcf8404239074a621864cb471ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "43825f43fa778cdf45b13e98e2fb664c944b98a5d731b142f9aff2fa5972527c" + }, + { + "timelock": "2057efa3c68cfb5575fa96acd0265673c620eae612c88ca82eef827852f04a7160ad02e803b2", + "unbond": "2057efa3c68cfb5575fa96acd0265673c620eae612c88ca82eef827852f04a7160ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "2057efa3c68cfb5575fa96acd0265673c620eae612c88ca82eef827852f04a7160ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "d0225504e7ae838930a15189af74156b28c97b27a8bc2c02e12e8bb02b50e542" + }, + { + "timelock": "201718f5ebb5bde4c7b315a0c389aeb996d2fed6bd6ee19a784a57e138ca9ac88dad02e803b2", + "unbond": "201718f5ebb5bde4c7b315a0c389aeb996d2fed6bd6ee19a784a57e138ca9ac88dad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "201718f5ebb5bde4c7b315a0c389aeb996d2fed6bd6ee19a784a57e138ca9ac88dad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "0a00d868385e99d9ce5f85c9ac64ec02929c5bb63edda9f52a35b38599cbc417" + }, + { + "timelock": "20a16a72c01c54535a8ebbe5b4398435595c40e66ad89f09388d38e15da2e7df49ad02e803b2", + "unbond": "20a16a72c01c54535a8ebbe5b4398435595c40e66ad89f09388d38e15da2e7df49ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "slash": "20a16a72c01c54535a8ebbe5b4398435595c40e66ad89f09388d38e15da2e7df49ad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "root_hash": "4aaa52374ea1f75b86b93bbff2df68ff362f50fc2869957eab6f2ac3201eb13d" + } +] diff --git a/rust/chains/tw_bitcoin/tests/data/babylon_staking_transactions.json b/rust/chains/tw_bitcoin/tests/data/babylon_staking_transactions.json new file mode 100644 index 00000000000..eab9db9f383 --- /dev/null +++ b/rust/chains/tw_bitcoin/tests/data/babylon_staking_transactions.json @@ -0,0 +1,213 @@ +[ + { + "name": "1 finality key, 1 covenant key, 1 staker key with op_return", + "parameters": { + "covenant_public_keys": [ + "024852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30fa" + ], + "covenant_quorum": 1, + "finality_provider_public_keys": [ + "0246542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5" + ], + "staker_public_key": "024000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafd", + "staking_time": 1000, + "staking_value": 100000, + "staking_tx_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "staking_output_index": 0, + "unbonding_tx_version": 2, + "unbonding_time": 100, + "unbonding_fee": 2000, + "tag": "01020304", + "network": "mainnet" + }, + "expected": { + "staking_output_pkscript_hex": "5120b2b169d39fb8ea9828f6ed8dbbeaa12594706d03bfd1638a912608a085fdd7a5", + "staking_output_value": 100000, + "staking_transaction_timelock_script_hex": "204000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafdad02e803b2", + "staking_transaction_unbonding_script_hex": "204000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafdad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "staking_transaction_slashing_script_hex": "204000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafdad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "unbonding_transaction_hex": "020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff01d07e01000000000022512071f38cba0e3c0453eb34ac8f9a60805965e9500a9ab69724f636a00fcafc252f00000000", + "unbonding_transaction_time_lock_script_hex": "204000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafdad0164b2", + "unbonding_transaction_slashing_script_hex": "204000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafdad2046542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e5ad204852a5d3e79cfdee1bda4ea729e879c1db0f19230eadd3cb96d3cb1efa8d30faac", + "op_return_script_hex": "6a4701020304004000bd2c8b975d351c5f3a42618aca31e07e2b253fcd571e9630540a3cb6eafd46542ccdcbde8a8c147c8d00f14d47f0d5c13c684d27497fc4610c7a4def15e503e8" + } + }, + { + "name": "1 finality key, 3/5 covenant committe, 1 staker key with op_return", + "parameters": { + "covenant_public_keys": [ + "02cc5c77da065c490a320834fdcf2c3da70ecd442054c90f874a1edb4669607b83", + "022f57b6d267043beda2deebab1187a67316121f1eac24047c16d1209c5e6cd0a5", + "03784bdab9a1c71ea51fa5904227e721681453e59d20af3d97485e79e5518bdfff", + "03f31cf8fb9ee1d9157e172d65b1bd00a0b54cee3906df13ced709bfd8d407e9a2", + "035c3e1bb7c6b475a9caeebcb09fab74546a9b8a364dd0ccede7e5aea5c5b12ed0" + ], + "covenant_quorum": 3, + "finality_provider_public_keys": [ + "03b5e37d93a8d04daee62838ac680ead407d7cbe0c858525781f8834495be0bce7" + ], + "staker_public_key": "03cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fe", + "staking_time": 10000, + "staking_value": 1000000, + "staking_tx_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "staking_output_index": 0, + "unbonding_tx_version": 2, + "unbonding_time": 50, + "unbonding_fee": 20000, + "tag": "01020304", + "network": "mainnet" + }, + "expected": { + "staking_output_pkscript_hex": "5120bbc6be1fff9cb4d93cb638f0fc9e5df7036c137b89c8baa762fb8637acdf3da7", + "staking_output_value": 1000000, + "staking_transaction_timelock_script_hex": "20cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fead021027b2", + "staking_transaction_unbonding_script_hex": "20cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fead202f57b6d267043beda2deebab1187a67316121f1eac24047c16d1209c5e6cd0a5ac205c3e1bb7c6b475a9caeebcb09fab74546a9b8a364dd0ccede7e5aea5c5b12ed0ba20784bdab9a1c71ea51fa5904227e721681453e59d20af3d97485e79e5518bdfffba20cc5c77da065c490a320834fdcf2c3da70ecd442054c90f874a1edb4669607b83ba20f31cf8fb9ee1d9157e172d65b1bd00a0b54cee3906df13ced709bfd8d407e9a2ba539c", + "staking_transaction_slashing_script_hex": "20cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fead20b5e37d93a8d04daee62838ac680ead407d7cbe0c858525781f8834495be0bce7ad202f57b6d267043beda2deebab1187a67316121f1eac24047c16d1209c5e6cd0a5ac205c3e1bb7c6b475a9caeebcb09fab74546a9b8a364dd0ccede7e5aea5c5b12ed0ba20784bdab9a1c71ea51fa5904227e721681453e59d20af3d97485e79e5518bdfffba20cc5c77da065c490a320834fdcf2c3da70ecd442054c90f874a1edb4669607b83ba20f31cf8fb9ee1d9157e172d65b1bd00a0b54cee3906df13ced709bfd8d407e9a2ba539c", + "unbonding_transaction_hex": "020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0120f40e0000000000225120d4ea23776e49e47df891d52a42a2ec389786aa3bdafa874b5e69df13070fb4bf00000000", + "unbonding_transaction_time_lock_script_hex": "20cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fead0132b2", + "unbonding_transaction_slashing_script_hex": "20cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257fead20b5e37d93a8d04daee62838ac680ead407d7cbe0c858525781f8834495be0bce7ad202f57b6d267043beda2deebab1187a67316121f1eac24047c16d1209c5e6cd0a5ac205c3e1bb7c6b475a9caeebcb09fab74546a9b8a364dd0ccede7e5aea5c5b12ed0ba20784bdab9a1c71ea51fa5904227e721681453e59d20af3d97485e79e5518bdfffba20cc5c77da065c490a320834fdcf2c3da70ecd442054c90f874a1edb4669607b83ba20f31cf8fb9ee1d9157e172d65b1bd00a0b54cee3906df13ced709bfd8d407e9a2ba539c", + "op_return_script_hex": "6a470102030400cc327c2a1ee1a70d7033bee61ed2e3e6ffc7c5f5d6c637c4f0d2ddfae8a257feb5e37d93a8d04daee62838ac680ead407d7cbe0c858525781f8834495be0bce72710" + } + }, + { + "name": "3 finality keys, 3/5 covenant committe, 1 staker key with no op_return", + "parameters": { + "covenant_public_keys": [ + "02042916c9cd52cfa146118c37b6b118f082bb50fb91da1c51b76dfc2100e66f00", + "03a2bfd2843357efda60fd26238b6affe6cbafa65d1f3cfbfb63ae3841d35887e6", + "035df1524dfc57fc1b0288ebac9b7abbcf5c61cfbfb8e4667ae0009445b04ecf22", + "03652ea9ac1c526218691e6b94769beaebbaedda080c5f1f036c239d5af69088b6", + "02dca4c9e5848db62e0f62e2c1cafca10f4464c10ae00bf579a8f7b5d81cd697f6" + ], + "covenant_quorum": 3, + "finality_provider_public_keys": [ + "0275e96ca4067b937f8e93bcb962523a1713c25d2af1a26f61034dd81f4e4687fe", + "0396c5c62af047a0839f8ea2b230315db93af25fa7074c71a2b95deeb47e507a63", + "034d4fb121fbe9f13ed7b55440fb9f3ad0f8bce8bd92589423b5f90d871db5142d" + ], + "staker_public_key": "037b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29", + "staking_time": 10000, + "staking_value": 1000000, + "staking_tx_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "staking_output_index": 0, + "unbonding_tx_version": 2, + "unbonding_time": 50, + "unbonding_fee": 20000, + "tag": "01020304", + "network": "mainnet" + }, + "expected": { + "staking_output_pkscript_hex": "51206136db5cccc1b9fca70164b3445020d6f6f8dd1428e78cef9111b295bd4eec89", + "staking_output_value": 1000000, + "staking_transaction_timelock_script_hex": "207b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29ad021027b2", + "staking_transaction_unbonding_script_hex": "207b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29ad20042916c9cd52cfa146118c37b6b118f082bb50fb91da1c51b76dfc2100e66f00ac205df1524dfc57fc1b0288ebac9b7abbcf5c61cfbfb8e4667ae0009445b04ecf22ba20652ea9ac1c526218691e6b94769beaebbaedda080c5f1f036c239d5af69088b6ba20a2bfd2843357efda60fd26238b6affe6cbafa65d1f3cfbfb63ae3841d35887e6ba20dca4c9e5848db62e0f62e2c1cafca10f4464c10ae00bf579a8f7b5d81cd697f6ba539c", + "staking_transaction_slashing_script_hex": "207b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29ad204d4fb121fbe9f13ed7b55440fb9f3ad0f8bce8bd92589423b5f90d871db5142dac2075e96ca4067b937f8e93bcb962523a1713c25d2af1a26f61034dd81f4e4687feba2096c5c62af047a0839f8ea2b230315db93af25fa7074c71a2b95deeb47e507a63ba519d20042916c9cd52cfa146118c37b6b118f082bb50fb91da1c51b76dfc2100e66f00ac205df1524dfc57fc1b0288ebac9b7abbcf5c61cfbfb8e4667ae0009445b04ecf22ba20652ea9ac1c526218691e6b94769beaebbaedda080c5f1f036c239d5af69088b6ba20a2bfd2843357efda60fd26238b6affe6cbafa65d1f3cfbfb63ae3841d35887e6ba20dca4c9e5848db62e0f62e2c1cafca10f4464c10ae00bf579a8f7b5d81cd697f6ba539c", + "unbonding_transaction_hex": "020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0120f40e0000000000225120a52e08ebd0db898e6a52870ca3e72a78391b799eac97a1675eccd416adace90e00000000", + "unbonding_transaction_time_lock_script_hex": "207b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29ad0132b2", + "unbonding_transaction_slashing_script_hex": "207b251c30f5bd7a29f3b1749d599c60f2bcdc11553e500a6c32e4075b80d5bd29ad204d4fb121fbe9f13ed7b55440fb9f3ad0f8bce8bd92589423b5f90d871db5142dac2075e96ca4067b937f8e93bcb962523a1713c25d2af1a26f61034dd81f4e4687feba2096c5c62af047a0839f8ea2b230315db93af25fa7074c71a2b95deeb47e507a63ba519d20042916c9cd52cfa146118c37b6b118f082bb50fb91da1c51b76dfc2100e66f00ac205df1524dfc57fc1b0288ebac9b7abbcf5c61cfbfb8e4667ae0009445b04ecf22ba20652ea9ac1c526218691e6b94769beaebbaedda080c5f1f036c239d5af69088b6ba20a2bfd2843357efda60fd26238b6affe6cbafa65d1f3cfbfb63ae3841d35887e6ba20dca4c9e5848db62e0f62e2c1cafca10f4464c10ae00bf579a8f7b5d81cd697f6ba539c", + "op_return_script_hex": "" + } + }, + { + "name": "1 finality keys, 7/9 covenant committe, 1 staker key with op_return", + "parameters": { + "covenant_public_keys": [ + "0287ed5bb2d036baf209eb49520327f6bd05285dabd30c97f239c3a69ff419950b", + "02ce45e9c7828b2f1b52f29076b0fbbac380db5e6a8318cb7718c99e0b3fe6228f", + "0354967fa36311ba41a19a725925a68053579122e7e833c5b76a7dde99eec8fb31", + "02cdf01d771c38b4ee3363daf4d33c25796adbade4da3f621e954fd1d2e40d7c1f", + "038a7f080c5629980a5f3ccbfb1ea8a1bc20e7ce0dccbfefe3f35620e69c589e63", + "03b2729d612586227170a2116d9f97e2728f7cdc5f1c2e8eb4c4acd35a143917fd", + "0357925d2886601e7d9cecc60352cf435111f429b3fc97be8704dbf1f0fe491089", + "0369769314404fb9c68cd5c77adb2a5837372c4d5e5ef5d564e8de98f886a1a13e", + "02792a29aeaaaa1bc69ed57612ef050b29345e6f987951b2e22fcb00195b3ae2d5" + ], + "covenant_quorum": 7, + "finality_provider_public_keys": [ + "03bc9d21a6400b831e1741056dd45aaeecd42f23fafdf3ea49b815e49ebb4f95fc" + ], + "staker_public_key": "03a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0", + "staking_time": 20000, + "staking_value": 10000000, + "staking_tx_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "staking_output_index": 0, + "unbonding_tx_version": 2, + "unbonding_time": 201, + "unbonding_fee": 50000, + "tag": "01020304", + "network": "mainnet" + }, + "expected": { + "staking_output_pkscript_hex": "51208b763bd20b8bca50c7bc059e3c7810471bfe30111f546bbbf4d879e437dd6ae2", + "staking_output_value": 10000000, + "staking_transaction_timelock_script_hex": "20a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0ad02204eb2", + "staking_transaction_unbonding_script_hex": "20a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0ad2054967fa36311ba41a19a725925a68053579122e7e833c5b76a7dde99eec8fb31ac2057925d2886601e7d9cecc60352cf435111f429b3fc97be8704dbf1f0fe491089ba2069769314404fb9c68cd5c77adb2a5837372c4d5e5ef5d564e8de98f886a1a13eba20792a29aeaaaa1bc69ed57612ef050b29345e6f987951b2e22fcb00195b3ae2d5ba2087ed5bb2d036baf209eb49520327f6bd05285dabd30c97f239c3a69ff419950bba208a7f080c5629980a5f3ccbfb1ea8a1bc20e7ce0dccbfefe3f35620e69c589e63ba20b2729d612586227170a2116d9f97e2728f7cdc5f1c2e8eb4c4acd35a143917fdba20cdf01d771c38b4ee3363daf4d33c25796adbade4da3f621e954fd1d2e40d7c1fba20ce45e9c7828b2f1b52f29076b0fbbac380db5e6a8318cb7718c99e0b3fe6228fba579c", + "staking_transaction_slashing_script_hex": "20a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0ad20bc9d21a6400b831e1741056dd45aaeecd42f23fafdf3ea49b815e49ebb4f95fcad2054967fa36311ba41a19a725925a68053579122e7e833c5b76a7dde99eec8fb31ac2057925d2886601e7d9cecc60352cf435111f429b3fc97be8704dbf1f0fe491089ba2069769314404fb9c68cd5c77adb2a5837372c4d5e5ef5d564e8de98f886a1a13eba20792a29aeaaaa1bc69ed57612ef050b29345e6f987951b2e22fcb00195b3ae2d5ba2087ed5bb2d036baf209eb49520327f6bd05285dabd30c97f239c3a69ff419950bba208a7f080c5629980a5f3ccbfb1ea8a1bc20e7ce0dccbfefe3f35620e69c589e63ba20b2729d612586227170a2116d9f97e2728f7cdc5f1c2e8eb4c4acd35a143917fdba20cdf01d771c38b4ee3363daf4d33c25796adbade4da3f621e954fd1d2e40d7c1fba20ce45e9c7828b2f1b52f29076b0fbbac380db5e6a8318cb7718c99e0b3fe6228fba579c", + "unbonding_transaction_hex": "020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0130d3970000000000225120bd14e11a13e473ed65a7233cd8ae5ccae670fedc9b50bb99c1a09b9cff669fc800000000", + "unbonding_transaction_time_lock_script_hex": "20a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0ad02c900b2", + "unbonding_transaction_slashing_script_hex": "20a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0ad20bc9d21a6400b831e1741056dd45aaeecd42f23fafdf3ea49b815e49ebb4f95fcad2054967fa36311ba41a19a725925a68053579122e7e833c5b76a7dde99eec8fb31ac2057925d2886601e7d9cecc60352cf435111f429b3fc97be8704dbf1f0fe491089ba2069769314404fb9c68cd5c77adb2a5837372c4d5e5ef5d564e8de98f886a1a13eba20792a29aeaaaa1bc69ed57612ef050b29345e6f987951b2e22fcb00195b3ae2d5ba2087ed5bb2d036baf209eb49520327f6bd05285dabd30c97f239c3a69ff419950bba208a7f080c5629980a5f3ccbfb1ea8a1bc20e7ce0dccbfefe3f35620e69c589e63ba20b2729d612586227170a2116d9f97e2728f7cdc5f1c2e8eb4c4acd35a143917fdba20cdf01d771c38b4ee3363daf4d33c25796adbade4da3f621e954fd1d2e40d7c1fba20ce45e9c7828b2f1b52f29076b0fbbac380db5e6a8318cb7718c99e0b3fe6228fba579c", + "op_return_script_hex": "6a470102030400a9200483d40c3c81a7a983a8f0a0de37955fbea587595b4bf8fcb878d857c0c0bc9d21a6400b831e1741056dd45aaeecd42f23fafdf3ea49b815e49ebb4f95fc4e20" + } + }, + { + "name": "10 finality keys, 18/20 covenant committe, 1 staker key with no op_return", + "parameters": { + "covenant_public_keys": [ + "02e3803a6ecff76daf35709c8484f382783d211970f22397d7a258f40ca3b46304", + "03378f464f4365136230d30c3ad7d6425457d6c0742ed7fefd1015f0040cc70769", + "03583c7fbf7450f8f78977b8c9f2981ff10676efefb6399ec925ba54eed7978a2e", + "021fccb5af3a431a928c32267e556f1ba256871f98428419fae1cce6e7b8356cb9", + "03243b84dd7a170a6e1edc32b22357b20f8365a4149a12ef1d340342701d535991", + "037947f704a584a804c91d07250239ec528300bd01dd3137770f7227309f63db0a", + "021e435d3bbab5fb0244c273e38d6f078bfe57a1bd151016ab5c4ac84457d724b3", + "039010f319cde80190717d96dd60823b8e05905a2da458c69f4de19d5c9226cca8", + "03db2bb56299fe3219ea5901198d1872f0523234d529a29fa912a76c9cb6d70db6", + "030f14729c14b6a9b292e7b5142fc5483d7c25196af1ef4b9722ea5821719e2c40", + "02dbf2c13d6e3b0a8ad7286966bd00164ca0a163b483d305df6b492119cbffbabb", + "03f399ff078d6d954a390f67989705ed21e575ccf43a1340ca8752cd1613ca7ce0", + "030feacb370b7fcfcdd5ee7a363d73fda02d596ce61fd00e308d0ab68a7b955f48", + "022367edcf01edd517d46c1b3cc3d40f5a28608e0313636dd09b0904799bccd635", + "03a63763c4100bacdb1c06e5e1ba5599e379a2e23553d56d2b1d0ee3c990bd1192", + "0364df415d63ef3ce23ddd639c4a5ec8fdbfa67254db91228ca77fbec9610367ae", + "03f6a33481886c7d5ed5429c3bb0a83dbdefafd6d140b22a87aa7081cf7073136c", + "0336612620ae82332e4f5da8d78d0d8a4ff7c8b09e88ee988b3dde18ef6a04c497", + "03e0647bb72f1b3d33d69f360d7e9f58f6a693d40b6ef58f49f140a212bf16931c", + "0339eca9e5cf04e22471d6cde649604e6fd0aae077cd5b1af9a1dc26e56d8502f3" + ], + "covenant_quorum": 18, + "finality_provider_public_keys": [ + "027bb40e3cb76545a71fafcb29738fb27b452c3176b41ffd614f7231b40ff5ce68", + "0378c5d7a263ab7e98e311c8d2d7305d13d78a014d4ae0603b427bb55cb42dbed1", + "03f1209d7248936f9ff2fa7b0ec97e5c0d26e4f41ad6de9f2e423128780da5dfec", + "0350eff2669c6d83f858937cd9229fc93ad57c10cc8d8cfb7ec9bc40e32a9f7ff2", + "03fe96402099f8387ce0ddef84bee2d0f869b35bb599a58b9aaec4a73db08dc490", + "0329f526a81bc92dd2ed8cf1682fc4ae5956987f3f6d3c0da71fbcf230c71781ff", + "03cca2d8aa1feb47ad4933154ef8196db229cfbba676607c3c0583397d65a1e4ee", + "02ae3c70699ae235ddebef85378a550f796723a6b961dcff1f33716d5dbb096a1e", + "03a7165ffc90d12c25187b75b2ebdcb57b1c759e357d655d8371cfb96dc4df2ecc", + "034bcfb4042e5d13286fcd3d481b81a61cdfc3715aaf7909c4ee93de49356cd0b4" + ], + "staker_public_key": "037aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5", + "staking_time": 65535, + "staking_value": 100000000, + "staking_tx_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "staking_output_index": 0, + "unbonding_tx_version": 2, + "unbonding_time": 201, + "unbonding_fee": 100000, + "tag": "01020304", + "network": "mainnet" + }, + "expected": { + "staking_output_pkscript_hex": "51205c7f92282283bf913e9a1cf7659b574104b4ca10095cb4cd293d77664e9d6a2d", + "staking_output_value": 100000000, + "staking_transaction_timelock_script_hex": "207aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5ad03ffff00b2", + "staking_transaction_unbonding_script_hex": "207aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5ad200f14729c14b6a9b292e7b5142fc5483d7c25196af1ef4b9722ea5821719e2c40ac200feacb370b7fcfcdd5ee7a363d73fda02d596ce61fd00e308d0ab68a7b955f48ba201e435d3bbab5fb0244c273e38d6f078bfe57a1bd151016ab5c4ac84457d724b3ba201fccb5af3a431a928c32267e556f1ba256871f98428419fae1cce6e7b8356cb9ba202367edcf01edd517d46c1b3cc3d40f5a28608e0313636dd09b0904799bccd635ba20243b84dd7a170a6e1edc32b22357b20f8365a4149a12ef1d340342701d535991ba2036612620ae82332e4f5da8d78d0d8a4ff7c8b09e88ee988b3dde18ef6a04c497ba20378f464f4365136230d30c3ad7d6425457d6c0742ed7fefd1015f0040cc70769ba2039eca9e5cf04e22471d6cde649604e6fd0aae077cd5b1af9a1dc26e56d8502f3ba20583c7fbf7450f8f78977b8c9f2981ff10676efefb6399ec925ba54eed7978a2eba2064df415d63ef3ce23ddd639c4a5ec8fdbfa67254db91228ca77fbec9610367aeba207947f704a584a804c91d07250239ec528300bd01dd3137770f7227309f63db0aba209010f319cde80190717d96dd60823b8e05905a2da458c69f4de19d5c9226cca8ba20a63763c4100bacdb1c06e5e1ba5599e379a2e23553d56d2b1d0ee3c990bd1192ba20db2bb56299fe3219ea5901198d1872f0523234d529a29fa912a76c9cb6d70db6ba20dbf2c13d6e3b0a8ad7286966bd00164ca0a163b483d305df6b492119cbffbabbba20e0647bb72f1b3d33d69f360d7e9f58f6a693d40b6ef58f49f140a212bf16931cba20e3803a6ecff76daf35709c8484f382783d211970f22397d7a258f40ca3b46304ba20f399ff078d6d954a390f67989705ed21e575ccf43a1340ca8752cd1613ca7ce0ba20f6a33481886c7d5ed5429c3bb0a83dbdefafd6d140b22a87aa7081cf7073136cba01129c", + "staking_transaction_slashing_script_hex": "207aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5ad2029f526a81bc92dd2ed8cf1682fc4ae5956987f3f6d3c0da71fbcf230c71781ffac204bcfb4042e5d13286fcd3d481b81a61cdfc3715aaf7909c4ee93de49356cd0b4ba2050eff2669c6d83f858937cd9229fc93ad57c10cc8d8cfb7ec9bc40e32a9f7ff2ba2078c5d7a263ab7e98e311c8d2d7305d13d78a014d4ae0603b427bb55cb42dbed1ba207bb40e3cb76545a71fafcb29738fb27b452c3176b41ffd614f7231b40ff5ce68ba20a7165ffc90d12c25187b75b2ebdcb57b1c759e357d655d8371cfb96dc4df2eccba20ae3c70699ae235ddebef85378a550f796723a6b961dcff1f33716d5dbb096a1eba20cca2d8aa1feb47ad4933154ef8196db229cfbba676607c3c0583397d65a1e4eeba20f1209d7248936f9ff2fa7b0ec97e5c0d26e4f41ad6de9f2e423128780da5dfecba20fe96402099f8387ce0ddef84bee2d0f869b35bb599a58b9aaec4a73db08dc490ba519d200f14729c14b6a9b292e7b5142fc5483d7c25196af1ef4b9722ea5821719e2c40ac200feacb370b7fcfcdd5ee7a363d73fda02d596ce61fd00e308d0ab68a7b955f48ba201e435d3bbab5fb0244c273e38d6f078bfe57a1bd151016ab5c4ac84457d724b3ba201fccb5af3a431a928c32267e556f1ba256871f98428419fae1cce6e7b8356cb9ba202367edcf01edd517d46c1b3cc3d40f5a28608e0313636dd09b0904799bccd635ba20243b84dd7a170a6e1edc32b22357b20f8365a4149a12ef1d340342701d535991ba2036612620ae82332e4f5da8d78d0d8a4ff7c8b09e88ee988b3dde18ef6a04c497ba20378f464f4365136230d30c3ad7d6425457d6c0742ed7fefd1015f0040cc70769ba2039eca9e5cf04e22471d6cde649604e6fd0aae077cd5b1af9a1dc26e56d8502f3ba20583c7fbf7450f8f78977b8c9f2981ff10676efefb6399ec925ba54eed7978a2eba2064df415d63ef3ce23ddd639c4a5ec8fdbfa67254db91228ca77fbec9610367aeba207947f704a584a804c91d07250239ec528300bd01dd3137770f7227309f63db0aba209010f319cde80190717d96dd60823b8e05905a2da458c69f4de19d5c9226cca8ba20a63763c4100bacdb1c06e5e1ba5599e379a2e23553d56d2b1d0ee3c990bd1192ba20db2bb56299fe3219ea5901198d1872f0523234d529a29fa912a76c9cb6d70db6ba20dbf2c13d6e3b0a8ad7286966bd00164ca0a163b483d305df6b492119cbffbabbba20e0647bb72f1b3d33d69f360d7e9f58f6a693d40b6ef58f49f140a212bf16931cba20e3803a6ecff76daf35709c8484f382783d211970f22397d7a258f40ca3b46304ba20f399ff078d6d954a390f67989705ed21e575ccf43a1340ca8752cd1613ca7ce0ba20f6a33481886c7d5ed5429c3bb0a83dbdefafd6d140b22a87aa7081cf7073136cba01129c", + "unbonding_transaction_hex": "020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff01605af4050000000022512044b4c847be70ba60488426ae7c59a636dc952b8494fb2f01584c9f533cd4b07c00000000", + "unbonding_transaction_time_lock_script_hex": "207aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5ad02c900b2", + "unbonding_transaction_slashing_script_hex": "207aa0dafe26a7b663b679418f2c19fe7ea1215919051aa5ac76e4a9fbef2f33c5ad2029f526a81bc92dd2ed8cf1682fc4ae5956987f3f6d3c0da71fbcf230c71781ffac204bcfb4042e5d13286fcd3d481b81a61cdfc3715aaf7909c4ee93de49356cd0b4ba2050eff2669c6d83f858937cd9229fc93ad57c10cc8d8cfb7ec9bc40e32a9f7ff2ba2078c5d7a263ab7e98e311c8d2d7305d13d78a014d4ae0603b427bb55cb42dbed1ba207bb40e3cb76545a71fafcb29738fb27b452c3176b41ffd614f7231b40ff5ce68ba20a7165ffc90d12c25187b75b2ebdcb57b1c759e357d655d8371cfb96dc4df2eccba20ae3c70699ae235ddebef85378a550f796723a6b961dcff1f33716d5dbb096a1eba20cca2d8aa1feb47ad4933154ef8196db229cfbba676607c3c0583397d65a1e4eeba20f1209d7248936f9ff2fa7b0ec97e5c0d26e4f41ad6de9f2e423128780da5dfecba20fe96402099f8387ce0ddef84bee2d0f869b35bb599a58b9aaec4a73db08dc490ba519d200f14729c14b6a9b292e7b5142fc5483d7c25196af1ef4b9722ea5821719e2c40ac200feacb370b7fcfcdd5ee7a363d73fda02d596ce61fd00e308d0ab68a7b955f48ba201e435d3bbab5fb0244c273e38d6f078bfe57a1bd151016ab5c4ac84457d724b3ba201fccb5af3a431a928c32267e556f1ba256871f98428419fae1cce6e7b8356cb9ba202367edcf01edd517d46c1b3cc3d40f5a28608e0313636dd09b0904799bccd635ba20243b84dd7a170a6e1edc32b22357b20f8365a4149a12ef1d340342701d535991ba2036612620ae82332e4f5da8d78d0d8a4ff7c8b09e88ee988b3dde18ef6a04c497ba20378f464f4365136230d30c3ad7d6425457d6c0742ed7fefd1015f0040cc70769ba2039eca9e5cf04e22471d6cde649604e6fd0aae077cd5b1af9a1dc26e56d8502f3ba20583c7fbf7450f8f78977b8c9f2981ff10676efefb6399ec925ba54eed7978a2eba2064df415d63ef3ce23ddd639c4a5ec8fdbfa67254db91228ca77fbec9610367aeba207947f704a584a804c91d07250239ec528300bd01dd3137770f7227309f63db0aba209010f319cde80190717d96dd60823b8e05905a2da458c69f4de19d5c9226cca8ba20a63763c4100bacdb1c06e5e1ba5599e379a2e23553d56d2b1d0ee3c990bd1192ba20db2bb56299fe3219ea5901198d1872f0523234d529a29fa912a76c9cb6d70db6ba20dbf2c13d6e3b0a8ad7286966bd00164ca0a163b483d305df6b492119cbffbabbba20e0647bb72f1b3d33d69f360d7e9f58f6a693d40b6ef58f49f140a212bf16931cba20e3803a6ecff76daf35709c8484f382783d211970f22397d7a258f40ca3b46304ba20f399ff078d6d954a390f67989705ed21e575ccf43a1340ca8752cd1613ca7ce0ba20f6a33481886c7d5ed5429c3bb0a83dbdefafd6d140b22a87aa7081cf7073136cba01129c", + "op_return_script_hex": "" + } + } +] diff --git a/rust/chains/tw_bitcoincash/src/address.rs b/rust/chains/tw_bitcoincash/src/address.rs index 748a8636f0f..af23b0bd7ce 100644 --- a/rust/chains/tw_bitcoincash/src/address.rs +++ b/rust/chains/tw_bitcoincash/src/address.rs @@ -7,7 +7,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::prelude::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::prefix::BitcoinBase58Prefix; use tw_keypair::ecdsa; use tw_memory::Data; diff --git a/rust/chains/tw_bitcoincash/src/cash_address/unchecked.rs b/rust/chains/tw_bitcoincash/src/cash_address/unchecked.rs index 9a4a0d5c7fa..4e1de1490e2 100644 --- a/rust/chains/tw_bitcoincash/src/cash_address/unchecked.rs +++ b/rust/chains/tw_bitcoincash/src/cash_address/unchecked.rs @@ -6,7 +6,7 @@ use crate::cash_address::checksum::{calculate_checksum, CHECKSUM_LEN}; use crate::cash_address::{cash_base32, CashAddress, CashAddressType}; use std::fmt; use std::str::FromStr; -use tw_coin_entry::error::prelude::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bech32; use tw_hash::H160; use tw_memory::Data; diff --git a/rust/chains/tw_pactus/src/encoder/error.rs b/rust/chains/tw_pactus/src/encoder/error.rs index b3643854a90..4baba85527f 100644 --- a/rust/chains/tw_pactus/src/encoder/error.rs +++ b/rust/chains/tw_pactus/src/encoder/error.rs @@ -1,4 +1,4 @@ -use tw_coin_entry::error::prelude::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; /// Errors encountered when encoding or decoding data. #[derive(Debug)] diff --git a/rust/chains/tw_solana/src/modules/message_decompiler.rs b/rust/chains/tw_solana/src/modules/message_decompiler.rs index d74e6393785..70b8c556250 100644 --- a/rust/chains/tw_solana/src/modules/message_decompiler.rs +++ b/rust/chains/tw_solana/src/modules/message_decompiler.rs @@ -5,7 +5,7 @@ use crate::address::SolanaAddress; use crate::transaction::versioned::VersionedMessage; use crate::transaction::CompiledInstruction; -use tw_coin_entry::error::prelude::{OrTWError, ResultContext, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; /// [`Instruction`] without `accounts` field. diff --git a/rust/frameworks/tw_ton_sdk/src/address/user_friendly_address.rs b/rust/frameworks/tw_ton_sdk/src/address/user_friendly_address.rs index b1723e108d1..514dc58bda7 100644 --- a/rust/frameworks/tw_ton_sdk/src/address/user_friendly_address.rs +++ b/rust/frameworks/tw_ton_sdk/src/address/user_friendly_address.rs @@ -4,7 +4,7 @@ use crate::address::address_data::AddressData; use crate::crc::CRC_16_XMODEM; -use tw_coin_entry::error::prelude::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64; use tw_encoding::base64::{NO_PAD, URL_NO_PAD}; use tw_hash::{H256, H288}; diff --git a/rust/frameworks/tw_ton_sdk/src/boc/binary_writer.rs b/rust/frameworks/tw_ton_sdk/src/boc/binary_writer.rs index 688d71f4e18..db7acaf4c53 100644 --- a/rust/frameworks/tw_ton_sdk/src/boc/binary_writer.rs +++ b/rust/frameworks/tw_ton_sdk/src/boc/binary_writer.rs @@ -4,7 +4,7 @@ use crate::error::{CellErrorType, CellResult}; use bitstream_io::{BigEndian, BitWrite, BitWriter, Numeric}; -use tw_coin_entry::error::prelude::{MapTWError, OrTWError, ResultContext}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; pub struct BinaryWriter { diff --git a/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs b/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs index c3f4e3d60e0..7957446c83b 100644 --- a/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs +++ b/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs @@ -11,7 +11,7 @@ use crate::error::{CellErrorType, CellResult}; use std::cell::RefCell; use std::collections::BTreeMap; use std::sync::Arc; -use tw_coin_entry::error::prelude::{OrTWError, ResultContext}; +use tw_coin_entry::error::prelude::*; use tw_hash::H256; type IndexedCellRef = RefCell; diff --git a/rust/frameworks/tw_ton_sdk/src/cell/cell_builder.rs b/rust/frameworks/tw_ton_sdk/src/cell/cell_builder.rs index 99d394ca169..a12f505d98c 100644 --- a/rust/frameworks/tw_ton_sdk/src/cell/cell_builder.rs +++ b/rust/frameworks/tw_ton_sdk/src/cell/cell_builder.rs @@ -11,7 +11,7 @@ use crate::cell::{Cell, CellArc}; use crate::error::{CellError, CellErrorType, CellResult}; use bitstream_io::Numeric; use std::sync::Arc; -use tw_coin_entry::error::prelude::{MapTWError, ResultContext}; +use tw_coin_entry::error::prelude::*; use tw_number::U256; const MAX_CELL_BITS: usize = 1023; diff --git a/rust/frameworks/tw_ton_sdk/src/cell/cell_parser.rs b/rust/frameworks/tw_ton_sdk/src/cell/cell_parser.rs index e7143ded0a3..0dadb162f2c 100644 --- a/rust/frameworks/tw_ton_sdk/src/cell/cell_parser.rs +++ b/rust/frameworks/tw_ton_sdk/src/cell/cell_parser.rs @@ -8,7 +8,7 @@ use crate::address::address_data::AddressData; use crate::error::{CellError, CellErrorType, CellResult}; use bitreader::BitReader; use num_bigint::BigUint; -use tw_coin_entry::error::prelude::{MapTWError, ResultContext}; +use tw_coin_entry::error::prelude::*; use tw_hash::H256; use tw_memory::Data; use tw_number::U256; diff --git a/rust/frameworks/tw_ton_sdk/src/cell/mod.rs b/rust/frameworks/tw_ton_sdk/src/cell/mod.rs index c6edaf7adfb..2ef481ca280 100644 --- a/rust/frameworks/tw_ton_sdk/src/cell/mod.rs +++ b/rust/frameworks/tw_ton_sdk/src/cell/mod.rs @@ -8,7 +8,7 @@ use crate::boc::binary_writer::BinaryWriter; use crate::cell::cell_parser::CellParser; use std::fmt; use std::sync::Arc; -use tw_coin_entry::error::prelude::{MapTWError, OrTWError, ResultContext}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64::{self, URL_NO_PAD}; use tw_encoding::hex::ToHex; use tw_hash::sha2::sha256; diff --git a/rust/frameworks/tw_utxo/src/context.rs b/rust/frameworks/tw_utxo/src/context.rs index f0fd5e7b109..3b9cb66fb63 100644 --- a/rust/frameworks/tw_utxo/src/context.rs +++ b/rust/frameworks/tw_utxo/src/context.rs @@ -4,7 +4,7 @@ use crate::script::Script; use std::str::FromStr; -use tw_coin_entry::error::prelude::{AddressError, SigningResult}; +use tw_coin_entry::error::prelude::*; pub struct AddressPrefixes { pub p2pkh_prefix: u8, diff --git a/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs index f2ee8880590..81029ac191a 100644 --- a/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs +++ b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs @@ -65,7 +65,7 @@ where let utxo_args = UtxoPreimageArgs { input_index: signing_input_index, - script_pubkey: utxo.script_pubkey.clone(), + script_pubkey: utxo.reveal_script_pubkey.clone(), amount: utxo.amount, // TODO move `leaf_hash_code_separator` to `UtxoTaprootPreimageArgs`. leaf_hash_code_separator: utxo.leaf_hash_code_separator, @@ -91,15 +91,14 @@ where .input_args() .iter() .enumerate() - .map(|(i, utxo)| { - if i == signing_input_index { - // Use the scriptPubkey required to spend this UTXO. - utxo.script_pubkey.clone() - } else { - // Use the original scriptPubkey declared in the unspent output for other UTXOs - // (different from that we sign at this iteration). - utxo.prevout_script_pubkey.clone() - } + .map(|(i, utxo)| match utxo.taproot_reveal_script_pubkey { + // Use the scriptPubkey required to spend this UTXO. + Some(ref tr_reveal_script) if i == signing_input_index => { + tr_reveal_script.clone() + }, + // Use the original scriptPubkey declared in the unspent output for other UTXOs + // (different from that we sign at this iteration). + _ => utxo.prevout_script_pubkey.clone(), }) .collect(); diff --git a/rust/frameworks/tw_utxo/src/script/mod.rs b/rust/frameworks/tw_utxo/src/script/mod.rs index 7600a1e5ba8..dbcfc3de181 100644 --- a/rust/frameworks/tw_utxo/src/script/mod.rs +++ b/rust/frameworks/tw_utxo/src/script/mod.rs @@ -89,6 +89,26 @@ impl Script { self.bytes.extend_from_slice(data); } + /// Adds instructions to push an integer onto the stack. + /// + /// Integers are encoded as little-endian signed-magnitude numbers, but there are dedicated + /// opcodes to push some small integers. + pub fn push_int(&mut self, data: i64) { + // We can special-case -1, 1-16 + if data == -1 || (1..=16).contains(&data) { + let opcode = (data - 1 + OP_TRUE as i64) as u8; + self.push(opcode) + } + // We can also special-case zero + else if data == 0 { + self.push(OP_0) + } + // Otherwise encode it as data + else { + self.push_int_non_minimal(data) + } + } + /// Appends the given data to the end of the script as-is. pub fn append(&mut self, data: &[u8]) { self.bytes.extend_from_slice(data); @@ -101,6 +121,16 @@ impl Script { pub fn to_vec(&self) -> Data { self.bytes.clone() } + + /// Adds instructions to push an integer onto the stack without optimization. + /// + /// This uses the explicit encoding regardless of the availability of dedicated opcodes. + fn push_int_non_minimal(&mut self, data: i64) { + let mut buf = [0u8; 8]; + // Use rust-bitcoin crate for now. + let len = bitcoin::script::write_scriptint(&mut buf, data); + self.push_slice(&buf[..len]) + } } impl From