diff --git a/.github/workflows/RPCNodeRelease.yml b/.github/workflows/RPCNodeRelease.yml deleted file mode 100644 index 9e8a8c58f..000000000 --- a/.github/workflows/RPCNodeRelease.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: RpcNodeRelease -on: - push: - tags: - - '*-release' -env: - CARGO_TERM_COLOR: always -jobs: - build: - strategy: - matrix: - platform: [scalable] - runs-on: ${{ matrix.platform }} - env: - ENV: release - PRIVATE_ECR_URL: 358484141435.dkr.ecr.us-west-2.amazonaws.com - PUBLIC_ECR_URL: public.ecr.aws/k6m5b6e2 - DOCKERHUB_URL: findoranetwork - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - components: rustfmt - - - - name: Prepare key - shell: bash - run: | - tar -C ~/.ssh -zcf key.tar.gz ./ - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - aws-region: us-east-1 - - - name: Login to Amazon ECR - uses: aws-actions/amazon-ecr-login@v1 - with: - registry-type: public - - - name: Clean garbage containers and images - shell: bash - run: | - docker rm $(docker ps -a | grep -v "^CONTAINER" | awk '{print $1}') || true - docker rmi -f $(docker images -f "dangling=true" -q) || true - - name: Build rust base image - shell: bash - run: | - make ci_build_binary_rust_base - - name: Build binary image - env: - GITHUB_CONTEXT: ${{ toJSON(github) }} - shell: bash - run: | - echo "REF: ${GITHUB_REF}" - TAGS=$(grep "refs/tags" <<< ${GITHUB_REF}) || true - TAGV="${TAGS#refs/tags/}" - export IMAGE_TAG="RPCNode-${TAGV:-main}" - export VERGEN_SHA_EXTERN="${IMAGE_TAG}" - echo "Image tag: ${IMAGE_TAG}" - make ci_build_release_web3_binary_image - - - name: Build images - env: - PRIVATE_ECR_URL: ${{ env.PRIVATE_ECR_URL }} - PUBLIC_ECR_URL: ${{ env.PUBLIC_ECR_URL }} - ENV: release - shell: bash - run: | - echo "REF: ${GITHUB_REF}" - TAGS=$(grep "refs/tags" <<< ${GITHUB_REF}) || true - TAGV="${TAGS#refs/tags/}" - export IMAGE_TAG="RPCNode-${TAGV:-main}" - echo "Image tag: ${IMAGE_TAG}" - make ci_build_image_web3 - - - name: Push images - env: - PRIVATE_ECR_URL: ${{ env.PRIVATE_ECR_URL }} - PUBLIC_ECR_URL: ${{ env.PUBLIC_ECR_URL }} - ENV: release - shell: bash - run: | - echo "REF: ${GITHUB_REF}" - TAGS=$(grep "refs/tags" <<< ${GITHUB_REF}) || true - TAGV="${TAGS#refs/tags/}" - export IMAGE_TAG="RPCNode-${TAGV:-main}" - echo "Image tag: ${IMAGE_TAG}" - make ci_push_image - diff --git a/Cargo.toml b/Cargo.toml index 1ee579fa2..57d92f5b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ members = [ "src/components/contracts/modules/evm/precompile/modexp", "src/components/contracts/modules/evm/precompile/sha3fips", "src/components/contracts/modules/evm/precompile/anemoi", + "src/components/contracts/modules/evm/precompile/blake2", + "src/components/contracts/modules/evm/precompile/bn128", "src/components/contracts/modules/evm/precompile/utils", "src/components/contracts/modules/evm/precompile/utils/macro", "src/components/contracts/modules/xhub", diff --git a/Makefile b/Makefile index 95f404a22..3b3563d73 100644 --- a/Makefile +++ b/Makefile @@ -370,7 +370,7 @@ build_musl_fn_macos_base: build_musl_fn_macos: docker build -t musl_fn_macos -f container/Dockerfile-fn-musl-macos . docker run -d --rm --name fn_macos musl_fn_macos - docker cp fn_macos:/volume/target/x86_64-apple-darwin/release/fn fn + docker cp fn_macos:/platform/target/x86_64-apple-darwin/release/fn fn tar -czvf fn_macos.tar.gz fn rm fn diff --git a/src/components/abciapp/src/abci/server/callback/mod.rs b/src/components/abciapp/src/abci/server/callback/mod.rs index 0cbcec7d6..d0ae04da8 100644 --- a/src/components/abciapp/src/abci/server/callback/mod.rs +++ b/src/components/abciapp/src/abci/server/callback/mod.rs @@ -2,6 +2,13 @@ //! # Impl function of tendermint ABCI //! +use globutils::wallet; +use ledger::{ + data_model::ASSET_TYPE_FRA, + staking::{FF_ADDR_EXTRA_120_0000, FF_ADDR_LIST}, +}; +use zei::xfr::asset_record::AssetRecordType; + mod utils; use { @@ -29,7 +36,7 @@ use { ledger::{ converter::is_convert_account, data_model::Operation, - staking::KEEP_HIST, + staking::{evm::EVM_STAKING, KEEP_HIST, VALIDATOR_UPDATE_BLOCK_ITV}, store::{ api_cache, fbnc::{new_mapx, Mapx}, @@ -470,6 +477,33 @@ pub fn end_block( let mut la = s.la.write(); + if header.height == CFG.checkpoint.evm_staking_inital_height { + let ledger_state = la.get_committed_state().read(); + let staking = ledger_state.get_staking(); + let validators = staking + .validator_get_current() + .map(|v| v.get_validators().values().cloned().collect::>()) + .unwrap_or_default(); + + let delegations = staking + .get_global_delegation_records() + .values() + .map(|v| (v.id, v.clone())) + .collect(); + let coinbase_balance = staking.coinbase_balance(); + + if let Err(e) = EVM_STAKING.get().c(d!()).and_then(|staking| { + staking.write().import_validators( + &validators, + &delegations, + coinbase_balance, + ) + }) { + tracing::error!(target: "evm staking", "import_validators error:{:?}", e); + panic!() + }; + } + // mint coinbase, cache system transactions to ledger { let laa = la.get_committed_state().read(); @@ -485,25 +519,54 @@ pub fn end_block( if !la.all_commited() && la.block_txn_count() != 0 { pnk!(la.end_block()); } + if td_height <= CFG.checkpoint.evm_staking_inital_height { + if let Ok(Some(vs)) = ruc::info!(staking::get_validators( + la.get_committed_state().read().get_staking().deref(), + begin_block_req.last_commit_info.as_ref() + )) { + resp.set_validator_updates(RepeatedField::from_vec(vs)); + } - if let Ok(Some(vs)) = ruc::info!(staking::get_validators( - la.get_committed_state().read().get_staking().deref(), - begin_block_req.last_commit_info.as_ref() - )) { - resp.set_validator_updates(RepeatedField::from_vec(vs)); + staking::system_ops( + &mut la.get_committed_state().write(), + &header, + begin_block_req.last_commit_info.as_ref(), + &begin_block_req.byzantine_validators.as_slice(), + ); } - staking::system_ops( - &mut la.get_committed_state().write(), - &header, - begin_block_req.last_commit_info.as_ref(), - &begin_block_req.byzantine_validators.as_slice(), - ); - - if td_height <= CFG.checkpoint.disable_evm_block_height + let evm_resp = if td_height <= CFG.checkpoint.disable_evm_block_height || td_height >= CFG.checkpoint.enable_frc20_height { - let _ = s.account_base_app.write().end_block(req); + let mut sum = 0; + let ledger = la.get_committed_state().read(); + let mut addrs = FF_ADDR_LIST.to_vec(); + addrs.push(FF_ADDR_EXTRA_120_0000); + for fra_addr in addrs.iter() { + for (_, (utxo, _)) in pnk!(wallet::public_key_from_bech32(fra_addr) + .and_then(|pub_key| ledger.get_owned_utxos(&pub_key))) + { + if AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType + == utxo.0.record.get_record_type() + && Some(ASSET_TYPE_FRA) == utxo.0.record.asset_type.get_asset_type() + { + if let Some(v) = utxo.0.record.amount.get_amount() { + sum += v; + }; + } + } + } + + s.account_base_app.write().end_block(req, sum) + } else { + Default::default() + }; + + if td_height > CFG.checkpoint.evm_staking_inital_height + && 0 == TENDERMINT_BLOCK_HEIGHT.load(Ordering::Relaxed) + % VALIDATOR_UPDATE_BLOCK_ITV + { + resp.validator_updates = evm_resp.validator_updates; } resp diff --git a/src/components/abciapp/src/abci/server/mod.rs b/src/components/abciapp/src/abci/server/mod.rs index dc252eebd..aa66ce218 100644 --- a/src/components/abciapp/src/abci/server/mod.rs +++ b/src/components/abciapp/src/abci/server/mod.rs @@ -15,7 +15,7 @@ use { }, baseapp::BaseApp as AccountBaseAPP, config::abci::global_cfg::CFG, - ledger::store::LedgerState, + ledger::{staking::evm::EVM_STAKING, store::LedgerState}, parking_lot::RwLock, rand_chacha::ChaChaRng, rand_core::SeedableRng, @@ -70,7 +70,10 @@ impl ABCISubmissionServer { )) } }; - + let account_base_app = Arc::new(RwLock::new(account_base_app)); + if EVM_STAKING.set(account_base_app.clone()).is_err() { + return Err(eg!("Invalid usage.")); + } let prng = rand_chacha::ChaChaRng::from_entropy(); Ok(ABCISubmissionServer { la: Arc::new(RwLock::new( @@ -81,7 +84,7 @@ impl ABCISubmissionServer { ) .c(d!())?, )), - account_base_app: Arc::new(RwLock::new(account_base_app)), + account_base_app, }) } } diff --git a/src/components/abciapp/src/abci/staking/mod.rs b/src/components/abciapp/src/abci/staking/mod.rs index 10c0ac3fd..d65de6ae3 100644 --- a/src/components/abciapp/src/abci/staking/mod.rs +++ b/src/components/abciapp/src/abci/staking/mod.rs @@ -4,6 +4,8 @@ //! Business logic based on [**Ledger Staking**](ledger::staking). //! +use ledger::staking::evm::EVM_STAKING_MINTS; + mod whoami; #[cfg(test)] @@ -468,6 +470,13 @@ pub fn system_mint_pay( mint_entries.append(&mut mints); + //Mints from evm staking + for mint in EVM_STAKING_MINTS.lock().drain(..).map(|(pk, amount)| { + MintEntry::new(MintKind::Other, pk, None, amount, ASSET_TYPE_FRA) + }) { + mint_entries.push(mint) + } + if mint_entries.is_empty() { None } else { diff --git a/src/components/config/src/abci/mod.rs b/src/components/config/src/abci/mod.rs index 6d3f3460a..66b6ec98b 100644 --- a/src/components/config/src/abci/mod.rs +++ b/src/components/config/src/abci/mod.rs @@ -141,6 +141,15 @@ pub struct CheckPointConfig { #[serde(default = "def_validator_whitelist_v3")] pub validator_whitelist_v3: Vec, + + #[serde(default = "def_max_gas_price_limit")] + pub max_gas_price_limit: i64, + + #[serde(default = "def_evm_staking_inital_height")] + pub evm_staking_inital_height: i64, + + #[serde(default = "def_evm_staking_address")] + pub evm_staking_address: String, } fn def_fix_check_replay() -> u64 { @@ -205,6 +214,19 @@ fn def_validator_whitelist_v3_height() -> u64 { fn def_validator_whitelist_v3() -> Vec { DEFAULT_CHECKPOINT_CONFIG.validator_whitelist_v3.clone() } + +fn def_max_gas_price_limit() -> i64 { + DEFAULT_CHECKPOINT_CONFIG.max_gas_price_limit +} + +fn def_evm_staking_inital_height() -> i64 { + DEFAULT_CHECKPOINT_CONFIG.evm_staking_inital_height +} + +fn def_evm_staking_address() -> String { + DEFAULT_CHECKPOINT_CONFIG.evm_staking_address.clone() +} + #[cfg(feature = "debug_env")] lazy_static! { static ref DEFAULT_CHECKPOINT_CONFIG: CheckPointConfig = CheckPointConfig { @@ -246,6 +268,9 @@ lazy_static! { validator_whitelist_v2: vec![], validator_whitelist_v3_height: 0, validator_whitelist_v3: vec![], + max_gas_price_limit: 0, + evm_staking_inital_height: 128, + evm_staking_address: "0x84db796A3F8F02396f82219e3197933d15960771".to_owned(), }; } @@ -577,6 +602,9 @@ lazy_static! { "37D3228A650F591522698BECDF42DCE5D1113D88".to_string(), "577F8548D8F834D39D26350D2A3A928F478AF5FD".to_string(), ], + max_gas_price_limit: 5000000, + evm_staking_inital_height: 5000000, + evm_staking_address: "".to_owned(), }; } diff --git a/src/components/contracts/baseapp/Cargo.toml b/src/components/contracts/baseapp/Cargo.toml index 9336d2b42..5a2598c54 100644 --- a/src/components/contracts/baseapp/Cargo.toml +++ b/src/components/contracts/baseapp/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1.0.40" storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } sha3 = "0.8" +zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } config = { path = "../../config"} diff --git a/src/components/contracts/baseapp/src/app.rs b/src/components/contracts/baseapp/src/app.rs index e810172f2..035a76a2e 100644 --- a/src/components/contracts/baseapp/src/app.rs +++ b/src/components/contracts/baseapp/src/app.rs @@ -1,4 +1,4 @@ -use crate::extensions::SignedExtra; +use crate::{extensions::SignedExtra, BaseApp}; use abci::*; use config::abci::global_cfg::CFG; use enterprise_web3::{ @@ -7,14 +7,25 @@ use enterprise_web3::{ }; use fp_core::context::RunTxMode; use fp_evm::BlockId; +use fp_traits::account::AccountAsset; use fp_types::{ actions::xhub::NonConfidentialOutput, assemble::convert_unchecked_transaction, + crypto::Address, }; use fp_utils::tx::EvmRawTxWrapper; -use module_evm::utils::{deposit_asset_event_topic_str, parse_deposit_asset_event}; -use primitive_types::U256; +use ledger::data_model::ASSET_TYPE_FRA; +use module_evm::{ + get_claim_on_contract_address, + system_contracts::SYSTEM_ADDR, + utils::{ + coinbase_mint_event, coinbase_mint_event_topic_str, + deposit_asset_event_topic_str, parse_deposit_asset_event, + parse_evm_staking_coinbase_mint_event, + }, +}; +use primitive_types::{H160, H256, U256}; use ruc::*; -use std::{mem::take, ops::DerefMut}; +use std::{mem::take, ops::DerefMut, str::FromStr}; use tracing::{debug, error, info}; impl crate::BaseApp { /// info implements the ABCI interface. @@ -283,12 +294,40 @@ impl crate::BaseApp { if td_height > CFG.checkpoint.prismxx_inital_height && 0 == resp.code { let deposit_asset_topic = deposit_asset_event_topic_str(); + let coinbase_mint_event_topic = coinbase_mint_event_topic_str(); for evt in resp.events.iter() { if evt.field_type == *"ethereum_ContractLog" { let mut bridge_contract_found = false; let mut deposit_asset_foud = false; + let mut staking_contract_found = false; + let mut coinbase_mint_foud = false; + let mut validator = H256::zero(); + let mut delegator = H256::zero(); + + let claim_on_contract_address = if td_height + > CFG.checkpoint.evm_staking_inital_height + { + match H160::from_str(SYSTEM_ADDR).c(d!()).and_then( + |from| { + get_claim_on_contract_address::( + &self.modules.evm_module.contracts, + &self.deliver_state, + from, + ) + }, + ) { + Ok(v) => v, + Err(e) => { + resp.code = 1; + resp.log = e.to_string(); + return (resp, vec![]); + } + } + } else { + Default::default() + }; for pair in evt.attributes.iter() { let key = String::from_utf8(pair.key.clone()) .unwrap_or_default(); @@ -302,6 +341,11 @@ impl crate::BaseApp { .to_lowercase() { bridge_contract_found = true + } else if addr + == format!("{:?}", claim_on_contract_address) + .to_lowercase() + { + staking_contract_found = true; } } if key == *"topics" { @@ -309,29 +353,131 @@ impl crate::BaseApp { String::from_utf8(pair.value.clone()) .unwrap_or_default(); if topic == deposit_asset_topic { - deposit_asset_foud = true + deposit_asset_foud = true; + } else if td_height + > CFG.checkpoint.evm_staking_inital_height + && topic.starts_with( + &coinbase_mint_event_topic[0 + ..coinbase_mint_event_topic.len() + - 1], + ) + { + let hashes = topic + .strip_prefix('[') + .and_then(|v| v.strip_suffix(']')) + .unwrap_or(&topic) + .split(", ") + .collect::>(); + + let mut validator_flag = false; + if let Some(v) = hashes.get(1) { + validator_flag = true; + + let s = v + .strip_prefix(' ') + .and_then(|v| v.strip_suffix(' ')) + .unwrap_or(v); + validator = match H256::from_str(s) { + Ok(v) => v, + Err(e) => { + resp.code = 1; + resp.log = e.to_string(); + return (resp, vec![]); + } + }; + }; + let mut delegator_flag = false; + if let Some(v) = hashes.get(2) { + delegator_flag = true; + + let s = v + .strip_prefix(' ') + .and_then(|v| v.strip_suffix(' ')) + .unwrap_or(v); + delegator = match H256::from_str(s) { + Ok(v) => v, + Err(e) => { + resp.code = 1; + resp.log = e.to_string(); + return (resp, vec![]); + } + }; + }; + coinbase_mint_foud = + validator_flag && delegator_flag; } } - if key == *"data" - && bridge_contract_found - && deposit_asset_foud - { - let data = String::from_utf8(pair.value.clone()) - .unwrap_or_default(); - - let data_vec = - serde_json::from_str(&data).unwrap(); - - let deposit_asset = - parse_deposit_asset_event(data_vec); - - match deposit_asset { - Ok(deposit) => { - non_confidential_outputs.push(deposit) + if key == *"data" { + if bridge_contract_found && deposit_asset_foud { + let data = + String::from_utf8(pair.value.clone()) + .unwrap_or_default(); + + let data_vec = + serde_json::from_str(&data).unwrap(); + + let deposit_asset = + parse_deposit_asset_event(data_vec); + + match deposit_asset { + Ok(deposit) => non_confidential_outputs + .push(deposit), + Err(e) => { + resp.code = 1; + resp.log = e.to_string(); + } } - Err(e) => { - resp.code = 1; - resp.log = e.to_string(); + } else if td_height + > CFG.checkpoint.evm_staking_inital_height + && staking_contract_found + && coinbase_mint_foud + { + let data = + String::from_utf8(pair.value.clone()) + .unwrap_or_default(); + + let data_vec: Vec = + serde_json::from_str(&data).unwrap(); + + let event = coinbase_mint_event(); + let ret = + parse_evm_staking_coinbase_mint_event( + &event, + vec![ + event.signature(), + validator, + delegator, + ], + data_vec, + ); + + match ret { + Ok((addr, pub_key, amount)) => { + if let Some(pk) = pub_key { + non_confidential_outputs.push( + NonConfidentialOutput { + asset: ASSET_TYPE_FRA, + amount, + target: pk, + decimal: 0, + max_supply: 0, + }, + ); + } else if let Err(e) = module_account::App::< + BaseApp, + >::mint( + &self.deliver_state, + &Address::from(addr), + U256::from(amount), + ) { + resp.code = 2; + resp.log = e.to_string(); + } + } + Err(e) => { + resp.code = 1; + resp.log = e.to_string(); + } } } } @@ -357,13 +503,22 @@ impl crate::BaseApp { } #[cfg(any(feature = "abci_mock", test))] - pub fn end_block(&mut self, _req: &RequestEndBlock) -> ResponseEndBlock { + pub fn end_block( + &mut self, + _req: &RequestEndBlock, + _ff_addr_balance: u64, + ) -> ResponseEndBlock { Default::default() } #[cfg(all(not(feature = "abci_mock"), not(test)))] - pub fn end_block(&mut self, req: &RequestEndBlock) -> ResponseEndBlock { - self.modules.end_block(&mut self.deliver_state, req) + pub fn end_block( + &mut self, + req: &RequestEndBlock, + ff_addr_balance: u64, + ) -> ResponseEndBlock { + self.modules + .end_block(&mut self.deliver_state, req, ff_addr_balance) } pub fn commit(&mut self, _req: &RequestCommit) -> ResponseCommit { diff --git a/src/components/contracts/baseapp/src/lib.rs b/src/components/contracts/baseapp/src/lib.rs index f04b52801..19c0c42af 100644 --- a/src/components/contracts/baseapp/src/lib.rs +++ b/src/components/contracts/baseapp/src/lib.rs @@ -9,9 +9,11 @@ mod app; pub mod extensions; mod modules; mod notify; +mod staking; use crate::modules::ModuleManager; use abci::Header; +use config::abci::global_cfg::CFG; use ethereum::BlockV0 as Block; use evm_precompile::{self, FindoraPrecompiles}; use fin_db::{FinDB, RocksDB}; @@ -31,10 +33,12 @@ use fp_traits::{ use fp_types::{actions::xhub::NonConfidentialOutput, actions::Action, crypto::Address}; use lazy_static::lazy_static; use ledger::data_model::Transaction as FindoraTransaction; +use ledger::LEDGER_TENDERMINT_BLOCK_HEIGHT; use notify::*; use parking_lot::RwLock; use primitive_types::{H160, H256, U256}; use ruc::{eg, Result}; +use std::sync::atomic::Ordering; use std::{borrow::BorrowMut, path::Path, sync::Arc}; use storage::state::{ChainState, ChainStateOpts}; use tracing::info; @@ -94,9 +98,30 @@ impl module_account::Config for BaseApp { parameter_types! { pub ChainId: u64 = *EVM_CAHIN_ID; - pub BlockGasLimit: U256 = U256::from(u32::max_value()); pub const BlockHashCount: u32 = 256; } +pub struct BlockGasLimit; +impl BlockGasLimit { + /// Returns the value of this parameter type. + pub fn get() -> U256 { + let td_height = LEDGER_TENDERMINT_BLOCK_HEIGHT.load(Ordering::Relaxed); + if td_height < CFG.checkpoint.max_gas_price_limit { + U256::from(u32::max_value()) + } else { + U256::from(100000000) + } + } +} +impl> ::fp_core::macros::Get for BlockGasLimit { + fn get() -> I { + let td_height = LEDGER_TENDERMINT_BLOCK_HEIGHT.load(Ordering::Relaxed); + if td_height < CFG.checkpoint.max_gas_price_limit { + I::from(U256::from(u32::max_value())) + } else { + I::from(U256::from(100000000)) + } + } +} impl module_ethereum::Config for BaseApp { type AccountAsset = module_account::App; diff --git a/src/components/contracts/baseapp/src/modules.rs b/src/components/contracts/baseapp/src/modules.rs index 8a3d4c79c..42d91dfdc 100644 --- a/src/components/contracts/baseapp/src/modules.rs +++ b/src/components/contracts/baseapp/src/modules.rs @@ -78,17 +78,25 @@ impl ModuleManager { &mut self, ctx: &mut Context, req: &RequestEndBlock, + ff_addr_balance: u64, ) -> ResponseEndBlock { let mut resp: ResponseEndBlock = Default::default(); // Note: adding new modules need to be updated. - self.account_module.end_block(ctx, req); - self.ethereum_module.end_block(ctx, req); - self.evm_module.end_block(ctx, req); - self.xhub_module.end_block(ctx, req); - let resp_template = self.template_module.end_block(ctx, req); - if !resp_template.validator_updates.is_empty() { - resp.validator_updates = resp_template.validator_updates; + self.account_module.end_block(ctx, req, ff_addr_balance); + let (mresp, burn_amount) = self.evm_module.end_block(ctx, req, 0); + if !mresp.validator_updates.is_empty() { + resp.validator_updates = mresp.validator_updates; } + if let Err(e) = module_account::App::::burn( + ctx, + &Address::from(self.evm_module.contracts.staking_address), + burn_amount, + ) { + tracing::warn!("module_account::App::::burn error: {:?}", e) + } + self.ethereum_module.end_block(ctx, req, 0); + self.xhub_module.end_block(ctx, req, 0); + self.template_module.end_block(ctx, req, 0); resp } diff --git a/src/components/contracts/baseapp/src/staking.rs b/src/components/contracts/baseapp/src/staking.rs new file mode 100644 index 000000000..a99f360f2 --- /dev/null +++ b/src/components/contracts/baseapp/src/staking.rs @@ -0,0 +1,381 @@ +use crate::BaseApp; +use config::abci::global_cfg::CFG; +use ethereum_types::{H160, U256}; +use fp_traits::{ + account::AccountAsset, + evm::{DecimalsMapping, EthereumDecimalsMapping}, +}; +use fp_types::crypto::Address; +use ledger::staking::{ + evm::EVMStaking, Delegation, DelegationState, Validator, BLOCK_HEIGHT_MAX, +}; +use module_evm::{ + system_contracts::SYSTEM_ADDR, DelegatorParam, UndelegationInfos, ValidatorParam, +}; +use ruc::{d, Result, RucResult}; +use sha3::{Digest, Keccak256}; +use std::{collections::BTreeMap, str::FromStr}; +use zei::xfr::sig::XfrPublicKey; + +impl EVMStaking for BaseApp { + fn import_validators( + &self, + validators: &[Validator], + delegations: &BTreeMap, + coinbase_balance: u64, + ) -> Result<()> { + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + let mut vs = vec![]; + { + for v in validators.iter() { + let begin_block = delegations + .get(&v.id) + .map(|d| d.start_height) + .unwrap_or(self.deliver_state.header.height as u64); + + vs.push(ValidatorParam { + td_addr: H160::from_slice(&v.td_addr), + td_pubkey: v.td_pubkey.clone(), + keytype: U256::from(2), + memo: serde_json::to_string(&v.memo).c(d!())?, + rate: mapping_rate(v.commission_rate), + staker: mapping_address(&v.id), + staker_pk: v.id.as_bytes().to_vec(), + power: U256::from(v.td_power), + begin_block: U256::from(begin_block), + }); + } + } + let power = vs.iter().map(|v| v.power.as_u128()).sum::(); + let amount = + EthereumDecimalsMapping::from_native_token(U256::from(power)).c(d!())?; + + let evm_staking_address = + H160::from_str(CFG.checkpoint.evm_staking_address.as_str()).c(d!())?; + + module_account::App::::mint( + &self.deliver_state, + &Address::from(evm_staking_address), + amount, + )?; + + if let Err(e) = + self.modules + .evm_module + .import_validators(&self.deliver_state, from, &vs) + { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "import_validators error:{:?}", e); + return Err(e); + } + + let mut ds: BTreeMap<(XfrPublicKey, H160), Vec<(u64, U256)>> = BTreeMap::new(); + { + for delegation in delegations.values() { + let public_key = delegation.receiver_pk.unwrap_or(delegation.id); + + for (validator, amount) in delegation.delegations.iter() { + let amount = U256::from(*amount); + ds.entry((public_key, mapping_address(validator))) + .and_modify(|am| { + am.push((delegation.end_height, amount)); + }) + .or_insert(vec![(delegation.end_height, amount)]); + } + } + } + let mut delegators = vec![]; + let mut undelegation_infos = vec![]; + { + for ((public_key, validator_address), value) in ds.iter() { + let mut bound_amount = U256::zero(); + let mut unbound_amount = U256::zero(); + + let delegator_address = mapping_address(public_key); + + for (end_height, amount) in value.iter() { + if BLOCK_HEIGHT_MAX == *end_height { + bound_amount = bound_amount.checked_add(*amount).c(d!())?; + } else { + unbound_amount = unbound_amount.checked_add(*amount).c(d!())?; + undelegation_infos.push(UndelegationInfos { + validator: *validator_address, + delegator: delegator_address, + amount: *amount, + height: U256::from(*end_height), + }); + } + } + delegators.push(DelegatorParam { + validator: *validator_address, + delegator: delegator_address, + delegator_pk: public_key.as_bytes().to_vec(), + bound_amount, + unbound_amount, + }); + } + } + + if let Err(e) = self.modules.evm_module.import_delegators( + &self.deliver_state, + from, + &delegators, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "import_delegators error:{:?}", e); + return Err(e); + } + if let Err(e) = self.modules.evm_module.import_undelegations( + &self.deliver_state, + from, + &undelegation_infos, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "import_undelegations error:{:?}", e); + return Err(e); + } + + let reward = delegations + .values() + .filter(|d| d.state == DelegationState::Bond) + .map(|d| { + ( + mapping_address(&d.receiver_pk.unwrap_or(d.id)), + d.rwd_amount, + ) + }) + .collect::>(); + if let Err(e) = + self.modules + .evm_module + .import_reward(&self.deliver_state, from, &reward) + { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "import_reward error:{:?}", e); + return Err(e); + } + if let Err(e) = self.modules.evm_module.import_coinbase_balance( + &self.deliver_state, + from, + coinbase_balance, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "import_coinbase_balance error:{:?}", e); + return Err(e); + } + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + Ok(()) + } + fn stake( + &self, + staker: &XfrPublicKey, + amount: u64, + td_addr: &[u8], + td_pubkey: Vec, + memo: String, + rate: [u64; 2], + ) -> Result<()> { + let staker_pk = staker.as_bytes().to_vec(); + let staker_address = mapping_address(staker); + + let amount = + EthereumDecimalsMapping::from_native_token(U256::from(amount)).c(d!())?; + + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + module_account::App::::mint( + &self.deliver_state, + &Address::from(from), + amount, + )?; + + if let Err(e) = self.modules.evm_module.stake( + &self.deliver_state, + from, + amount, + H160::from_slice(td_addr), + td_pubkey, + staker_address, + staker_pk, + memo, + mapping_rate(rate), + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "stake error:{:?}", e); + return Err(e); + } + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + Ok(()) + } + + fn delegate( + &self, + delegator: &XfrPublicKey, + amount: u64, + td_addr: &[u8], + ) -> Result<()> { + let delegator_pk = delegator.as_bytes().to_vec(); + let delegator_address = mapping_address(delegator); + + let amount = + EthereumDecimalsMapping::from_native_token(U256::from(amount)).c(d!())?; + + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + module_account::App::::mint( + &self.deliver_state, + &Address::from(from), + amount, + )?; + + if let Err(e) = self.modules.evm_module.delegate( + &self.deliver_state, + from, + H160::from_slice(td_addr), + delegator_address, + delegator_pk, + amount, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "delegate error:{:?}", e); + return Err(e); + } + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + + Ok(()) + } + + fn undelegate( + &self, + delegator: &XfrPublicKey, + td_addr: &[u8], + amount: u64, + ) -> Result<()> { + let delegator_address = mapping_address(delegator); + + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + let amount = + EthereumDecimalsMapping::from_native_token(U256::from(amount)).c(d!())?; + + if let Err(e) = self.modules.evm_module.undelegate( + &self.deliver_state, + from, + H160::from_slice(td_addr), + delegator_address, + amount, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "undelegate error:{:?}", e); + return Err(e); + }; + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + + Ok(()) + } + + fn update_validator( + &self, + staker: &XfrPublicKey, + td_address: &[u8], + memo: String, + rate: [u64; 2], + ) -> Result<()> { + let staker_address = mapping_address(staker); + let rate = mapping_rate(rate); + let validator = H160::from_slice(td_address); + + if let Err(e) = self.modules.evm_module.update_validator( + &self.deliver_state, + staker_address, + validator, + memo, + rate, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "update_validator error:{:?}", e); + return Err(e); + } + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + Ok(()) + } + + fn replace_delegator( + &self, + validator: &[u8], + staker: &XfrPublicKey, + new_staker_address: H160, + ) -> Result<()> { + let validator = H160::from_slice(validator); + let staker_address = mapping_address(staker); + + if let Err(e) = self.modules.evm_module.replace_delegator( + &self.deliver_state, + validator, + staker_address, + new_staker_address, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "replace_delegator error:{:?}", e); + return Err(e); + } + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + Ok(()) + } + fn claim(&self, td_addr: &[u8], delegator_pk: &XfrPublicKey) -> Result<()> { + let validator = H160::from_slice(td_addr); + let delegator = mapping_address(delegator_pk); + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + if let Err(e) = self.modules.evm_module.claim( + &self.deliver_state, + from, + validator, + delegator, + delegator_pk, + ) { + self.deliver_state.state.write().discard_session(); + self.deliver_state.db.write().discard_session(); + tracing::error!(target: "evm staking", "claim error:{:?}", e); + return Err(e); + } + + self.deliver_state.state.write().commit_session(); + self.deliver_state.db.write().commit_session(); + Ok(()) + } +} +fn mapping_rate(rate: [u64; 2]) -> U256 { + if rate[0] == 0 { + return U256::zero(); + } + + let deciamls = 1_000_000_u64; + U256::from(rate[0].saturating_mul(deciamls) / rate[1]) +} + +pub fn mapping_address(pk: &XfrPublicKey) -> H160 { + let result = Keccak256::digest(pk.as_bytes()); + H160::from_slice(&result.as_slice()[..20]) +} diff --git a/src/components/contracts/modules/account/src/impls.rs b/src/components/contracts/modules/account/src/impls.rs index 808aed138..94352bd80 100644 --- a/src/components/contracts/modules/account/src/impls.rs +++ b/src/components/contracts/modules/account/src/impls.rs @@ -2,7 +2,7 @@ use crate::{storage::*, App, Config}; use config::abci::global_cfg::CFG; use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; use fp_core::{account::SmartAccount, context::Context}; -use fp_storage::{Borrow, BorrowMut}; +use fp_storage::BorrowMut; use fp_traits::account::AccountAsset; use fp_types::crypto::Address; use primitive_types::{H160, U256}; @@ -10,7 +10,7 @@ use ruc::*; impl AccountAsset
for App { fn total_issuance(ctx: &Context) -> U256 { - TotalIssuance::get(ctx.state.read().borrow()).unwrap_or_default() + TotalIssuance::get(&ctx.state.read()).unwrap_or_default() } fn account_of( @@ -20,9 +20,9 @@ impl AccountAsset
for App { ) -> Option { let version = height.unwrap_or(0); if version == 0 { - AccountStore::get(ctx.state.read().borrow(), who) + AccountStore::get(&ctx.state.read(), who) } else { - AccountStore::get_ver(ctx.state.read().borrow(), who, version) + AccountStore::get_ver(&ctx.state.read(), who, version) } } @@ -207,7 +207,7 @@ impl AccountAsset
for App { } fn allowance(ctx: &Context, owner: &Address, spender: &Address) -> U256 { - Allowances::get(ctx.state.read().borrow(), owner, spender).unwrap_or_default() + Allowances::get(&ctx.state.read(), owner, spender).unwrap_or_default() } fn approve( diff --git a/src/components/contracts/modules/ethereum/src/impls.rs b/src/components/contracts/modules/ethereum/src/impls.rs index 7884c44da..f28cde195 100644 --- a/src/components/contracts/modules/ethereum/src/impls.rs +++ b/src/components/contracts/modules/ethereum/src/impls.rs @@ -16,7 +16,7 @@ use fp_core::{ }; use fp_events::Event; use fp_evm::{BlockId, CallOrCreateInfo, Runner, TransactionStatus}; -use fp_storage::{Borrow, BorrowMut}; +use fp_storage::BorrowMut; use fp_types::crypto::Address; use fp_types::{ actions::evm as EvmAction, @@ -472,13 +472,13 @@ impl App { id: Option, ) -> Option> { let hash = HA256::new(Self::block_hash(ctx, id).unwrap_or_default()); - CurrentTransactionStatuses::get(ctx.db.read().borrow(), &hash) + CurrentTransactionStatuses::get(&ctx.db.read(), &hash) } /// Get the block with given block id. pub fn current_block(&self, ctx: &Context, id: Option) -> Option { let hash = HA256::new(Self::block_hash(ctx, id).unwrap_or_default()); - CurrentBlock::get(ctx.db.read().borrow(), &hash) + CurrentBlock::get(&ctx.db.read(), &hash) } /// Get receipts with given block id. @@ -488,12 +488,12 @@ impl App { id: Option, ) -> Option> { let hash = HA256::new(Self::block_hash(ctx, id).unwrap_or_default()); - CurrentReceipts::get(ctx.db.read().borrow(), &hash) + CurrentReceipts::get(&ctx.db.read(), &hash) } /// Get current block hash pub fn current_block_hash(ctx: &Context) -> Option { - if let Some(number) = CurrentBlockNumber::get(ctx.db.read().borrow()) { + if let Some(number) = CurrentBlockNumber::get(&ctx.db.read()) { Self::get_hash(ctx, number) } else { None @@ -502,7 +502,7 @@ impl App { /// Get current block number pub fn current_block_number(ctx: &Context) -> Option { - CurrentBlockNumber::get(ctx.db.read().borrow()) + CurrentBlockNumber::get(&ctx.db.read()) } /// Get header hash of given block id. @@ -519,7 +519,7 @@ impl App { /// The index of the transaction in the block pub fn transaction_index(ctx: &Context, hash: H256) -> Option<(U256, u32)> { - TransactionIndex::get(ctx.db.read().borrow(), &HA256::new(hash)) + TransactionIndex::get(&ctx.db.read(), &HA256::new(hash)) } fn logs_bloom(logs: Vec, bloom: &mut Bloom) { @@ -532,7 +532,7 @@ impl App { } fn get_hash(ctx: &Context, number: U256) -> Option { - if let Some(hash) = BlockHash::get(ctx.db.read().borrow(), &number) { + if let Some(hash) = BlockHash::get(&ctx.db.read(), &number) { return Some(hash.h256()); } None @@ -541,7 +541,7 @@ impl App { pub fn migrate(ctx: &mut Context) -> Result<()> { //Migrate existing transaction indices from chain-state to rocksdb. let txn_idxs: Vec<(HA256, (U256, u32))> = - TransactionIndex::iterate(ctx.state.read().borrow()); + TransactionIndex::iterate(&ctx.state.read()); let txn_idxs_cnt = txn_idxs.len(); for idx in txn_idxs { diff --git a/src/components/contracts/modules/ethereum/src/lib.rs b/src/components/contracts/modules/ethereum/src/lib.rs index b27d80e79..02ff0eafc 100644 --- a/src/components/contracts/modules/ethereum/src/lib.rs +++ b/src/components/contracts/modules/ethereum/src/lib.rs @@ -134,7 +134,8 @@ impl AppModule for App { &mut self, ctx: &mut Context, req: &RequestEndBlock, - ) -> ResponseEndBlock { + _ff_addr_balance: u64, + ) -> (ResponseEndBlock, U256) { let _ = ruc::info!(self.store_block(ctx, U256::from(req.height))); Default::default() } @@ -201,12 +202,22 @@ impl ValidateUnsigned for App { C::BlockGasLimit::get() ))); } + let mut flag = false; + if ctx.header.height < CFG.checkpoint.max_gas_price_limit { + if transaction.gas_price < C::FeeCalculator::min_gas_price() { + flag = true; + } + } else if transaction.gas_price < C::FeeCalculator::min_gas_price() + || transaction.gas_price > C::FeeCalculator::max_gas_price() + { + flag = true; + } - if transaction.gas_price < C::FeeCalculator::min_gas_price() { + if flag { return Err(eg!(format!( - "InvalidGasPrice: got {}, but the minimum gas price is {}", + "InvalidGasPrice: got {}, but the minimum gas price is {}, max gas price is {}", transaction.gas_price, - C::FeeCalculator::min_gas_price() + C::FeeCalculator::min_gas_price(), C::FeeCalculator::max_gas_price() ))); } diff --git a/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs b/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs index 958886887..e9764f8f2 100644 --- a/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs +++ b/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs @@ -135,7 +135,7 @@ fn test_abci_deliver_tx() { fn test_abci_end_block() { let mut req = RequestEndBlock::default(); req.height = 3; - let _ = BASE_APP.lock().unwrap().end_block(&req); + let _ = BASE_APP.lock().unwrap().end_block(&req, 0); } fn test_abci_commit() { diff --git a/src/components/contracts/modules/evm/Cargo.toml b/src/components/contracts/modules/evm/Cargo.toml index 2762fcc0f..4f57c3c6e 100644 --- a/src/components/contracts/modules/evm/Cargo.toml +++ b/src/components/contracts/modules/evm/Cargo.toml @@ -25,6 +25,7 @@ sha3 = { version = "0.10", default-features = false } hex = "0.4.2" ethabi = "17.1.0" zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +protobuf = "2.16" # primitives, don't depend on any modules fp-core = { path = "../../primitives/core" } @@ -38,14 +39,13 @@ storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5 fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } ledger = { path = "../../../../ledger" } enterprise-web3 = { path = "../../primitives/enterprise-web3" } - +module-ethereum = { path = "../ethereum" } [dev-dependencies] baseapp = { path = "../../baseapp" } fp-mocks = { path = "../../primitives/mocks" } hex = "0.4.2" module-account = { path = "../account" } -module-ethereum = { path = "../ethereum" } serde_json = "1.0.64" [features] diff --git a/src/components/contracts/modules/evm/contracts/EVMStaking.abi.json b/src/components/contracts/modules/evm/contracts/EVMStaking.abi.json new file mode 100644 index 000000000..586c15f28 --- /dev/null +++ b/src/components/contracts/modules/evm/contracts/EVMStaking.abi.json @@ -0,0 +1,794 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "CoinbaseMint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MintOps", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getClaimOnContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTriggerOnContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "getValidator", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "enum IBaseEnum.PublicKeyType", + "name": "", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getValidatorsList", + "outputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "internalType": "enum IBaseEnum.PublicKeyType", + "name": "ty", + "type": "uint8" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "power", + "type": "uint256" + } + ], + "internalType": "struct IValidators.ValidatorInfo[]", + "name": "res", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "importCoinBase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "delegator_pk", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "boundAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unboundAmount", + "type": "uint256" + } + ], + "internalType": "struct IBaseEnum.DelegatorParam[]", + "name": "dp", + "type": "tuple[]" + } + ], + "name": "importDelegators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct IBaseEnum.RewardParam[]", + "name": "rp", + "type": "tuple[]" + } + ], + "name": "importReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address payable", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "height", + "type": "uint256" + } + ], + "internalType": "struct IBaseEnum.UndelegationParam[]", + "name": "udp", + "type": "tuple[]" + } + ], + "name": "importUndelegations", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "td_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "internalType": "enum IBaseEnum.PublicKeyType", + "name": "ty", + "type": "uint8" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "bytes", + "name": "staker_pk", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "power", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "beginBlock", + "type": "uint256" + } + ], + "internalType": "struct IBaseEnum.ValidatorParam[]", + "name": "vp", + "type": "tuple[]" + } + ], + "name": "importValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + }, + { + "internalType": "address", + "name": "newDelegator", + "type": "address" + } + ], + "name": "replaceDelegator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rewardAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setRewardAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setStakingAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setSystemAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "stakingAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "systemAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + } + ], + "name": "systemClaim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "delegator_pk", + "type": "bytes" + } + ], + "name": "systemDelegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + }, + { + "internalType": "address", + "name": "oldDelegator", + "type": "address" + }, + { + "internalType": "address", + "name": "newDelegator", + "type": "address" + } + ], + "name": "systemReplaceDelegator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "public_key", + "type": "bytes" + }, + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "bytes", + "name": "staker_pk", + "type": "bytes" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "systemStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "systemUndelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "systemUpdateValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "address[]", + "name": "voted", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "unvoted", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "byztine", + "type": "address[]" + }, + { + "internalType": "enum IBaseEnum.ByztineBehavior[]", + "name": "behavior", + "type": "uint8[]" + }, + { + "internalType": "uint256", + "name": "preIssueAmount", + "type": "uint256" + } + ], + "name": "trigger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "updateValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/contracts/PrismXXProxy.bytecode b/src/components/contracts/modules/evm/contracts/PrismXXProxy.bytecode deleted file mode 100644 index 13d32ac2f..000000000 --- a/src/components/contracts/modules/evm/contracts/PrismXXProxy.bytecode +++ /dev/null @@ -1 +0,0 @@ -0x608060405234801561001057600080fd5b5061395c806100206000396000f3fe6080604052600436106100f75760003560e01c80638129fc1c1161008a578063d905b00911610059578063d905b009146102d8578063efe685b414610315578063f2fde38b1461033e578063fabeb15814610367576100fe565b80638129fc1c146102445780638da5cb5b1461025b578063a758acc214610286578063b9322ce1146102af576100fe565b80633eb0edcf116100c65780633eb0edcf146101ab5780635e436673146101e857806367681aed14610204578063715018a61461022d576100fe565b8063044edb6f146101035780630505dce01461012e5780630b392516146101595780633e4a55e914610182576100fe565b366100fe57005b600080fd5b34801561010f57600080fd5b50610118610390565b60405161012591906129f0565b60405180910390f35b34801561013a57600080fd5b506101436103b6565b60405161015091906129f0565b60405180910390f35b34801561016557600080fd5b50610180600480360381019061017b9190612414565b6103dc565b005b34801561018e57600080fd5b506101a960048036038101906101a4919061250c565b6106cf565b005b3480156101b757600080fd5b506101d260048036038101906101cd91906122ea565b610f78565b6040516101df9190612cc5565b60405180910390f35b61020260048036038101906101fd91906125bc565b610fae565b005b34801561021057600080fd5b5061022b600480360381019061022691906123a8565b611087565b005b34801561023957600080fd5b50610242611377565b005b34801561025057600080fd5b5061025961138b565b005b34801561026757600080fd5b506102706114d1565b60405161027d91906129f0565b60405180910390f35b34801561029257600080fd5b506102ad60048036038101906102a8919061233c565b6114fb565b005b3480156102bb57600080fd5b506102d660048036038101906102d191906122ea565b611758565b005b3480156102e457600080fd5b506102ff60048036038101906102fa9190612494565b6117a4565b60405161030c9190612cc5565b60405180910390f35b34801561032157600080fd5b5061033c600480360381019061033791906123a8565b6117dd565b005b34801561034a57600080fd5b50610365600480360381019061036091906122ea565b611bdd565b005b34801561037357600080fd5b5061038e600480360381019061038991906122ea565b611c61565b005b609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073ffffffffffffffffffffffffffffffffffffffff16609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561046e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610465906130ad565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610500576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104f7906130cd565b60405180910390fd5b600061050c86846117a4565b90506000609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663d0e5390d8389876040518463ffffffff1660e01b815260040161057293929190612d09565b600060405180830381600087803b15801561058c57600080fd5b505af11580156105a0573d6000803e3d6000fd5b5050505060003390507faae31ca36c1ef3c9daa9d5efff8c47306109c0f7cf997e61d766ba15d27e071e838888876000806040516105e396959493929190612d9c565b60405180910390a16000609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff166303b5b1e78a8489896040518563ffffffff1660e01b81526004016106519493929190612b3a565b600060405180830381600087803b15801561066b57600080fd5b505af115801561067f573d6000803e3d6000fd5b505050507fd878696e9c4484a8cb53f7ddf7f10a3583d67db9a664da6cbabc11f67e55c69789838a8a8a8a6040516106bc96959493929190612a59565b60405180910390a1505050505050505050565b61200073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461073f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073690612f0d565b60405180910390fd5b83609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156107d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107c89061300d565b60405180910390fd5b609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085990612f8d565b60405180910390fd5b6000609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008173ffffffffffffffffffffffffffffffffffffffff1663979a9b5e8c6040518263ffffffff1660e01b81526004016108eb9190612cc5565b60206040518083038186803b15801561090357600080fd5b505afa158015610917573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093b9190612601565b905060016002811115610977577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8160028111156109b0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b1415610b41576000808373ffffffffffffffffffffffffffffffffffffffff1663d87e0e328e6040518263ffffffff1660e01b81526004016109f29190612cc5565b604080518083038186803b158015610a0957600080fd5b505afa158015610a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4191906124d0565b9150915060018914610a88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7f90612fed565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff166365321187838c848c8c6040518663ffffffff1660e01b8152600401610ac9959493929190612aec565b600060405180830381600087803b158015610ae357600080fd5b505af1158015610af7573d6000803e3d6000fd5b505050507fe64394cd094bedd1856251d4913d477b47c8d32a2bdd1533c114be19703f5ec5828d8d8d85604051610b32959493929190612bdb565b60405180910390a15050610f6b565b600280811115610b7a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b816002811115610bb3577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b1415610d05576000808373ffffffffffffffffffffffffffffffffffffffff1663c04fd06c8e6040518263ffffffff1660e01b8152600401610bf59190612cc5565b604080518083038186803b158015610c0c57600080fd5b505afa158015610c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4491906124d0565b915091508473ffffffffffffffffffffffffffffffffffffffff166319a18ca0838c848d8d8d6040518763ffffffff1660e01b8152600401610c8b96959493929190612b7f565b600060405180830381600087803b158015610ca557600080fd5b505af1158015610cb9573d6000803e3d6000fd5b505050507f75ea7effe223920090a1b6c39a5ead279cd393aa7053e00eef9cb850042964e8828d8d8d858e604051610cf696959493929190612c29565b60405180910390a15050610f6a565b60008273ffffffffffffffffffffffffffffffffffffffff16631996e4f48d6040518263ffffffff1660e01b8152600401610d409190612cc5565b60206040518083038186803b158015610d5857600080fd5b505afa158015610d6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d909190612313565b90506000819050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610e07576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dfe9061304d565b60405180910390fd5b60008173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4f57600080fd5b505afa158015610e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e87919061262a565b905060008a905060068260ff161115610eb357610eb08b600684610eab919061332c565b611cad565b90505b8673ffffffffffffffffffffffffffffffffffffffff16633d2b0e0d858e848e8e6040518663ffffffff1660e01b8152600401610ef4959493929190612aec565b600060405180830381600087803b158015610f0e57600080fd5b505af1158015610f22573d6000803e3d6000fd5b505050507f9774afbdcd0096ddaa798a7aa585dde8bb80b0a0d6dd8127a111d7f5f890df78848f8f8f85604051610f5d959493929190612bdb565b60405180910390a1505050505b5b5050505050505050505050565b6000607760001b82604051602001610f91929190612ce0565b604051602081830303815290604052805190602001209050919050565b600034905080610fbf82600c611cd4565b14610fff576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ff69061306d565b60405180910390fd5b7faae31ca36c1ef3c9daa9d5efff8c47306109c0f7cf997e61d766ba15d27e071e6000801b8484846006600060405161103d96959493929190612df8565b60405180910390a17fe188820b6d0ed06fa9f2ecef45ebf5afb68b48e4df0db212cae1b4db0e947d943384848460405161107a9493929190612c85565b60405180910390a1505050565b600073ffffffffffffffffffffffffffffffffffffffff16609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611119576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611110906130ad565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156111ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111a2906130cd565b60405180910390fd5b60006111b785836117a4565b90506000609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff166305c62fe38388866040518463ffffffff1660e01b815260040161121d93929190612d09565b600060405180830381600087803b15801561123757600080fd5b505af115801561124b573d6000803e3d6000fd5b5050505060003390507faae31ca36c1ef3c9daa9d5efff8c47306109c0f7cf997e61d766ba15d27e071e83878760016000600160405161129096959493929190612d40565b60405180910390a16000609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663554f103b8984886040518463ffffffff1660e01b81526004016112fc93929190612ab5565b600060405180830381600087803b15801561131657600080fd5b505af115801561132a573d6000803e3d6000fd5b505050507fe684696f780e170df9f475819b4580ee78c6a3bb4453793f8719afad24f80c4a8883898989604051611365959493929190612a0b565b60405180910390a15050505050505050565b61137f611d0c565b6113896000611d8a565b565b60008060019054906101000a900460ff161590508080156113bc5750600160008054906101000a900460ff1660ff16105b806113e957506113cb30611e50565b1580156113e85750600160008054906101000a900460ff1660ff16145b5b611428576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161141f90612fad565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015611465576001600060016101000a81548160ff0219169083151502179055505b61146d611e73565b611475611ec4565b80156114ce5760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249860016040516114c59190612eb0565b60405180910390a15b50565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b61200073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461156b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161156290612f0d565b60405180910390fd5b83609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156115fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115f49061300d565b60405180910390fd5b609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561168e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161168590612f8d565b60405180910390fd5b6116ad8573ffffffffffffffffffffffffffffffffffffffff16611e50565b156117275761172183838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050858773ffffffffffffffffffffffffffffffffffffffff16611f259092919063ffffffff16565b50611751565b611750848673ffffffffffffffffffffffffffffffffffffffff16611f5490919063ffffffff16565b5b5050505050565b611760611d0c565b80609760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600260001b83836040516020016117bf93929190612d09565b60405160208183030381529060405280519060200120905092915050565b600073ffffffffffffffffffffffffffffffffffffffff16609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561186f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611866906130ad565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611901576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118f8906130cd565b60405180910390fd5b600084905060008173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561194e57600080fd5b505afa158015611962573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611986919061262a565b90506000819050600084905060068360ff161115611a1257846119b5866006866119b0919061332c565b611cd4565b146119f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119ec9061306d565b60405180910390fd5b60069150611a0f85600685611a0a919061332c565b612048565b90505b6000611a1d89610f78565b90506000609860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663ed7e205b838c6040518363ffffffff1660e01b8152600401611a81929190612ce0565b600060405180830381600087803b158015611a9b57600080fd5b505af1158015611aaf573d6000803e3d6000fd5b5050505060003390507faae31ca36c1ef3c9daa9d5efff8c47306109c0f7cf997e61d766ba15d27e071e838b8b87896000604051611af296959493929190612e54565b60405180910390a16000609760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663025993488d848c6040518463ffffffff1660e01b8152600401611b5e93929190612ab5565b600060405180830381600087803b158015611b7857600080fd5b505af1158015611b8c573d6000803e3d6000fd5b505050507fb29d49ecfb85df38addb1eadbfb8da67f791ee50336005c3988d5eda55fc56248c838d8d8d604051611bc7959493929190612a0b565b60405180910390a1505050505050505050505050565b611be5611d0c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611c55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c4c90612eed565b60405180910390fd5b611c5e81611d8a565b50565b611c69611d0c565b80609860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008082600a611cbd91906131b4565b90508084611ccb91906132d2565b91505092915050565b60008082600a611ce491906131b4565b905060008185611cf49190613130565b90508181611d0291906132d2565b9250505092915050565b611d14612075565b73ffffffffffffffffffffffffffffffffffffffff16611d326114d1565b73ffffffffffffffffffffffffffffffffffffffff1614611d88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d7f90612fcd565b60405180910390fd5b565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081603360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600060019054906101000a900460ff16611ec2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611eb99061308d565b60405180910390fd5b565b600060019054906101000a900460ff16611f13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f0a9061308d565b60405180910390fd5b611f23611f1e612075565b611d8a565b565b6060611f4b8484846040518060600160405280602981526020016138fe6029913961207d565b90509392505050565b80471015611f97576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f8e90612f4d565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff1682604051611fbd906129db565b60006040518083038185875af1925050503d8060008114611ffa576040519150601f19603f3d011682016040523d82523d6000602084013e611fff565b606091505b5050905080612043576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161203a90612f2d565b60405180910390fd5b505050565b60008082600a61205891906131b4565b9050600081856120689190613130565b9050809250505092915050565b600033905090565b6060824710156120c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120b990612f6d565b60405180910390fd5b6120cb85611e50565b61210a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121019061302d565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161213391906129c4565b60006040518083038185875af1925050503d8060008114612170576040519150601f19603f3d011682016040523d82523d6000602084013e612175565b606091505b5091509150612185828286612191565b92505050949350505050565b606083156121a1578290506121f1565b6000835111156121b45782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121e89190612ecb565b60405180910390fd5b9392505050565b6000813590506122078161387a565b92915050565b60008151905061221c8161387a565b92915050565b60008135905061223181613891565b92915050565b600081359050612246816138a8565b92915050565b60008083601f84011261225e57600080fd5b8235905067ffffffffffffffff81111561227757600080fd5b60208301915083600182028301111561228f57600080fd5b9250929050565b6000815190506122a5816138bf565b92915050565b6000813590506122ba816138cf565b92915050565b6000815190506122cf816138cf565b92915050565b6000815190506122e4816138e6565b92915050565b6000602082840312156122fc57600080fd5b600061230a848285016121f8565b91505092915050565b60006020828403121561232557600080fd5b60006123338482850161220d565b91505092915050565b6000806000806060858703121561235257600080fd5b600061236087828801612222565b9450506020612371878288016122ab565b935050604085013567ffffffffffffffff81111561238e57600080fd5b61239a8782880161224c565b925092505092959194509250565b600080600080606085870312156123be57600080fd5b60006123cc878288016121f8565b945050602085013567ffffffffffffffff8111156123e957600080fd5b6123f58782880161224c565b93509350506040612408878288016122ab565b91505092959194509250565b60008060008060006080868803121561242c57600080fd5b600061243a888289016121f8565b955050602086013567ffffffffffffffff81111561245757600080fd5b6124638882890161224c565b94509450506040612476888289016122ab565b9250506060612487888289016122ab565b9150509295509295909350565b600080604083850312156124a757600080fd5b60006124b5858286016121f8565b92505060206124c6858286016122ab565b9150509250929050565b600080604083850312156124e357600080fd5b60006124f18582860161220d565b9250506020612502858286016122c0565b9150509250929050565b600080600080600080600060a0888a03121561252757600080fd5b60006125358a828b01612237565b975050602088013567ffffffffffffffff81111561255257600080fd5b61255e8a828b0161224c565b965096505060406125718a828b016121f8565b94505060606125828a828b016122ab565b935050608088013567ffffffffffffffff81111561259f57600080fd5b6125ab8a828b0161224c565b925092505092959891949750929550565b600080602083850312156125cf57600080fd5b600083013567ffffffffffffffff8111156125e957600080fd5b6125f58582860161224c565b92509250509250929050565b60006020828403121561261357600080fd5b600061262184828501612296565b91505092915050565b60006020828403121561263c57600080fd5b600061264a848285016122d5565b91505092915050565b61265c81613360565b82525050565b61266b81613384565b82525050565b600061267d8385613103565b935061268a83858461341f565b612693836134bf565b840190509392505050565b60006126a9826130ed565b6126b38185613114565b93506126c381856020860161342e565b80840191505092915050565b6126d8816133c5565b82525050565b6126e7816133d7565b82525050565b6126f6816133e9565b82525050565b612705816133fb565b82525050565b6127148161340d565b82525050565b6000612725826130f8565b61272f818561311f565b935061273f81856020860161342e565b612748816134bf565b840191505092915050565b600061276060268361311f565b915061276b826134dd565b604082019050919050565b600061278360228361311f565b915061278e8261352c565b604082019050919050565b60006127a6603a8361311f565b91506127b18261357b565b604082019050919050565b60006127c9601d8361311f565b91506127d4826135ca565b602082019050919050565b60006127ec60268361311f565b91506127f7826135f3565b604082019050919050565b600061280f60208361311f565b915061281a82613642565b602082019050919050565b6000612832602e8361311f565b915061283d8261366b565b604082019050919050565b600061285560208361311f565b9150612860826136ba565b602082019050919050565b6000612878601f8361311f565b9150612883826136e3565b602082019050919050565b600061289b60218361311f565b91506128a68261370c565b604082019050919050565b60006128be600083613114565b91506128c98261375b565b600082019050919050565b60006128e1601d8361311f565b91506128ec8261375e565b602082019050919050565b600061290460188361311f565b915061290f82613787565b602082019050919050565b600061292760118361311f565b9150612932826137b0565b602082019050919050565b600061294a602b8361311f565b9150612955826137d9565b604082019050919050565b600061296d601a8361311f565b915061297882613828565b602082019050919050565b6000612990601b8361311f565b915061299b82613851565b602082019050919050565b6129af816133ae565b82525050565b6129be816133b8565b82525050565b60006129d0828461269e565b915081905092915050565b60006129e6826128b1565b9150819050919050565b6000602082019050612a056000830184612653565b92915050565b6000608082019050612a206000830188612653565b612a2d6020830187612653565b8181036040830152612a40818587612671565b9050612a4f60608301846129a6565b9695505050505050565b600060a082019050612a6e6000830189612653565b612a7b6020830188612653565b8181036040830152612a8e818688612671565b9050612a9d60608301856129a6565b612aaa60808301846129a6565b979650505050505050565b6000606082019050612aca6000830186612653565b612ad76020830185612653565b612ae460408301846129a6565b949350505050565b6000608082019050612b016000830188612653565b612b0e6020830187612653565b612b1b60408301866129a6565b8181036060830152612b2e818486612671565b90509695505050505050565b6000608082019050612b4f6000830187612653565b612b5c6020830186612653565b612b6960408301856129a6565b612b7660608301846129a6565b95945050505050565b600060a082019050612b946000830189612653565b612ba16020830188612653565b612bae60408301876129a6565b612bbb60608301866129a6565b8181036080830152612bce818486612671565b9050979650505050505050565b6000608082019050612bf06000830188612653565b8181036020830152612c03818688612671565b9050612c126040830185612653565b612c1f60608301846129a6565b9695505050505050565b600060a082019050612c3e6000830189612653565b8181036020830152612c51818789612671565b9050612c606040830186612653565b612c6d60608301856129a6565b612c7a60808301846129a6565b979650505050505050565b6000606082019050612c9a6000830187612653565b8181036020830152612cad818587612671565b9050612cbc60408301846129a6565b95945050505050565b6000602082019050612cda6000830184612662565b92915050565b6000604082019050612cf56000830185612662565b612d026020830184612653565b9392505050565b6000606082019050612d1e6000830186612662565b612d2b6020830185612653565b612d3860408301846129a6565b949350505050565b600060a082019050612d556000830189612662565b8181036020830152612d68818789612671565b9050612d7760408301866126ed565b612d8460608301856126de565b612d9160808301846126ed565b979650505050505050565b600060a082019050612db16000830189612662565b8181036020830152612dc4818789612671565b9050612dd360408301866129a6565b612de060608301856126de565b612ded60808301846126cf565b979650505050505050565b600060a082019050612e0d6000830189612662565b8181036020830152612e20818789612671565b9050612e2f60408301866129a6565b612e3c606083018561270b565b612e4960808301846126cf565b979650505050505050565b600060a082019050612e696000830189612662565b8181036020830152612e7c818789612671565b9050612e8b60408301866129a6565b612e9860608301856129b5565b612ea560808301846126cf565b979650505050505050565b6000602082019050612ec560008301846126fc565b92915050565b60006020820190508181036000830152612ee5818461271a565b905092915050565b60006020820190508181036000830152612f0681612753565b9050919050565b60006020820190508181036000830152612f2681612776565b9050919050565b60006020820190508181036000830152612f4681612799565b9050919050565b60006020820190508181036000830152612f66816127bc565b9050919050565b60006020820190508181036000830152612f86816127df565b9050919050565b60006020820190508181036000830152612fa681612802565b9050919050565b60006020820190508181036000830152612fc681612825565b9050919050565b60006020820190508181036000830152612fe681612848565b9050919050565b600060208201905081810360008301526130068161286b565b9050919050565b600060208201905081810360008301526130268161288e565b9050919050565b60006020820190508181036000830152613046816128d4565b9050919050565b60006020820190508181036000830152613066816128f7565b9050919050565b600060208201905081810360008301526130868161291a565b9050919050565b600060208201905081810360008301526130a68161293d565b9050919050565b600060208201905081810360008301526130c681612960565b9050919050565b600060208201905081810360008301526130e681612983565b9050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600061313b826133ae565b9150613146836133ae565b92508261315657613155613490565b5b828204905092915050565b6000808291508390505b60018511156131ab5780860481111561318757613186613461565b5b60018516156131965780820291505b80810290506131a4856134d0565b945061316b565b94509492505050565b60006131bf826133ae565b91506131ca836133b8565b92506131f77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846131ff565b905092915050565b60008261320f57600190506132cb565b8161321d57600090506132cb565b8160018114613233576002811461323d5761326c565b60019150506132cb565b60ff84111561324f5761324e613461565b5b8360020a91508482111561326657613265613461565b5b506132cb565b5060208310610133831016604e8410600b84101617156132a15782820a90508381111561329c5761329b613461565b5b6132cb565b6132ae8484846001613161565b925090508184048111156132c5576132c4613461565b5b81810290505b9392505050565b60006132dd826133ae565b91506132e8836133ae565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561332157613320613461565b5b828202905092915050565b6000613337826133b8565b9150613342836133b8565b92508282101561335557613354613461565b5b828203905092915050565b600061336b8261338e565b9050919050565b600061337d8261338e565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60006133d0826133ae565b9050919050565b60006133e2826133b8565b9050919050565b60006133f4826133ae565b9050919050565b6000613406826133b8565b9050919050565b6000613418826133b8565b9050919050565b82818337600083830152505050565b60005b8381101561344c578082015181840152602081019050613431565b8381111561345b576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000601f19601f8301169050919050565b60008160011c9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f4f6e6c792073797374656d2063616e2063616c6c20746869732066756e63746960008201527f6f6e000000000000000000000000000000000000000000000000000000000000602082015250565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260008201527f6563697069656e74206d61792068617665207265766572746564000000000000602082015250565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000600082015250565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b7f7461726765742061646472657373206d757374206e6f74206265206173736574600082015250565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f4572726f723a2041737365742074797065206973206e6f742045524337323100600082015250565b7f7461726765742061646472657373206d757374206e6f74206265206c6564676560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b50565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b7f41737365742074797065206d7573742072656769737465640000000000000000600082015250565b7f6c6f77203132206d75737420626520302e000000000000000000000000000000600082015250565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b7f507269736d206173736574206d75737420626520696e6974616c000000000000600082015250565b7f507269736d206c6564676572206d75737420626520696e6974616c0000000000600082015250565b61388381613360565b811461388e57600080fd5b50565b61389a81613372565b81146138a557600080fd5b50565b6138b181613384565b81146138bc57600080fd5b50565b600381106138cc57600080fd5b50565b6138d8816133ae565b81146138e357600080fd5b50565b6138ef816133b8565b81146138fa57600080fd5b5056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220769bc877bb4a3dfd5f11463d777bfa1b5ac0fcc9a4a75524cfdc529196ec3fa164736f6c63430008040033 \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/Cargo.toml b/src/components/contracts/modules/evm/precompile/Cargo.toml index a56e57b1f..875e9a0cf 100644 --- a/src/components/contracts/modules/evm/precompile/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/Cargo.toml @@ -16,6 +16,8 @@ evm-precompile-frc20 = {path = "./frc20"} evm-precompile-modexp = {path = "./modexp"} evm-precompile-sha3fips = {path = "./sha3fips"} evm-precompile-anemoi = {path = "./anemoi"} +evm-precompile-blake2 = {path = "./blake2"} +evm-precompile-bn128 = {path = "./bn128"} fp-core = {path = "../../../primitives/core"} module-evm = {path = "../../../modules/evm"} parking_lot = "0.12" diff --git a/src/components/contracts/modules/evm/precompile/blake2/Cargo.toml b/src/components/contracts/modules/evm/precompile/blake2/Cargo.toml new file mode 100644 index 000000000..cc579abf8 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/blake2/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "evm-precompile-blake2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +evm = { version = "0.35.0", default-features = false, features = ["with-serde"] } +evm-precompile-utils = { path = "../utils"} +tracing = "0.1" +module-evm = { path = "../../../../modules/evm"} +num_enum = { version = "0.5.4", default-features = false } + +[dev-dependencies] +baseapp = { path = "../../../../baseapp" } +fp-mocks = { path = "../../../../primitives/mocks" } +ethereum-types = { version = "0.13.1", default-features = false } +hex = "0.4" + \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/blake2/src/eip_152.rs b/src/components/contracts/modules/evm/precompile/blake2/src/eip_152.rs new file mode 100644 index 000000000..b30b475eb --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/blake2/src/eip_152.rs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Findora. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) +/// There are 10 16-byte arrays - one for each round +/// the entries are calculated from the sigma constants. +const SIGMA: [[usize; 16]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], +]; + +/// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 +/// for details. +const IV: [u64; 8] = [ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, +]; + +#[inline(always)] +/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 +fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { + v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); + v[d] = (v[d] ^ v[a]).rotate_right(32); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(24); + v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); + v[d] = (v[d] ^ v[a]).rotate_right(16); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(63); +} + +/// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 +/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final +/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first +/// parameter is modified by the function. +pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { + let mut v = [0u64; 16]; + v[..h.len()].copy_from_slice(h); // First half from state. + v[h.len()..].copy_from_slice(&IV); // Second half from IV. + + v[12] ^= t[0]; + v[13] ^= t[1]; + + + + if f { + v[14] = !v[14] // Invert all bits if the last-block-flag is set. + } + for i in 0..rounds { + // Message word selection permutation for this round. + let s = &SIGMA[i % 10]; + g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } +} diff --git a/src/components/contracts/modules/evm/precompile/blake2/src/lib.rs b/src/components/contracts/modules/evm/precompile/blake2/src/lib.rs new file mode 100644 index 000000000..69652059b --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/blake2/src/lib.rs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Findora. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod eip_152; + +use evm::executor::stack::{PrecompileFailure, PrecompileOutput}; +use evm::{Context, ExitError, ExitSucceed}; +use module_evm::precompile::{FinState, Precompile, PrecompileId, PrecompileResult}; + +pub struct Blake2F; + +impl PrecompileId for Blake2F { + fn contract_id() -> u64 { + 0x09 + } +} + +impl Blake2F { + const GAS_COST_PER_ROUND: u64 = 1; // https://eips.ethereum.org/EIPS/eip-152#gas-costs-and-benchmarks +} + +impl Precompile for Blake2F { + /// Format of `input`: + /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] + fn execute( + input: &[u8], + target_gas: Option, + _context: &Context, + _state: &FinState, + ) -> PrecompileResult { + const BLAKE2_F_ARG_LEN: usize = 213; + + if input.len() != BLAKE2_F_ARG_LEN { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "input length for Blake2 F precompile should be exactly 213 bytes" + .into(), + ), + }); + } + + let mut rounds_buf: [u8; 4] = [0; 4]; + rounds_buf.copy_from_slice(&input[0..4]); + let rounds: u32 = u32::from_be_bytes(rounds_buf); + + let gas_cost: u64 = (rounds as u64) * Blake2F::GAS_COST_PER_ROUND; + if let Some(gas_left) = target_gas { + if gas_left < gas_cost { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); + } + } + + // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE + + let mut h_buf: [u8; 64] = [0; 64]; + h_buf.copy_from_slice(&input[4..68]); + let mut h = [0u64; 8]; + let mut ctr = 0; + for state_word in &mut h { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&h_buf[(ctr * 8)..(ctr + 1) * 8]); + *state_word = u64::from_le_bytes(temp); + ctr += 1; + } + + let mut m_buf: [u8; 128] = [0; 128]; + m_buf.copy_from_slice(&input[68..196]); + let mut m = [0u64; 16]; + ctr = 0; + for msg_word in &mut m { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&m_buf[(ctr * 8)..(ctr + 1) * 8]); + *msg_word = u64::from_le_bytes(temp); + ctr += 1; + } + + let mut t_0_buf: [u8; 8] = [0; 8]; + t_0_buf.copy_from_slice(&input[196..204]); + let t_0 = u64::from_le_bytes(t_0_buf); + + let mut t_1_buf: [u8; 8] = [0; 8]; + t_1_buf.copy_from_slice(&input[204..212]); + let t_1 = u64::from_le_bytes(t_1_buf); + + let f = if input[212] == 1 { + true + } else if input[212] == 0 { + false + } else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "incorrect final block indicator flag".into(), + ), + }); + }; + + crate::eip_152::compress(&mut h, m, [t_0, t_1], f, rounds as usize); + + let mut output_buf = [0u8; u64::BITS as usize]; + for (i, state_word) in h.iter().enumerate() { + output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); + } + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + cost: gas_cost, + output: output_buf.to_vec(), + logs: Default::default(), + }) + } +} diff --git a/src/components/contracts/modules/evm/precompile/bn128/Cargo.toml b/src/components/contracts/modules/evm/precompile/bn128/Cargo.toml new file mode 100644 index 000000000..520132cc2 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/bn128/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "evm-precompile-bn128" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +evm = { version = "0.35.0", default-features = false, features = ["with-serde"] } +evm-precompile-utils = { path = "../utils"} +tracing = "0.1" +module-evm = { path = "../../../../modules/evm"} +num_enum = { version = "0.5.4", default-features = false } +fp-types = {path = "../../../../primitives/types"} +bn = { package = "findora-bn", git = "https://github.com/FindoraNetwork/findora-bn.git", default-features = false } + +[dev-dependencies] +baseapp = { path = "../../../../baseapp" } +fp-mocks = { path = "../../../../primitives/mocks" } +ethereum-types = { version = "0.13.1", default-features = false } +hex = "0.4" diff --git a/src/components/contracts/modules/evm/precompile/bn128/src/lib.rs b/src/components/contracts/modules/evm/precompile/bn128/src/lib.rs new file mode 100644 index 000000000..e53bd057e --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/bn128/src/lib.rs @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Findora. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use evm::executor::stack::{PrecompileFailure, PrecompileOutput}; +use evm::{Context, ExitError, ExitSucceed}; +use module_evm::precompile::{FinState, Precompile, PrecompileId, PrecompileResult}; + +use fp_types::U256; + +fn read_fr(input: &[u8], start_inx: usize) -> Result { + if input.len() < start_inx + 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Input not long enough".into()), + }); + } + + bn::Fr::from_slice(&input[start_inx..(start_inx + 32)]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid field element".into()), + } + }) +} + +fn read_point(input: &[u8], start_inx: usize) -> Result { + use bn::{AffineG1, Fq, Group, G1}; + + if input.len() < start_inx + 64 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Input not long enough".into()), + }); + } + + let px = Fq::from_slice(&input[start_inx..(start_inx + 32)]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid point x coordinate".into()), + } + })?; + let py = + Fq::from_slice(&input[(start_inx + 32)..(start_inx + 64)]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid point y coordinate".into()), + } + })?; + Ok(if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid curve point".into()), + })? + .into() + }) +} + +/// The Bn128Add builtin +pub struct Bn128Add; + +impl PrecompileId for Bn128Add { + fn contract_id() -> u64 { + 0x06 + } +} + +impl Bn128Add { + const GAS_COST: u64 = 150; // https://eips.ethereum.org/EIPS/eip-1108 +} + +impl Precompile for Bn128Add { + fn execute( + input: &[u8], + _target_gas: Option, + _context: &Context, + _state: &FinState, + ) -> PrecompileResult { + use bn::AffineG1; + + let p1 = read_point(input, 0)?; + let p2 = read_point(input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Cannot fail since 0..32 is 32-byte length".into(), + ), + } + })?; + sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Cannot fail since 32..64 is 32-byte length".into(), + ), + } + })?; + } + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + cost: Bn128Add::GAS_COST, + output: buf.to_vec(), + logs: Default::default(), + }) + } +} + +/// The Bn128Mul builtin +pub struct Bn128Mul; + +impl PrecompileId for Bn128Mul { + fn contract_id() -> u64 { + 0x07 + } +} + +impl Bn128Mul { + const GAS_COST: u64 = 6_000; // https://eips.ethereum.org/EIPS/eip-1108 +} + +impl Precompile for Bn128Mul { + fn execute( + input: &[u8], + _target_gas: Option, + _context: &Context, + _state: &FinState, + ) -> PrecompileResult { + use bn::AffineG1; + + let p = read_point(input, 0)?; + let fr = read_fr(input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p * fr) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Cannot fail since 0..32 is 32-byte length".into(), + ), + } + })?; + sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Cannot fail since 32..64 is 32-byte length".into(), + ), + } + })?; + } + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + cost: Bn128Mul::GAS_COST, + output: buf.to_vec(), + logs: Default::default(), + }) + } +} + +/// The Bn128Pairing builtin +pub struct Bn128Pairing; + +impl PrecompileId for Bn128Pairing { + fn contract_id() -> u64 { + 0x08 + } +} + +impl Bn128Pairing { + // https://eips.ethereum.org/EIPS/eip-1108 + const BASE_GAS_COST: u64 = 45_000; + const GAS_COST_PER_PAIRING: u64 = 34_000; +} + +impl Precompile for Bn128Pairing { + fn execute( + input: &[u8], + target_gas: Option, + _context: &Context, + _state: &FinState, + ) -> PrecompileResult { + use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; + + let (ret_val, gas_cost) = + if input.is_empty() { + (U256::one(), Bn128Pairing::BASE_GAS_COST) + } else { + // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; + + let gas_cost: u64 = Bn128Pairing::BASE_GAS_COST + + (elements as u64 * Bn128Pairing::GAS_COST_PER_PAIRING); + if let Some(gas_left) = target_gas { + if gas_left < gas_cost { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); + } + } + + let mut vals = Vec::new(); + for idx in 0..elements { + let a_x = Fq::from_slice(&input[idx * 192..idx * 192 + 32]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid a argument x coordinate".into(), + ), + })?; + + let a_y = Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid a argument y coordinate".into(), + ), + })?; + + let b_a_y = Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid b argument imaginary coeff x coordinate".into(), + ), + })?; + + let b_a_x = Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid b argument imaginary coeff y coordinate".into(), + ), + })?; + + let b_b_y = Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid b argument real coeff x coordinate".into(), + ), + })?; + + let b_b_x = Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid b argument real coeff y coordinate".into(), + ), + })?; + + let b_a = Fq2::new(b_a_x, b_a_y); + let b_b = Fq2::new(b_b_x, b_b_y); + let b = if b_a.is_zero() && b_b.is_zero() { + G2::zero() + } else { + G2::from(AffineG2::new(b_a, b_b).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid b argument - not on curve".into(), + ), + } + })?) + }; + let a = if a_x.is_zero() && a_y.is_zero() { + G1::zero() + } else { + G1::from(AffineG1::new(a_x, a_y).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other( + "Invalid a argument - not on curve".into(), + ), + } + })?) + }; + vals.push((a, b)); + } + + let mul = pairing_batch(&vals); + + if mul == Gt::one() { + (U256::one(), gas_cost) + } else { + (U256::zero(), gas_cost) + } + }; + + let mut buf = [0u8; 32]; + ret_val.to_big_endian(&mut buf); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + cost: gas_cost, + output: buf.to_vec(), + logs: Default::default(), + }) + } +} diff --git a/src/components/contracts/modules/evm/precompile/src/lib.rs b/src/components/contracts/modules/evm/precompile/src/lib.rs index a44d7b21d..994ec14ae 100644 --- a/src/components/contracts/modules/evm/precompile/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/src/lib.rs @@ -4,10 +4,11 @@ use module_evm::precompile::{Precompile, PrecompileResult}; use std::marker::PhantomData; use evm_precompile_anemoi::Anemoi; -use evm_precompile_basic::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use evm_precompile_basic::{ECRecover, Identity, Ripemd160, Sha256}; +use evm_precompile_blake2::Blake2F; +use evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use evm_precompile_frc20::FRC20; use evm_precompile_modexp::Modexp; -use evm_precompile_sha3fips::{Sha3FIPS256, Sha3FIPS512}; use fp_core::context::Context as Context2; use module_evm::precompile::PrecompileId; use module_evm::Config; @@ -60,15 +61,17 @@ where a if a == H160::from_low_u64_be(Modexp::contract_id()) => { Some(Modexp::execute(input, target_gas, context, ctx)) } - // Non-Frontier specific nor Ethereum precompiles : - a if a == H160::from_low_u64_be(ECRecoverPublicKey::contract_id()) => { - Some(ECRecoverPublicKey::execute(input, target_gas, context, ctx)) + a if a == H160::from_low_u64_be(Bn128Add::contract_id()) => { + Some(Bn128Add::execute(input, target_gas, context, ctx)) } - a if a == H160::from_low_u64_be(Sha3FIPS256::contract_id()) => { - Some(Sha3FIPS256::execute(input, target_gas, context, ctx)) + a if a == H160::from_low_u64_be(Bn128Mul::contract_id()) => { + Some(Bn128Mul::execute(input, target_gas, context, ctx)) } - a if a == H160::from_low_u64_be(Sha3FIPS512::contract_id()) => { - Some(Sha3FIPS512::execute(input, target_gas, context, ctx)) + a if a == H160::from_low_u64_be(Bn128Pairing::contract_id()) => { + Some(Bn128Pairing::execute(input, target_gas, context, ctx)) + } + a if a == H160::from_low_u64_be(Blake2F::contract_id()) => { + Some(Blake2F::execute(input, target_gas, context, ctx)) } a if a == H160::from_low_u64_be(FRC20::::contract_id()) => { Some(FRC20::::execute(input, target_gas, context, ctx)) @@ -76,9 +79,9 @@ where a if a == H160::from_low_u64_be(Anemoi::contract_id()) => { Some(Anemoi::execute(input, target_gas, context, ctx)) } - // a if a == H160::from_low_u64_be(EthPairing::contract_id()) => { - // Some(EthPairing::execute(handle, ctx)) - // } + //a if a == H160::from_low_u64_be(EthPairing::contract_id()) => { + // Some(EthPairing::execute(input, target_gas, context, ctx)) + //} _ => None, } } diff --git a/src/components/contracts/modules/evm/precompile/testdata/blake2F.json b/src/components/contracts/modules/evm/precompile/testdata/blake2F.json new file mode 100644 index 000000000..f6ef8eef4 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/testdata/blake2F.json @@ -0,0 +1,37 @@ +[ + { + "Input": "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", + "Name": "vector 4", + "Gas": 0, + "NoBenchmark": false + }, + { + "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", + "Name": "vector 5", + "Gas": 12, + "NoBenchmark": false + }, + { + "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", + "Expected": "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", + "Name": "vector 6", + "Gas": 12, + "NoBenchmark": false + }, + { + "Input": "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", + "Name": "vector 7", + "Gas": 1, + "NoBenchmark": false + }, + { + "Input": "007A120048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189", + "Name": "vector 8", + "Gas": 8000000, + "NoBenchmark": false + } + ] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/testdata/common_bnadd.json b/src/components/contracts/modules/evm/precompile/testdata/common_bnadd.json new file mode 100644 index 000000000..d507884c5 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/testdata/common_bnadd.json @@ -0,0 +1 @@ +[{"Input":"089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf","Expected":"0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb","Name":"bnadd_0_0"},{"Input":"23f16f1bcc31bd002746da6fa3825209af9a356ccd99cf79604a430dd592bcd90a03caeda9c5aa40cdc9e4166e083492885dad36c72714e3697e34a4bc72ccaa21315394462f1a39f87462dbceb92718b220e4f80af516f727ad85380fadefbc2e4f40ea7bbe2d4d71f13c84fd2ae24a4a24d9638dd78349d0dee8435a67cca6","Expected":"013f227997b410cbd96b137a114f5b12d5a3a53d7482797bcd1f116ff30ff1931effebc79dee208d036553beae8ca71afb3b4c00979560db3991c7e67c49103c","Name":"bnadd_0_1"},{"Input":"0341b65d1b32805aedf29c4704ae125b98bb9b736d6e05bd934320632bf46bb60d22bc985718acbcf51e3740c1565f66ff890dfd2302fc51abc999c83d8774ba08ed1b33fe3cd3b1ac11571999e8f451f5bb28dd4019e58b8d24d91cf73dc38f11be2878bb118612a7627f022aa19a17b6eb599bba4185df357f81d052fff90b","Expected":"0e9e24a218333ed19a90051efabe246146a6d5017810140ef7e448030539038a230598b7d4127f5b4fd971820084c632ca940b29fcf30139cd1513bbbbf3a3dc","Name":"bnadd_0_2"},{"Input":"279e2a1eee50ae1e3fe441dcd58475c40992735644de5c8f6299b6f0c1fe41af21b37bd13a881181d56752e31cf494003a9d396eb908452718469bc5c75aa8071c35e297f7c55363cd2fd00d916c67fad3bdea15487bdc5cc7b720f3a2c8b776106c2a4cf61ab73f91f2258f1846b9be9d28b9a7e83503fa4f4b322bfc07223c","Expected":"22f8aa414eb0b9b296bed3fb355804e92ec0af419d9906335f50f032d87a8bf82643f41b228310b816c784c2c54dcfadeaa328b792dbe0d0e04741cd61dac155","Name":"bnadd_0_3"},{"Input":"0af6f1fd0b29a4f055c91a472f285e919d430a2b73912ae659224e24a458c65e2c1a52f5abf3e86410b9a603159b0bf51abf4d72cbd5e8161a7b5c47d60dfe571f752f85cf5cc01b2dfe279541032da61c2fcc8ae0dfc6d4253ba9b5d3c858231d03a84afe2a9f595ab03007400ccd36a2c0bc31203d881011dfc450c39b5abe","Expected":"1e51b9f09d8fc2e4ca11602326c2cfe191c6c6a47874526e80051197e9f6af842282e508ca489bf881e25cf9590151ff5cf94fa523683a0718d87abcc4d4a16f","Name":"bnadd_0_4"},{"Input":"0e6eab4103302750b22364bd1ec80e5edfb3ad06fa175ff2517ca49489f728e9050a17b5a594d0fd6fafed7fe5c447793fe9b617f0f97c3ee6dd29638f6c9232038de98419e242685862c118253ab7df7358f863a59170c37e606d5bd23c742f076ff3443f4e01b7d7ace1315fe50cf77c365d8d289c65303bcc11ba7961ab95","Expected":"2231ab2eee93d63596f718533ddbb95a86b13d39e1162897d791566e797f82952f39ea566bede8e7ba15f3c61b0e96275b2fc51800ee2baf2f9bd7acfa874f0a","Name":"bnadd_0_5"},{"Input":"1920c53c756f1ec1a40e0264e5f65808eafaeaa7b0885f89852297bc2186ac9d09416cc536a27b6d5616f74dd2bbbfb463b9961752e0aa38d47b5213994959ab015296293a5a1bb5e15a7d019787422cb3409e075e122c6fc5867f0c3f3715731782b870b6641d8d55323e27ebaea17909499877fda62e3ac1e2b2310cad5f9c","Expected":"0fa3236565b78b283f3ce63ca62bafb87c33407b11a077e39230ff37c054cf712a38174368bf872f80f78fb5222e95717183242b9d4da75c66243f043aed2fc5","Name":"bnadd_0_6"},{"Input":"001faaf97b965ffa633612b7c8f9f4be0b286b19662e5cbe6878019d8ba1382b16567ced7a7ee5c272bbc378a95c2436fb0c6133649c77e55a708b28419b5cac0750d51706ced69621c8e4ba1758ba90c39ba8b3b50507bfa545ace1737e360e283d609cd67a291fc3d720c5b1113eececba4ca31d58a1319d6a5a2fa89608f9","Expected":"044fe3c480840e5a8f544efd28a8bf3246f0741a8c61c3116e93d84773399c8b26c5b695120cd724aa2a5f4dfd3042c07f752be4c4a8b750398109d80f4772eb","Name":"bnadd_0_7"},{"Input":"128b65cb80257f3006fc20dbb6af6781da7e0f9213d2b909fd113ee0f2d2bb52251e288387db7be742fe67261f36a4f09eeb4763bbbaa1bb13af3dec65302a4115f64edf27478045bf45eded285544acaa7f2b3a2a36176acefc1a3d7181a73219d4344489688c2a2f16caf1141bc42021738339431b3a64cfbc293a73c1eddc","Expected":"0a20db61d2b74384ca184f20455ad1f380ba081f89e41c87ceb3fdbbba63c7aa1fbda20003ec799f306f70df7f53f91721e59353013ebf1647f5130903fff482","Name":"bnadd_0_8"},{"Input":"16a9fe4620e58d70109d6995fe5f9eb8b3d533280cc604a333dcf0fa688b62e20b972bf2daef6c10a41db685c2417b6f4362032421c8466277d3271b6e8706a809ad61a8a83df55f6cd293cd674338c35dbb32722e9db2d1a3371b43496c05fa09c73b138499e36453d67a2c9b543c2188918287c4eef2c3ccc9ebe1d6142d01","Expected":"005a68cc13a108287aa3ca0bd8bef95096ef22668e15c87f7cbe0167cd1cdc930359b9b2dd28843838cf74cb4af2cfd656690a7f73de771b891142db22fa61fb","Name":"bnadd_0_9"}] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/testdata/common_bnmul.json b/src/components/contracts/modules/evm/precompile/testdata/common_bnmul.json new file mode 100644 index 000000000..d565dab4b --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/testdata/common_bnmul.json @@ -0,0 +1 @@ +[{"Input":"089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6","Name":"bnmul_0_0"},{"Input":"25f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"18a902ac147b2951531770c7c18a25e3dd87765e23f7e0c4e9d62b624a6e37450288473776e7e99b2aaa27e8f4656ea9ce5e634fd1ca1aab45315199ecaced2e","Name":"bnmul_0_1"},{"Input":"23f16f1bcc31bd002746da6fa3825209af9a356ccd99cf79604a430dd592bcd90a03caeda9c5aa40cdc9e4166e083492885dad36c72714e3697e34a4bc72ccaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"0c6a880ffdd0737c53bfec9b65c9098a3298747bd4e5fd07026661b4cb804331116aeec88e11f49753df224c60c4bd8b8bc0a98b8d50f24ce64475268d227f4c","Name":"bnmul_0_2"},{"Input":"21315394462f1a39f87462dbceb92718b220e4f80af516f727ad85380fadefbc2e4f40ea7bbe2d4d71f13c84fd2ae24a4a24d9638dd78349d0dee8435a67cca6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"1d7985d51e53cdfbd73b051e9a74ab6e621b6b664a7efed00e30c1264f5623d02808eee3baec187160d2499b4aedbc665a532d245212a1be61e0d4b9b36f3075","Name":"bnmul_0_3"},{"Input":"0341b65d1b32805aedf29c4704ae125b98bb9b736d6e05bd934320632bf46bb60d22bc985718acbcf51e3740c1565f66ff890dfd2302fc51abc999c83d8774baffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"15bd6ea71fd264e1bfb04eb6d97b4f3686c5bf36f91356fc13ddde3494e172d90b3f8392fd4cdd5d542887ea4ee0274835bf37b58edf927ef242b8704af52e92","Name":"bnmul_0_4"},{"Input":"08ed1b33fe3cd3b1ac11571999e8f451f5bb28dd4019e58b8d24d91cf73dc38f11be2878bb118612a7627f022aa19a17b6eb599bba4185df357f81d052fff90bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"26ec73a6134f8ebce33d675e1f2e6ff3ec066e8d255ffca6eb55ef2ab7c5c51d06500cfcd6950c92de24b90ca09be110f8f9c2fb4d9cb2a9f9677dd81c1c0607","Name":"bnmul_0_5"},{"Input":"279e2a1eee50ae1e3fe441dcd58475c40992735644de5c8f6299b6f0c1fe41af21b37bd13a881181d56752e31cf494003a9d396eb908452718469bc5c75aa807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"06894837c70570eac651dae1a443b830c292c1801340ca4150c9d339177965e509ad8d4839bc83bd1852e6a8b71dcf01a1f7d6b6b174858ca02893bd5ace3eee","Name":"bnmul_0_6"},{"Input":"1c35e297f7c55363cd2fd00d916c67fad3bdea15487bdc5cc7b720f3a2c8b776106c2a4cf61ab73f91f2258f1846b9be9d28b9a7e83503fa4f4b322bfc07223cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"0e56eeb3f168767b21bce1489d9657f694951b25ea8a081f4ebf68469a1eb1e0293446d763ea9c40e52286f2ac504cfabb364b1f899b874b13d78879d25a5ec5","Name":"bnmul_0_7"},{"Input":"0af6f1fd0b29a4f055c91a472f285e919d430a2b73912ae659224e24a458c65e2c1a52f5abf3e86410b9a603159b0bf51abf4d72cbd5e8161a7b5c47d60dfe57ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"2f63f5f1275c401356e94adfbe5e8cff21485a9281e55d378a51eb93263a40802a817491a84e40c584481df4a5085b301c6fd66cb97856de55cd04df85a6a1d3","Name":"bnmul_0_8"},{"Input":"1f752f85cf5cc01b2dfe279541032da61c2fcc8ae0dfc6d4253ba9b5d3c858231d03a84afe2a9f595ab03007400ccd36a2c0bc31203d881011dfc450c39b5abeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","Expected":"0aa7b6fda656f23eab50e36db0519cdf79f4624d417253085907ebfd9aef38a414cdd2edce2b313fc6dd390628ac9fac910841706d55f9af2a064548694dc05c","Name":"bnmul_0_9"}] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/testdata/common_bnpair.json b/src/components/contracts/modules/evm/precompile/testdata/common_bnpair.json new file mode 100644 index 000000000..1f42ca54c --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/testdata/common_bnpair.json @@ -0,0 +1 @@ +[{"Input":"089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3629f2c1dbcc614745f242077001ec9edd475acdab9ab435770d456bd22bbd2abf268683f9b1be0bde4508e2e25e51f6b44da3546e87524337d506fd03c4ff7ce01851abe58ef4e08916bec8034ca62c04cd08340ab6cc525e61706340926221651b71422869c92e49465200ca19033a8aa425f955be3d8329c4475503e45c00e1","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_0"},{"Input":"23f16f1bcc31bd002746da6fa3825209af9a356ccd99cf79604a430dd592bcd90a03caeda9c5aa40cdc9e4166e083492885dad36c72714e3697e34a4bc72ccaa2e832e2bd419280d247658bb64323d59bbf47df41aa729d2168a272d66e306ff18ab999098bc5b30758183a160fcca776562d9a9370278aee9e6f71053e9358f0edd6252e0584efe53db6b3c40d1976d3849f08db15d39a0d7a6e327fc67f45e24925638e68e59cc22218917c61d2934c4e6353e2f62178a09627aed68c4e57a","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_1"},{"Input":"0341b65d1b32805aedf29c4704ae125b98bb9b736d6e05bd934320632bf46bb60d22bc985718acbcf51e3740c1565f66ff890dfd2302fc51abc999c83d8774ba0d2c492bf135ed45b0d6265c274d145d35b73afd41ee95d3f1da4bc8761038800251d138db1b9748ffc257b147a1aea66413b14df767f98f7ba02489c617eae51065ff2bd9a5b167db36225a35fd712d781309f4e2c8541a335b2c42bd2bcae4191cd528d749c52f3e198e534868d537867109419a32314886f6bb2bcd337773","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_2"},{"Input":"279e2a1eee50ae1e3fe441dcd58475c40992735644de5c8f6299b6f0c1fe41af21b37bd13a881181d56752e31cf494003a9d396eb908452718469bc5c75aa80728695310187ac477bdf3d7fb472e6374b1222f8ebcfcae72a8b1dc714ec853461fde556a4ef408d4a2ce89143674e9985eff87fa3df3ce6e7483c3a2f9b82bcb18df8612ef09775ec815b602e35eb6ec71ea6f7acebdc23667a60eb465e76d590500f7d19190ee1090bffe087045e97c913f7c5730110559665b3e8a9e0a526e","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_3"},{"Input":"0af6f1fd0b29a4f055c91a472f285e919d430a2b73912ae659224e24a458c65e2c1a52f5abf3e86410b9a603159b0bf51abf4d72cbd5e8161a7b5c47d60dfe5712fa10f98bc3c4faa2a9729408e92a8b4e6d304abc539cc396820f696f23ecbb2b30d01fe71a3550fa6780e70305d28ea686b72aff7d23cd550f886fcd60f6b30d1142c81b9b27e329c498e4b241fa2ab0e0c62170cbe2a1e642589e4ff6ca54069ec036f519726e40596ede6cd47577c5cc24746754280add50bf87fe7404c8","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_4"},{"Input":"0e6eab4103302750b22364bd1ec80e5edfb3ad06fa175ff2517ca49489f728e9050a17b5a594d0fd6fafed7fe5c447793fe9b617f0f97c3ee6dd29638f6c9232068b38fd28978b73b64592127317a542e084ea8291e404b88d3a731207104f9624d1ae64196a7d3b8e9fdcf72f9dce16d649fa3971daaca56405a0d331e2aa92210a32dfe27a36ca740e4d668066f7559acddea850fee4ebf9c9a241b337e771191a60da23a028dfc3b3b82c3d15802615f6386f29194e088936e3ffe96809eb","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_5"},{"Input":"1920c53c756f1ec1a40e0264e5f65808eafaeaa7b0885f89852297bc2186ac9d09416cc536a27b6d5616f74dd2bbbfb463b9961752e0aa38d47b5213994959ab081c536b7d9a27636d0af7d4c8dad2e380cb2996a1d650a2e6123224294a6c86039e75a4a93bcc2798aec714e37b9b2602760e7ec7c6f3342e44018f2986da9b2d1ceb9ba9f7b923459d8b4701dfae12b3efd4174fa3705747fba6bce14a87720c15d04d2bd099f86ccaf9834b9588e88fdec8d04bba329d4fed580884f4b2bb","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_6"},{"Input":"001faaf97b965ffa633612b7c8f9f4be0b286b19662e5cbe6878019d8ba1382b16567ced7a7ee5c272bbc378a95c2436fb0c6133649c77e55a708b28419b5cac0c37556712bc94140738212a1372a4720ff269102f3f0dad4493b675a85463021e03f350aa8213bd6ce54987e7b32275bcead9a9096897b5c7dcd5964a17e07a2550f45b84e947f8e46267041f661de0ced333f91af090435c4bd6d9cecb4836096855fbc396b73c589e66ad989d7851c08bb55755f5771987d91925ffb40c2f","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_7"},{"Input":"128b65cb80257f3006fc20dbb6af6781da7e0f9213d2b909fd113ee0f2d2bb52251e288387db7be742fe67261f36a4f09eeb4763bbbaa1bb13af3dec65302a4125ed8b2035e437f057cb45003f1c25510d7e232c7587581bdc3035064a96d2e32293733bfad88afa5adabcf320f055e3fe0d1ae733dc21f5e08fc234029be16a18414155506a4c287ac374bc28635be3e6b8fd0097554db051a45138f0b6140410f8c1e8992d53017580b1b806cd23729f9a2aefc6e60bb82606551c1393750b","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_8"},{"Input":"16a9fe4620e58d70109d6995fe5f9eb8b3d533280cc604a333dcf0fa688b62e20b972bf2daef6c10a41db685c2417b6f4362032421c8466277d3271b6e8706a8034c462aaaa7a3b4ed1266d755867524258c64b9553c46b2bd4d5c26aaedc66524e9d39a8749083573a1b84662c08ae15f16b75b9fd40089ee412f5e58dfad350e28b1c6e881a288290c7e9e12d23f122e61e89c43619f902c48d300ffe1734b014f125c1960a64127a16c8a17da5f21721e47b17575a94802a78711f45fe988","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_1_9"},{"Input":"1a4db8abfea95acd4ac4bdf3d9082de13dcb9b2a73d6aa98a296a7d7d1009a8a1e8a143f9ed8661a223e211c4f70b1ac1d5057d71e31f182637b6f26de9a59190e817e9fb476d397b0f09aa07d290cc830ceac3ef815c5b8f45caf2cdafd90e209888f93aab917c7b6a9f8ee7174ff889961ff3509256873a774926c0c5b9a0f1005b845c9f53465d9371efb0eb934ecc8db144d466eaf06614e89c77305c35501951dfd5ce7f374b05b2b2a0737a5251ce4d9f53f0ee7fe79399aa09d6bfe9414e57a7979f8f2d78a1c7dc790476265240fff65e18344caa11b0698a574d4412bb862b7c0f8283e05e10a9b6e3bd19e5b69f6988b3f470692f26c379c33132126a4d85b3499d830033f0388c18b8e7b824b5b0b57db1fe366a435b0c1a53c6000ed1afda982e3f8d71da60ede43dd562ae63597ec91b1cba21e1c9fd5b8fbd81795c6b9f6debf45b77638122c7333a59aaa2e7511f526f496feb84c20f651552581dd3c6f3d05c78234b559d784f502fa5dc0900ae7475ba88716e5ac3d499d","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_0"},{"Input":"1fc8f5de35d75e7446ef5703dcd7e07ad682030dd525b7620e1f7f36908d535e151d3453db1029bf894b5bded388d6a3fcf97d573bcccf93d27456ec79d9337d23ec8c4d66276cb927175d7bc25e3bf5a2252c4f83de5eabd695181834c67273017bea94aade8279f9b646a2e7f570a88aea7eb8bd51aaf40dbfe070a7228a5d06652252d3e68fc6fd6d76cd726513f0f757783f415762ecf16fb288ea9b10252a6ba1abfff1bf53aef31d24fc7d7c18d452ff529a04bb97e2aeaab748cc19762032882547249177d5cc51a46852ebc90ac17f098a3deb8dd8a37fd007bc2baf28714b756b948ac2b606fe0c4c1c3572423892040e5518f9e6abe89e7b1875bc25cd06956892564bd5e9e8557ba6c719e2ecedb4a4996817a8e77593af1321050b2488820e023afa5734ceab7072083831db2f7063cc57cd9852deab6918ca9b00b22e3956983827a599218890be78a3c0ac7f30d3a35d464a9cd4d4ef552576221f5a9cbdba030df39ed692fe2274fe2a99fda883aa4e724d54d6a0f74e0a89","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_1"},{"Input":"0069c0c3d6aa9ca250e5bd3e2826e47936b6e479ad7c776caa5ac801dd5a1c2115226a94941ba68cd74c5b982235773c3a9cf6a71b1dc9d4d8307251f1d031c630316709df5c7f6af25fc5f698db87b1170fdf63bd2917bc21b5037452053dd30bec8492774364ac6ceca4b56b9b8d1aec0bcda12943dd1437e515fa7fe5bf8000c0e3fd9719382c09eded028cf0c9f866d0fec2fab515f6aa60249557270f2e25f0d2cf69c495554a426914354b553f3f6f269e0d5340b2593a5793138189ae214aa7d305903ecbdbc10d564e1ebc9455bf0e8aa3ba3321f8b9023b911cecab19239edfaa2efe905d8a5d6884da42815cc84b13d9c4fb3c304cd99d3d27b61e15063c61103bc76e80aadf05a3ae6a36f90013bce43fdb48bfc8e3e2e09f838d1dcd88fe88d48dcb4d6401442b46fd713c41d36dfbc50d8dad834a9615fc01581377baf2e5ec86b4bfe2123c9f1d5cac10fe0a79fff333ac86ebffe1aabb98252316b9995e22925907421897f44ffffa963b730c7edad138f41f54652ddd8c30","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_2"},{"Input":"240517a373fda7a2fef6ee9e9895505545c354433af3d3548b8537abb10d445c12e45a8b8c4d7a82ae27f0ffe3dd976b1242b040b96043ad31ce9f2ffa5bc707275ec5565ec4ccde5088b40c57c946be5d48b9ba34f956611ec87ed33d47500116f3ff1c54f920a4ba438c43501d2ef59e7c7eb2f8ebde852309bbfbc087af1b1742abd3e12ea49804bc4e8e59ecb9da6789392b1e12958ad653ad49f3d4d8450a1190ddff2fe9afa9dbea1e42222afe9715e1bced4e7ac4c20d89476ed6253c0bcfe3f03313340fd392b32bccfcab83f942f6b13d71e2a3159239c61c50c7bb199c36728784b46d5607ebd7cf5a055a3db4458ad6f0fb7e826d6a280daf625d1db3a467a1de6f3b848da16893902bb07fd0b63f6509dacae6693c7975fe09940b291b95276b136f58cf6f899cbb5a6f4009a4d069be6c0f8f0d6bfd7c38991f0e46dd20f14881ed44b216b44ba054b1f32c61d01f33326ec7f6c2ace3c8901414f211e03a121fcbb34f328205223d7f7ddff484efdd5d06e8886f2781f8e549","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_3"},{"Input":"15848a9e99c4a0719df209a705edfa52bdca729d0b73a9835880da9dc0cd2ace108b1a4ceabe6a8ba3dea041bb0dff513e7b99057d4b3c2aa378807de47dbc3627515ea8ee84feadb86577a1b7553b2016cc63807de89fcbc05bc3d53b0f23332872f837e4cde9fbf64bdd170d6488a3155bcd5b1fef4fa7ad10089bec8ea7b803713c878e32a49ff95dc2bbe7b1491c38258835dbca14ab343e88dc64004dae0f9ec7e3c20939b84e40bf66864e8129742feed7e1291d752c76bdf28e02d03b2c48bfe10367b611f37ab5847542df1d30baed8abc5380d6d80032d0e04af0d10e119d730f867b4e286a3c1c4cd04236999b9bb94d9001a4f144982d72df407d059e6ea753e84de7e8b41b2d0dca1cfb6b12afeb2564a5e24a2064f1b662a1a912e0bdf2470e896d7fda45c39a66c5c4a5297b4f19810ee37e33f7f50499326b1f70831beccc3eb5fb719d27f111e5c731cbeb4002519a4b3831ba896cffb608157b59ceb6f7a0518df929eac70c7bac9ef9c196ba21266ff47cee9bb0c60864","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_4"},{"Input":"08a66cc423317e9d9560c307c7918b87672f3d99effd28b1d2bd2877e09fbba41a58977974f56dc522d159bffd0099791d207d2da49ee217055f5d0806e2dd0417b523e87e1d03b9498facd5df95cd85affaa681c2a43e51497997aa916318391835cc25b6a0979581ff7042a390a7315d8f5e938ea40dfdcb24822889e494cc18dcd8bedefa1c477913634203aa21a5a926595f62f4010eb3959d51acc430fa23ff858e75bc56f4d5054035aee0fb7264ecc4861ba1c44db81868108dcdc4dd099b58d1c3d880dda20c24def1b2c54cbb20ed9da85265983a00c1cf1facfcf903e39662a649ddbc4fa6927ed85694167b49ee5787bd755a5e4ff6fb82c32a9f20b8ce350fdb30744dc3dd1829ce8e7365a9ca56bfa4f79831355157a73fe99a17a534a927ed0e6cdfe9ae9befe94f581d20a4f19aa54c93149c95d53714c5a70771d55a8c477c0ebdde254d19e591fc3ecf011848276731a12a96a3267c5c710c76eb36abc43ab4d4b5a7a82770304fc2e9487512493dc5fd59aa6eb30402d0","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_5"},{"Input":"08808ba03b1abc4b870cd2e50b3960e85ea4ffde76e97ea70e62feb5712ac68823f1f949786f8cf8a18323bbad75bc71afac4e2419181fee3a627a8980f5d36f243b9ecee8d6c2f2f1b91980e4f7136d16f70c94c5fdc21354adb977bc66d748207322b7980616a6e4f4ddc3f223c52a9a11b785298b69f4075349274adeebb21226d7ded357133a1e9a8472f9c0f23e248c1ca1974ae336f4316731f1f473bf00b29428be1694c86da00b5cfe677c1b3332c93105d18df718f9ff0053bcb7a620adf7f96265baa9a9b1512eb1d59834eb931491d360408061135a8b30ddbfdc284fb8dde65089a6359a11fc8d2725409e2bc1e52141f1dd38bd56d10015346d0aad6b6b34672e8ffda5085e97e2a7f82ff4e3e6660b83e151b778e2dfd1ef3f14b07ac31c0927a5cb27b0447051f1ea39baa7ef12572b9b5dc535ffa28341a220dc6c1d760064168c59b36eae9560df6433eb96acd58aa88499ac5f081eed30245b974d3078709e037860b2970943659596a88d529a36275b34b38c51ced169","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_6"},{"Input":"198f03850c2c5df1bd5601c0a5cdae3cc955026dba50a79dd00aeefe49c48c252ee3bce85e4c7bb10fee21428a8b0c1328d662e152f36a6c62caaac2fe097c0b0c91b38273eedbc873b2172cce9eaf7af1463cb3fb064a49cff2dc95f0b5bc1224a1a5e52496677b5dfa29a4e314d7b32bd22c0f437679e12d0c49c9d84e988014d4d06a717737c0e36767b06809bba6bcda2707bd652b3f922c3a652dfc90b325d0de555188106e9c4bd93171a8377b9d53eeb93b0b259a91c7ba570591f4010f2dbb4dfc16b4fe806d2ed28daf51530d6b88a81c2ffb98d3a6c1153db9239421b7f52300ab7b6eb9d9ef4bd7d58811909cf4c4ad7a9f8546f56099eb6ae1370224ecd4af7d5ec18e5b0c8eb46a8bc774fc4ac6c4233aafe9bf22420ddea4752bff288e58f4bc80b1bb91b450652c120db2ece783788b8ff3beb20b2e8d3c7a2d90a5dc703456ffb854fe3a746143e82bd8e4aae8321ab59a93e5da9d615ede07f9f33a0a234486c63562fd9b27ee6615655b6880bd511475059ab40395d23c","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_7"},{"Input":"2722ae33a1d80f67a5da37ef2b7489dd39b1c98287fc408eb416e37de71bba1c3054f7ce76b782aa80254ce991fd5f115810e8f8f57ed0a12f30278112157c11165fa63e0d077c2b406c1001da54664d756636d6018616841c5b0d942519fecb1090ad3ca54838d2b89c0d2e3db3c2aff811d58bc32881d2ec947f473a8308a615b9fb021f60b7604ef07863fc6449504708718eaf59bd00f3620a9da16ed8830a95e72b8b56d1850126d77ddbdbd376e3b19f07a75071f0b958bd40471fe6900738bd659eab6973c3416b9e7c9660d341e8eee184c0f9c5ec59696f220903401e37dd4714f68da2a6affcf65a8cd26c7ae3fa0c0e5a3394887ceaebe4f49de90b501640d9ca26d1b466a9ea005b942b047fb519865b3413ccd90533d9902a85187c1fe325590f77ef42af537664469fa950986ce82c31eeaf1f6b348a3de3c6202ae189a9bf004bd80b9d8bdaa9b395a5263735242766aca3679ec3e547171b2a06ce9550542e6246331cbf852383c14830931cd63ededb0f21621b71e7f981","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_8"},{"Input":"2bc49f4d8134cf72ce0883c589f4f6f0636db174b6176e063b3effdd57e3404309f61eba66408ad541dac082701734d6e6d4cf6f90bc0e65d6578f2d18563e221eb59aa8d8a9187b0857ab89a453fc53209855b300377bbca411998bdf9aa6730bd2384fa0ad96fcd1fc6c1a1a1fb80d4859770ca91b9559281736cfd6745d011cb39aee49da07d70b0f579101c412f1068ea82671859f09c4c1cb3ae74f8cc103fb73a573c8f147ae15610e4f97e291bcfe16556842e00e88ad70db0df2dc3a05430e574064f2d1de68795706bf99b1b6079a99bda3e5ec266567f2f9246e090f5976710762dfa75253c53ae1c2c88523ff08d0c2523d9409033aa2b7e4392000036daaeef66504945efa89111745e29d39ac317d798bbb1d14f84bdf5bfac91134d59ae245678087748ccfd3c65ecf2db2d05595cd6ed8452346713768c7411ab6efb518910157009fdc818e5d4e082da02f0618f4a3d6166f830b81a4e683023ad24b09b49bfd967c7adbe895811172f04a2b90b1379dac89a6fa1a91d8cb","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_2_9"},{"Input":"03d310db98253bb4a3aaff90eeb790d236cbc5698d5a9a6014965acff56e759a1edc5e9ae29193d6e5deed5c3ac4171cae2da155880cf6058848318de6859ea21ee564b4d91e10d3c3a34787dc163a79bf9d571eeb67ff072609cbe19ff25fc7270e094c2467dcf6ecf8c97a09ef643cfff359cbec1426c5eb01864b5cf3c27309f432f65daced7c895c3748fa0b0dfc430584e419442a25b98e74400789801210fa7bc208e286ebea1f5c342a96782b563a0bc4cdb4c42ba151b9cb76eea93d0abcdaf6ffdf0cbf20235077c7bb3908b74b9a8252083f4b01dcbe84cab90fc71a9681988a15b7eea9ae8cb0c2210b870eec2366e0531c414abb65c668c6eb3e21d0be81c5698882ee63cb729ed3f5b725d7a670b76941ddffff9ddca50cf9af21e61a0ac51a17c0128baa2cda8f212b13943959cf26a7578342c93dd2de7deb194e408b546197d9ee99d643e5385dcb8d5904854d8a836763ab8ce20f9b5027222aced81c808247572971b490eef1515a49f651f7df254de2b35310bb5b78c218b2345c40036ea331bfcfb8536739a5e5530027709adae6632a3613cd0838cd204121beebd54ec6bb063ba5a6d84eeceda2a733260066c90d332425e992ef6c2b0f794d64952d560a422a7ff549a58bfaa0cf791ab6dfad1c4941e19078040324bad1c848f2d8efcd716f7d814c46e2632e74a5b8d455a64c55917b220aab982ed4bbed5f80fac726f4a95789fee7905eef0a241596acbea4268ec3f2b87f3129563b69a30c11a856f68c72f129988e8636c86f57467cba299cdb4917469b49137a989e5d714b4b882d4a455600940ab63b14f23e7934ddd62cf5181099c63b2f57525eb3d19451024a4e71ee09c72c5b7e0dff925001acaee126bcd29db5f600d8ff46230e348d08dcbcdb1f85a5a43aa7b4f51841f1d4f98c18bbb57651982cabdf6326194120727367bdae0b081aa7da12c8f8c6af0ce27d2f8d0f059fe1240c9c5a6993d344744d77bbb855960b6704e91846cf362444bff1ccaf45a7c217873a5d7834d0c1d7d9bab5f9934d7ac218834aa916d459d8ceafc280d59efb","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_0"},{"Input":"2761d967b3f2481435189a94502260dff1044c8cb24ea3809d0eec28cfdbf6c72c87711eaab02bf1f9940d504be3727789efbbb1c48370f61e2e4daad31be6de08a73a943ab3a7ea19633a4ef9e9bc1de68a51d778e41589504b8fb0818cdf88064eb3b5d1a63404e247cd5118a695b3b85d63e89c8512309d591701596f172a210f847e73452b29da165f6c76462f2ee76ff301b534bb0d2f0f7374b041e15612a910ab4044ec261965b917059383b5d5f5c9eb201eb61a6909fb5a610849aa1f4535e9a8bf16a4a4435004b8ab3a84bb6495a0d748ccfc25388832356732910e2936a799b55efa690fd821a3cb4d352cbb3f22ed5fe865f984e289329ca6591e4e4510639258f836e30c422e3a18fb76bdc121fd28ed77306a05b8a3299fec09decd53dc1620f84154fd333da06556e87fae4463cf02459964412fa50d4b082e0120af9b40957fb7ff0dd412f59cc0acc01c49021389cd470dec453e92faa3221680f87a61c96fefc9ada16473b853c4c92a03e1e38211a9487a2812f830760f4841710cf252069ea39d70793aac9b2f769a3954310546fc15a109f6d057d30f01c88e34c9ec83f28674534aa8ad16f61b744b293b63ae3c2fcb178a08408a1780e9ff8f2973152194ebd44ef8b158470d3f67d08425c7a9d99a4f1ee9502704bc22f9c0177342d11e54a0940cd9b51caa04c762f060d5262b5f31c66d088729dcf02930b56cf1a3bcdd147cab2fcfe913c53bf5456dc19edd706ad2122be5129ff68c503fcbe10cef00f314919b033000f43ca563f92e95a8a9dc19a867e400d5a976132900579ff4d8e1f64d86a453d7576e16459b641d574d2d9af32c0707107329853a55152341e8142e6bd97b51118d4777e44d347a3add4f6e73a6d52c0175507a0d4432b6a72508a8fe5838a14c953bb0a8834366aa0ace3a1c12a005e64fcf94cadbc98cb78a5f768409a1cda219c504f3c0e99d1e8de93601046528ceafcc22a3649dd0270094b4b3fbe55fae668028e67a8dc3378fb726cec2fd0c3322db4dccfde2f638c9e7cde93246102e9539f5ea6bf23e57bb3f60e8690e","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_1"},{"Input":"28d6423856311fe86c30fcd983dfa63c95bc5adc1dee6fe0ad715dbe7bba3c091165ad57bbd71dba3d734f446b3a11cbfe5e02051f6898a677bdda5134738d5709661289bdac8badf175790f8e406755b33e54bcd8149e1e469de37447185f110efd4f6454886db697584e85127bf4826304d0d9559195b6d3f875cad607958903e94e1ca8dd25c626e31daf9cd26b3caa7ca2615690127aa11647cba037c92a20179a34c869f27fe9783e133f50fcc38bfb81200b9c635d0b9bbbbc789f4cb60511e5e1561aad84bba3d2495e4853046ae1972e687ccab832f34f19fe9b55db11d42ecbbbf14d945c29319a9a945f64610a2bb9059c0c89911556232bdc6c4b1e92ef35b5ecc024193888d764218828778507233cda5076633acd49986023ba1c0110745aacf972ffd66add373241c8285dc6c96928a6ca012b4e572f1897540deab5945fc017e575d40284abc7356a20bb85330533be288d0a14159da44efd1fc7bd3676093afa2c2d809cb5a14b913c5914c77b341f018f391cfecf0029a2090010e0c084f8c16f8496f1c6db02902cccc6cb8d58118e8dee1baaea679a370075a1886efcc99523c8abf69974f742c7d2de3e12598a963713b9bbc31a9f0a14b98584a1aaebc7649c481668a54d9da6fcb308f3dec33e0965775624f2f904298d6b7a0d04b3a217099a7daf390d5a7edfd72b1c8aab02e6f5cb440cd4a84214772064081adcf6e36148eec3bcbbbd12d363b4a76e7018159f559c2ff1c0d8051f9f9abbafb9032e3c95ebde21931a554fab74bfd7f3d61c939268c46ff9c82ffc5143ba27c7aacca19326cd8522f9d55e6b6d17d4b0586b81d86e76dfb9730782e23592d8d735517d63b6b51313e09681bfb6e66cee098ebb60ca3ebb18f80ddd80135c76e5e26ec2d004f572d267f940a5d5b82e35f4df366312e39387662343e67b7783310ea825278a262dea12bd5b1fa71735819e3e360f467d0af5f522234b4bd70b251d63a8dc9bc9e63117ca8342c3dbb3068df38229b5b34f2bd21097b0a55ece871e554a89aa97a7ecb95cd9e1f5ecea94274019d17b9943de13","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_2"},{"Input":"09fa51b9f1972a041220b86a762c252262d942474d9c2ddaa8d292c79d34b8132771b3f3a480320bc7c9963dda5f4e8120b8f06a0cbc6095d37e0dbbf0b6f9ce23d0c74ab9b983b35311ad334580ad3e9b8ac6eb0ccd69a5097884056c8310e01317cbe87801a1bad8f362bfa07f65f4e6f0df7f1d3681cab1995df75f3caa2516abf6c2348309a172dfb07485e59d4b149d20741c740c9379f5d235c8b7dd922fc0496ddcb63fea074ce4aca7f9a91884b8f3158970ba3f03fd58608b50b7d511857f4a462fb73fa92204c79e81824de2825b74103238b4f8e601a2cb37121303b531c48bcf5c8ddf8e608cd16eef92bec33929ef4d8fe57572214999746567129e8547b1d2f8103bfab8af9a8849ef950c020c2a24c235c889dc65d54c6f00036688a10447a1ca50ecb46b9c77042a3e0959de2919eb62e55e08f94b3b491b05f4e65becdda13865c79072d38ca4a35cd934ccda724348e68631deb874a62d2bec9369394e4b5d549558b030a5b8681d23a67cb9d95465f531a39ae61782b729fc71bd60da082f1dd43572c15900fe35d03e10201fe93668c6e31e0e4330212b0fe78b06800bc1a549cdd0ff91ea7ada763609799901b63a1d3ecb446ad1110847890bfd62a9ad72ad058345167d3716969f4f0c1861e532fcf5dbcbdeec04018770d45b235156f127dc39b55ce54daaccb9b2bffaa9692591647fb85112a622ec7b25af397ed0c3738c242d8c7d98753b0a64f826ad284412b93decd25a091a8d6e03829186f4f33185c75bae823e2840d04ca583753b9ba7b9a215de06ce04ca57858bb1cf75033675d652331e737f7d98400ea1b42102273e74f75ec4a41716a588640c92a28e2d4bf4ca5a6c8fb6fb2d910ce2c882fee639f5e16f11cb1967f6d1826b83b4197e628bf36eead712652ea75027a48148059fb422bf03550f60639bae9064af3ee671b9168eef729fa30153c89843827a9991da684d72ef07e000a8e29e909b06936cc43d82c5bd6f7a4108014532483f674ed28df007232003493fff270b3289b402ba1eab4bf90cbbbdddaec3e70fd0eb648c0fa61b2c","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_3"},{"Input":"18db259896bf7a55fbd739049dcd87bab41bca06dbe6ba477bd91293ecc7b72a080f9707ac098f3ac7a06fc4cddf9de9024723d7803df1e832f23facc344e09f1f0a58aa3d929cf73ef5fb29717b46818570f8acc287057a61f1bf19952d5d870a985a1377ac0068c32090c4f61af49e438888371fa281863746ad9c4505839a1635829f9dc1168b264dc1b35951410b3f509179a86259cd1bc219e0d9fb6571282e9a7a2784db855d8fc301a21574e67751c63574f552b5075cf0d54feb2ee7086ac4e5ca51c4e84405e5310187119e9283a8e1d4b9be261bea666f724501c302d9e8305711e2081373125393ea672966c2b7931d9de75640aacce9bdd8ff8109610ee10faabfae8ddd3bae29f8644762b842a90a4baca50bd37c4c8e346cdf2c0a29ba08d26cd5e2ea3ac028f2c13854b997da5de4020810d8d1df0b5ede16181628b1a3d5936ef9414119e8bc454d0209022d6c3b6722ed63bd3be9fd11ef16a94dc0c53b0283290c9189b46b2b2e727ac54299b70b73ccd31eadbbbce239183aa0627c185bbed1b35c46d0d2ab1e248f7a4ab6d1896afcb8cecd471911ef208db0fcb30c4d601aecc21ad3457e4a636a2bb289712a98153589c3674a774f08c0f49c7c290fab83aa67c3ed645856531b1e537e060fddb3b722849819cbfe04336ad8c78b7ecd068f1f543b4cf09a510bed3f39c8afd71b549b957d5b4fdd0f2251c9278bb30892b0ea4fbc771380481fdad33dfe0e9f49916e476823feed199cdc78bf86665cfec1b451e92bb8122597a0b28bdd5d380024756fe070aa101deeac40a4d371c0154aecdddb082f6891c7c3a5074e1b5c1b30e4b3ffcca6980bab17266f609f8c1616cf6c23c51e60b24d2ebcff2c7e486abd11f878ad231b2e69a5773f6986e289b67c70b92da16c0c8f2a8dcced737641061312f5fe17952151ca3275c32561490b233e7284a4690a777da485efacab5557307c6d8e588608aa1e46b4edfd86ff457f0ab244e624488e4563be1a0ee35df8b547c43e4e0411b1dce99279dab100f4137847c3bac64342dc95b9cdc2e5880ae61f898181d8","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_4"},{"Input":"1688243c91950e540e5acdc41b84ee9d2b1c0ecc84dc373d6fd5959baebb33df140c8a9c0ae42792290c177a97425bfbfaf7ef008b7656e906c2df69fbdce89a029c4ee67b639e4b6f7cf0536ba1f24bf61837351b5c74f12fb6cb0887aac3ea1d3c75ef1b03ad925b1b223901a8e7a4e16ef2bf6600e7c175529c18ba8008350aa0f1fd72acfd2bc3c50664eba62133ea85341f0b4ec0c3618f3738401a42ce1fc3592b9642716c47152c9e52881fac74773f705c15b7737e0831ced7a0f8371245c36152deacfd18a947b3f3c83a30652958dd9614b7beef8aa8b5cdb157462ac85cca98a10e6ec5efafdd593179f2f56c4febb1ae6f1d72a9661fd8ed51032b98467d16a6e8e6bfb427838659b8df567ca38ec17c979560bee66d1ab72f372d38efa3e51e7ad3ab06b5610e0631db0a385101b45d0fe47c25610213aa023208289e3f0845bc2197d57ac44b4a808b30aef09a344f80062bd75a1844715d3d0cf55d95f2fefda6a1bab5796626fbb6345796ef134109de55bd61654f54c71f16eb795694ab923473ee66eaa59bbf61de8f8dfa94c481cb4f224b3ba90ec87318c3ec099e1aeb3d63840ccba2159386cb3d71481ef18b5466901b71cdc6e75e1ce807182d1b89fcbaff954b80d8a58d49c953c05a64e2a326cb3ae4802a0f9f29719e2b60002727f32b49638ac3c1d4addc61cb260c4ac99307ba24a7743ad52f325d4f32b8477cbd99a218c2e4ecb3171cab016c51cd800663bec05624f1072775db17ff93e8b807067572c97cbc7321e9690b8fc4f193eb0f1a93ce96a5c20cf7cc3dbf579b54af41204b2451098724e9d8940bd4cabff1605f33592e5f002817dd14edab5e60dde89c66b3c93dc057c53b926b2077d63d2dbbf9316bd64c2bb1b35df3f9ce28954fbe15eb52a08aa70453112cc94ad923616aec0553dfce0d5e44001e7b139917b4cd2d32bca418f24f44bd7ffa16edc47ff8178efee06f28630c2816b74f232864deeea9073bdc2128d704d2375431c7bb196825b0ba890b5ecd90caa2107b346a40472b9972bdca3e388e38a2914e2c1f022a82d49b4f","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_5"},{"Input":"0eefcc4248b67e40298c48e0ed47dca1dbf6f6e377506899430b11bf03b17ede10c71a49ccfb43f8475de84b581eef9ab283c1238e172bf48150f29e1e56201204c9eaee6321d6ee36b8670498aa1c73665f3ecc7350c8ee09a573e692d26320171450a989ebe50e365b92d24c311fd830972e7b16a2523e634175c4e24c884a1a6bf429331081e6e930bd36e23bdf91bb8180a6e4129c11655cb9468093ceba0e9d0caa4d47983e49ee253051aaefd86cc6dd367384f89e2d166bd721c658122a55d1dc0b4bd2f33984f0099a6d44c85baa10aaca32d0ec89e494d24514a68c2d43c0e6576ec46f88dbdcc7c57d685a3eb9342ba7c71b62de9e6373bd9094ff0a716e218f8e4174821a7dc090fa93b7683e2a6e363afe3eb7287a7dcbf9ea090dada034b037e05a7c8b232b8c691b7f0c3b9a870d786e913d936f8256f2e8c0131bdc0ab71a147cb4d91bba85941c00ec91994d2e9e53b1929a14ee2e876dff021d8b2738aed0201234d3919498c918fd5340541ec616542d92d955df4c182902237ac6c6802585c60640eefde7c05fbdf206a5ec25dc3411a4e9b4e6b57be611bbc865815ccfb7c666b117834cbf2a7edd9c2baa691ee3d453c3652c49761b234be3f502cdf3d1e6258eb846afca7acb0fe13577e226ddd277daf32d1d0c5c25e39a0be0435820302ccb2763016b10d0dbde867c745fe86cfba8cc925a2eb72d4bb48beada74cddc1c791a980d9bf7601ce0aec444354a5c02185d392f66f50e4860e017fc0453fd1bb0453197ff5abfacd35b90e7d9b03ff0900cc857ef7f2f0ccd45b2e137fd53d99d027b79aa390ac021bf4d6aa95cc96bacd6a071386e21f73cf3d42f0ed82ca6f673bb89223e16386c7328385edb01eadd4ce28e5ab418861414b0b511cc6300bd0cc6c95538014be8a4a20d8cf56d74f4a63225302b1f065e5ce6c676af04344de377f580aad377dab935d532f2dce9daeb0f31d6900af84aeeab7ebb4390e6bbb4218558fba894959f2cc2fef8af5d1c16998a38c6076f86dbd858db5b0b15ca2cddb0092ef861188e0434f4232d7e3e884fa42bba","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_6"},{"Input":"05fa7cb22fb8809abb745dc167279147fa174e0444cfc99a38f23f2b93e49ad62f7f94d2d206264a9358b54fc96ac31921948cf3c13d121c923991f0b47c04291b2582586c93d23816aac8ce2f90eb913e554211251bc9a2d64dd2efb9a905b80f73f8ca76c166aa5735d9370cf944306abfcac5a5874cc95b00b569f7eba01a1d92dac05e6fe60b5131a0d2e9c459d4b3deaf1a462332b285f90a25e5b61c211467961e060894bc8111abdbb66365b9da4a6700f2d530ae69289d2cc32185d004951597c8a0547ea22859b97dd87a37a8b4125bfcb513eff29e0bcae7a7dab00cb314388716efc2bee7b4294811db5bacaa3bcf720932e253f002b53e968f930bfa9409e358a1370c298a8cb622a0079bd7bf1a0e32e8f8dde7a0f56877945d2f95f233ceb61db914a2fc789bd35b3df37f62329a886194450ccc9e708d900c1fec898ed485e2970646f939313b33a6d3416476fcb249a96f6f11d030093f890621cd7af05f335e3514d7f10972f2b56614e917d9c4cb85178cce40f09a2291211e5387ed32f8c6f90d4c90d60fb08844a0fe0786717113aaf8060208331d3a03558a26134672a33a540774a5a994c549f2ec4e048e99c45f6f44692fa09ecf2d625e843fb3c186d9502acb5c9c3e6d0402ed815d76cf081e3ed847bdf8b0db0caddcd63f8dbab0b1ffdd45ae118ad1dcd7c1306c75a1f448a0e610994e5f300c058a66f63de3e94c81e96dd0cba0182c1371f61006d4cf9c9e24cc439d0cac280db62277ae92b4403f31d0bef2674900f1c21a0fb715b5cad4689154bbf53c2430b990502a5231b79e63246d15293a37918adf641bc8f8b18462909c8d1eea14434c1a33eb5a3b425729a7885db91c54240d3e8049c3d10811709dc86e71ed08d9b41714cb20a4d28f608d83b541cf998bef0ea0257f477e00c180b4e8ae4a0698e1e77ae24715eddf432eb950c3e26972a08521c31c484a733ce217b944f6190db804087bf4786ccbde4b6e149f2471121b29b6cb21d1f7baa6be1ad6ef18295d7425b744106a6fefb550e079074b1933af2bcf989c833a5f133f8e1c490e","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_7"},{"Input":"273cc7b93b70bbfde8748d62210c033579947802560fd05dad9d65003482995b1b7379946dee0b15e90720dfff72cbb60b60b5aaa8490ed2b77e8f57a8906d1816836ad1a19ace6e0feaa4195ee6006280ce178b5987341221c6ae618794c10603ea811d60c99786835aff3871ef0163cdceec010fd806bd11adeae2a91f4cb6179a512e9627d2af3dfc8a36850e144c7255568b3154d41797e2ac4782234925047ecbe78073575c62a32129476a6456c61545eb98bbf925ae1d673a32d680e71f20ccd414fe03613ff7a3d330ab8bf8964cba66cdf3af8bb955cdc729014e692ef7c335beb80626f05301278f5e3b228bcc981736a258ea6bc4802392f081de265db5dda279bf034808ed23c8613341b31d3d29a2f65a367024af1376d1bb9907b7ed2f35386382b00fb0afa6c5643c170010e35824d8fc84d3604690fa18b404685df83549a5573c7228b29a7dde8679d8771a19e8af709ca46c60f8126e4b033a388adb6c574f85524ff523bc7c44b49c8731b763836e0a4ebf054f0008492b60683ad457d04d91f082e169606c6e5d6e172845fe586925db00a8e6a242c409a8c11d9bd07c2ebbdd653519e9a0f5d29576033ebab9a564e1b07d8909b014232fdeb565d57fecdf556f1b9753361e8d7ceaef0d4778b4669643a8f980ed9107e310cfb6db05062d60b0810fa4aa6ff9825ebc58ae839c844f82ad08d9a273161549f2d3778e678f8167640fde4f43d30f2f60d7ec8cc83673d0e5100235e405c743fcac26a09a44bfb9bbd6f767ccf6dd030879dae04027353dcbfc9a89af0f566f9e06f1a6035c08bd9873e41dc3bf009005f905fc0b9b74ea57a16c5a2411c984cf6d6d274f79bec9cea6021001d0729f412858eda5c909d8bdce6a24980d28d7ff8a6bc01cf9ba50e6613c5495f13a39ced02158862b29579c4cb0e947142fa8592c012a3038fe1178bcb515a016a06651e387087a9110ac43ea9548a520537d116ead0e2498e487613ebb81ef0f6f5210cb448e92f2cda4a4f57013ed1f922e1c90d8e8891a308adec863c0b48faf4b7581e0565ded5a8ac6bccdee08","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_8"},{"Input":"22f2e1830cea84486d7ff320365a5ead50d356379d7bc4f2b9d77dd298f36fd8087baa2aad4fd8bbca8cc0b525041e1b17aeb3ecc63086cdc6fd1ec2310bb8db243fe8ce837f106ef17e387f9d78df833811427d5338c83f0622f92a65101da31ab23d2eb26b70ff15decd58baf91a68ec85f149012d481da67f806b0571c9471928767c9a5576ba5fbd7cee08203e6de3d9dd36a756fc621700031081a80b7e1aee70334cfaeec2f7551368818353fbd5810266c7d1ba49de47cec9b81b6e82007aed597093879d894ea438d094f3373b51f31bd5993e026e76502bcb1334e32189a92e9df00c0b5c21811462933dd17a5328075b472a53ca61bd9e136701571e529e7c4912cb565a3819473e8cb2c45e0a7b17a63f11e509d80472921e86f92fabe095002240b012e60bc9159383bdbf515def6c41acf180adde6913625a7b11d22c04a21962ecdcb1b679168cc14899e2912f233ed37d116cbde2110af4861ca847018951f8d71a369874541390d23df3cf9e5031655d4e7af4affdda5ec50fb5b633ac619c06ff0312901dc9d949ddb443ded5f59662788c101859ed0cab1782bd118c15612ab18ecaeeb9226a0145c0c1d01428987b8a5523c6da78e015261c22b6d73323e863964a003fb93594743f70e9248ed6898776780a7f3ae7a41b07a0d873b1ece5c55c6c942a7c7249c3d9026fcc607a68fe6e73cd4a76a6821f86065d5e87ffecc2c47bbd85658eb21bbdacdb79f23b031a8c6df93e64574c06eb93cb065183692c4ae4dff2efd51615d926782da50054e4a280c5bafb913d02369aa17493b4fccafbe637b1b7c9e1225e7b6fb24720f778548e304e7617b407e83fd6e3da09599850d6399059fadd57c4fadb492f59ad3adb4783ccda65272f190a9ca862a6d9508d9b2513edecf47aaa4fdc7a64127dcccbe64b7f206d400148ee9fc305099c6a7334452f25473a58edc7161895ab52e3590ea600e2e885010b062ce0df9974072bf87f5a1c8072df23e612b254b688084b5958314efe190bb6fb66657b45e6f81fa7c5fd41004f815ffd08ec00cb46ced2b1f97b04e69b","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_4_9"},{"Input":"03d333c171b569f9721355f4b5f9569c18006d55ea805d0ab72f392e6e6be88e230688a302d20e6934bc1151bf8a0af65d4294568f5af0b041197aaec74aabea1aae25b6edb4994684b2877875575b74c14a19eb068d429accd0bbbcd4de1d110b2f112b63197fcaa10a2afb08cd221bd509c829efecdd4a3bade00bf947cc3911796bc946a8148ce73aa901e2e1f4dcb259b11ee880e088ddff65f5f6f05d441ae8c9a28a7ee1d483dc47235e16e19303455ee1b7c6c29fdff01d3eab2c4e77284e25e7b8203b7b40dbf1bfcdb4fbdedea474fd44ed67dab27a031959453e9b0010adb1d55c492437f0bab7e1b63a56467681f06a29aca6ab95d29d5fd23c3529e107847478c3dd0aeb69d6c4345dd0239ba105a1bddc699512e027bbb34b81111903892d003d32111610c7ccd4c529f75cc8bf33a894f40756510ec8b9bcfd0402b66e82c6b8fd6de9652d5c81821f69445b0dca7cd052e1811760803f778a1ae8318c37a3652bdcab122282e95dd3f7393b3214e8ce290c01c9345ce81d1c09304eb9899baa26aa963503f8a55ed2a5d0cc2d5d0fbdfae81c3a823790d2371874cf1b2e447a896844c5338098f2ad9dea545e40d5f5a4369125d95fcd5acf0c0ffafa0ba1c1053fdc155d63329f5d8540fe5c6a876793e04913a1e6a7c88815fe284d364a500612c376e7bd39a466e1b9c4c0a85b105d15a973db33a0f1d42ee64373074312ec2147daed5fbc660ff99664dcb993750af8f192ee51b849a51d9a24c4dbe4f69715d00e8ede2f32c2a54c5e8f8a57487cf80dad49915cdc18239b7847b2fe9c17f926ad11e5161802872b6607265d5bf10c737d9eb157506c05725034e5c2a941efb693478b4401e684afba8af20cfc14c53f66652c737ab71657a4156fc5dc9ddf2b07d05c72395c7bb98f97743c6a81dcc11d25dcf313890effb8dceb430ae9009afe11d1f00e0ec2ca627ce9c4919287a319590dfba56d1d76f3288b570588497d0e5cc88341ba9b40b8fee65f042836161d718ebba12203bab8927db4e4b4dcf9ca7f4250c61d0a055985996d04e0c76d49bc83bad37e0a1a1f642a16d95eaddfb9b7a403bdd032f07c9222813df4dda4aa3054716d7622a999ac90eaa7bbc4ec78bb5d47736aaf04f706ddcc4724776a5dc0bc39cd1a0c9c5fb89113f93dc81db66c1ca13534f16518fb0347056c08bac62a1bcd1b201516a4f52fca78d9d140d40687b093176eb90fb236c03cad3ebf57027afc11742095f3a98a957815f7b13e263a5f11bccea9f6e716e219915e9075d9c0c2a8e0260e553a1182aa35b5d8d710c9705010e1350c02b6a217ec61245bee22c8509810027b242574ec29b652f249774d7320612dde5ca36f20f42bb169352a568e4c14a972b4ef4a1ca49f0e4b095f77ec5929486d1a051ed3b766a40d442e8e7d3b04ebc527aedcdd807d94774c23dbf3bf2841a2a0e3272e10431a056b1fb1224d16565b2f5350a0c8bcdcc6a3a2d189cc488c6c88cf9a0bd213248f73095f4ac0116d2a932043b527cb2a7c42e329a00310c9418da803179a099418ddb9ed859b06035b9b8fa5ebdbcc460641e8af2bd20e68e62d50563672a52294cc0e94cb331287c3cc9c9b8f389de88ed033ca26234d38089a712dfac171b8c8d743c5a2560b1f5c5d64fb31d6830a6c982fc8daafcc6b2ac02ac20685e11cf211edadf2bc01f9b7d3b716110dbfcda9974d00a0e90721e9aae490f3e0ba84b55cefa94919197ef9a4b21ccef5186f0d9801a25cbb77227b2d8488fa8da35e8c70495fb6861997575cfbbc644daf21868564be6a9fbfd216b252271f08fce405355d84d49028f6c5397686e765c5157034c2ed2f92e2d11c7411613f5c60b5ee50540df6fc025a3e1aee7b30e3113afca04fa7e3949a54f65a25aa8241d5056f289c3378a72d4730731a6659294dfe163718d63cc6239d09033ba48004c52a9d55d66317b62493908d3215efe3d2cb77ff6447a971599b2df711a59395515c4cac93a0f2211fada2e1799efd65247699ffbc3b35cce7d210a61e868d3bd8abb37e20bd5afe2a628ffe54a17a274af70c3584b4f9a2e567c6ae5d5a00d14ac7ffc12d04e06a03d1fee23fa99c63fb8a760fe4794af4221f7bb7ceb194c7df2c63859c8b0329","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_0"},{"Input":"0bc588b9295424a643de2b40a69c9daff62305085e893ac5c5d404ad565cb89f064f181b7f3fa23a51b829fc7965ddba879c6fb6eb4935ae33756fd01a603ff006bf88db9882537afc39611a5da601b4461f23b8bcd724de1602d10ddf2400e1103d4162b724a3f59c1dde973ecd3086f599fc3fba3092a392b247ae0e555352086554bc12b5b21906ff1f5cbb8ea8065b8384364f1d75dc43fefd5be2ba6e2b1db55d47bb0e96bc45248f7d8e3576f91e6c7db1ba7bd60c36faa62286ae7b7120f8942c679c45219b38e8eacc365bca7dd43e7a5c72457b6c4ccffe1ed38fff28676ef29b9330535753b41464f583af98d87d9af5e453e26e5ae2b5b8900d6a2273ec96dd5d5ae35442d2a401aec95af380f6b94594495f7db684a7bd21cc7115756d01443923350e0f55d5a4572478e5bfd895af1671355dc71962d475d97e0fcbf9663bc4a5135ec00c0275fd629e2c671486523ac3cb274f3c021e434f230e9f37feb748aac515c58c6ada11cd7668939a75cfc4a641c82a88dd642fb39c1b901ae71fb85cc4a5990d19fbe23eb4ea7aeef7a07b5bd9a8628ee7eda77bbe150a9c622deca8c8505253cc14e2908bbbbf8e5cc2e812e8f7a93ef3f3b7c37f1d5fa37baeed342068aecd69557b859a833938de0192b80bb345648eb1af85481421f8a6cfe15954d7c45e82e8c192368f817e1de8a01803d6ded2d258b889c92424fe2cd68ba3328ac7b2042640ed0f9107499dad072a1170bdfc11e3999b7a177288e296a9e13a230d6d2ac704f62abd9af434cc7172468268a27b058cc6822b911b724343991820ddeb0507ba1efc64b6da9bbfb66bcc6dc1968334e59e991987ca0264f9c8da5964afb471dfcee45009ac14dd37520ec63f022ea4ecbb522c466e5b405cdca061c8e56af4864f93fc16271978deadc7bfb667b940186527218b1308f19b5f78e38826e8c0edfcb7bac067f8c412dea480afe0699e1bce28306386adad44c4dcead96aba3c6ac2b511c2050fc36c108842dc68adc692b46006c7437845b91104ce9492712dd05eddebb786e9e50965a87ad9388d375d014b162aa118ed010d6fac2133e46dacd279605ad141d548ce5f6df47f2af040c689127705a6dcefda95143ce3f33a8f1e2bb5d3a6023d7a8bb405337d07bad6e9982b7521e4e177303deba61b19a3c11675b6180e29a79f720c56c1ddc77771969918ecc306d7cf8f2d996ed438b22234436042472402e589dcebe4d4ebd56f54971f7cef6a8b3e1b08248d3f42d957cb5d73d7680167d985cd5a71d91b1e8bd5601262d2cc8102679d1e3765d15df5f020ddac2b2745b108b0d7291700dee47b7b13d6d567250f967755036b3aab4fc48fd80efed33154469ed1deeb843622138723655e3bc692b8b6c508dbd5a6b02cf7f7818224636792dc569f8b5098062fcb1343dc85466867ea7c3f0afc7e45520cbfd8fdf56a3a3679aed086a391b6af57216d573faf61d6405f3d65a897ab4a5a77d94507d8ec790300157dd57b46fe3916316960cecdef7d1af1aa631a1b74da59e4ec618405bcffc885059c3c74585b1b87f2762ee8618a229bd0da5cc35d3d3e920e1863c2babb0ca59bc19597b8200f579d17c5c51094be0e8c425f3cce12e814ca719411c869863d14e1750bba991e0f1d9766b2dc000b3c7451093e5f3293a3c9d36bcafd092d5a19224484ce9100d9659a9571a96dbedb8073a218101ed8f5d224d0b11ca2c898a2b05368adbf2f27181e8fc713173cc3655a599ba70f25ffaf2f29a803d2f58145b6e25327851b55391c39fb460487972b6c817e566c9bc35cbdfa6a89c4643ec632000828ba17530c62524376df313c12934b1745e14167666d4147764280ae0f6ab03c2d4f04f0f5d5ff2e39df860b936e87569da2287a7fcc00e9eca8c9860ece79ddced50c566c803192d5f6863d56d83d10e49dcd60d34555b23d7ef607bbb9a5531b390d9b2016ec136313cb56354fc69c2c97cbf5afd99bff85948e6e730cd37f6cf819fcb2230faca93dc6de50b15aa3dcde0f7d0864056c7ccc11c01b61058a483109171498c1064ca213a3305f1ff0a2861aaff3646bc6a8c8c71804874714881f2a8aefc2aa3e7c8fce2741f292cdac86484aa567e188ee8a908aa5acfaf0dbbc","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_1"},{"Input":"2685aea6edddbd98ca03c2df477608e71c613e56d9817eb504fd4646d5699f8c266d709c3621b8da9d775d325bd8b916e112f456067cc94c3cdaefd1363ae58d0f238e6e421f1e1a80659b3f3d53c82da29dcd92c8f3e7f5cd6d144264c962e60f0e5f7beee4ab3ca6c225dfcd70cb1dacea4fb1c11efcd92a9bfd000f20385d0c507168460ff0ae1f4a3f4d4d40c2bac5f44e18eb22f961d62817a542ff63111ec38e34e8c84f6b8aeb81ab4d04f44178977880ad50136de60a1d62f3b4fc9407245d2ec4baf533022981fbe51d195b64d3fbc15db7d356f6bd96e425672fa3183cba898f86a37b5f236e41e4938fb5badbfcb19a929fb0b0a945d557d9ba4f0bfae5037583fed3a2d1b726b9f5352a512d1c35ee7e8997aa57b0be6a07b3220aeb130f7f24d035942c0b51ae34ad675041dd89d49c0854aa29036687872f9f105a80888c3259e1ab38ac878b95449da892c2424c0b73a32a4bfd6c1fc2b035166da5e9e14dd821a2bda674e8fbc7edb1c8e1edf32a7fc1a57c5a0583f888460ae28b4c49f27952ca34d10e87fba821e197b891224167a5accfff0a2d6835ca0517e1a590ba9c9f6a14a0b105a34bb5d38df06fe6c87ad79b5902445dcec7d824ac886acc6b9e4405b69d9650e76e92eafd8cd4c670d0eaf4b87894b775221c105c4529534d44bfc6df876cd3f64b459730aaa58f02daabfd425016db382dd12c241b2ae39e4730028739a266d7428bb0f186b0177a5151d821aba508d907ac1ba083d662d28d081508cc46353bd659898905e68ba2d8291da61ab408ac83462f96a39797f8f6957f9994c9e83022ffcb23487eeb10fa010f62467449b1db9b29cf62c0d6b266473eb0bd57accde054bec7804cd87c63b4cea3d9ef688c2076300d74c7b5d90044fed0a3e144ca22f49fd0f519d7f9133f4afe84ed531b8e5c151fba2c92ef82b4974906eaed202eca91413bbef2c19408110d86ab097369a8128a72e0ee836a38fe369c34ab28832467ca7e7da305c7f1734db29dbf5ddd1d2eff09551ac5d3fe228c9cab77da07fbaecb7a4344847b7172858963a81c1cc92ba8f8505e4001622b17c3324a74f572f7905655a37c63dbf2b1e178378c9fb31468feaeb5aef9dd81159a28c201ec816b0bf2358d5eaaf2994f79d945e9f9f22f5b9427cf97e32fbbc4ce9f9bc8b203b5ce92a13388719701140ab94b927d261e807c4698e5ae5b179fd8bb17ee8ac91d6a071c09885c5fbf27529cec212fe8075e75c52a3598f787a261bb7281750b87fe125ea914a2ad8bd77ad60b5994101456c38d0b29c7c9d5251dee44c53ce5c1e06ec623041469154939fca1497a2b24aa5766645e993d87c143ac92f67bd241ee71f36fad6359b04ef25d1e106003161e7e243a350dddd4e5e71f3d260498acfe2da15ac92e024ec7f420dc323b111c0704235cea83ef84cbf533f0b559302ebaf637df3f4d8b68328fad9501355b00a96d3df4f329e4cfcb81daefa893d65caa287cdb330844d31fdc35aa824f8f01b9d722f2ca441ed789275d21ceca9f8b4ad5a2c64d1467519e364ef77bec63054ce19859936c36a5c396cedfae169291b3a2d8d005fed1e4b104f991914fc61b0215a5d38e3bad0ace8354013e8b6ebe73d96fdce88d550953235643111bde05ec8fd2737015151f523cb38a6baa918a4c98e6f955eb2805ede92b82292986061700c3e0189d210d3fb0b4a3bd09bfa0a00241b386394b4e10fe3d480f6c4922823fd2cf9d6134fa302cab2b1fab5957bf6fc7dc4268d5ec0fdab9bf854cf01b07d712153db5c2cef41f822eedb1ec8b86528fb59a12b9362f406c784c55c32b3d0294f737850b94d2ae395964456e19e826fb958730c06e01de0f7f4c3ba9243d95d6a537132236597b565b60453d6ea32c75afb2d875c305661b30ca22f615014ffd20951f8ad3096b7f66eadb520ff3dcab6c86f8fa58f8d486fb0e7c1c2386a1d6047b8807b70d436be59291dd8263a1f3ae9813355f2fbe58010f67d20f576105e2efb422ca422c015e9394e16e484b6ba6da85746f121888b43159d61b793d3497a73545b2e74d525a1a67a201a3a0b9926a2b94d33ed7aa5b0158ff20af4b861226ba9a2c474ab446c2c9eb22f77fe0d45b8b2a810689970885f88b","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_2"},{"Input":"1891e8689d81bec3dadf1f9604c15044cf827e14e75defde61944ff7bcd6030b1c9d52e52a1ec18146b4eb9dfee11579a281998ec99c3828854c69edffc5cc1a1c2be993abe1a808d8adcfbb9fb05aa82c12ac5aabe25f1e45638d8f522f06c005d8941ad434d5775f021bf825f48b90d106afdc75383fb209244d52f1320ea706ea0f67d737534e83b4c371e90088517aaa2f2a663d8e20ee66fa5d96d9335f28f837eee92c44da5329976752d99b1490668677a12f6957a24e87cf06f9bf14277937b372c4dee270475b2c3950a50dd506c82a326944fb1faf73392ec5a54c22ce783eccf8f06cce6ba0c21fb0b4dc405879b8c4e8e8971df765815f9878a41c8ef188c94fed5973836a452d4d365003c8e56af3fc6effda3362c29201741005f4f78eb380df7e435060e8ca35c6453d8e4bda391e23e47d8bad4d28921a5606d86dfc9574f67d7334c2e73ddae937a36c8c59a81c3f25a3ff7336c024cfe21b86dcf0c54a5ede73ad5c313d5cf5438a1e6745b3b61b18635d3d3d5f51df5d2bc6c558c5f3346e9a400595186a9c9ded299367cfb3a0674f23f1ad0350cd251f268d166c85bca4eb0b3cf3c53bce15abcdd22e22ce0e9758a23c6ca5dc7bf9183144119ebb7d667bf302b4039bbad10ed360d3e0ed47ea273c22083d7fc62f1f5ee2f7167b517eff34f496919da38c6c35093638ec958dc251e7ffa9c173a00809cf94fada7d2fa86013db39978eb153f48c11003b37b0b772700047f4f2531895fc8b4f8b0b884014f229aa5d3f71da7438ef65c446e0b427de019cb6f37b18b768d980b9e6f007a41802ca6903b9c02894bad35e0a40c0b857ca9d9d36fa301afd2c4a0e09c76b8c2cf6e39bfb2ed74f709f4f0108f13b111a04b2411e3515b772fcc87fd59b28bb73361c203c9fcaec94adb10c921a4af2f72e5c8be6ad1626d2029044cefbcee5973de8f419f525704d7558e86bdd34502c56da56cde6282c36358df918f4065f03d107144d581ec4d0d4ded89a81f0c276e5653ab7712b2a995c15310672ba6b4431362266dc719d5cfe3b23dd83a2a34b086c18e02b045ebfda6fbb64a22a1d7709ef5f2b065ee20265d4900d004eb1936ddc3fe60f130649fb7b2ec9103636131656a7a47d161e53af88ec99797c4e8e7051d1732607d5ae2eddd9ff4b12ee5823285651ecc4cf5e6241392f72234e5e6b240575900e21d3821d0ac2c6ed2f0e0b615bf1dc36e365bd69d182733f951be4ac6fcc44116425c28405a420df7e495650d5d0f74ea7dc27ee928d8c7d5907a13416384b15eaf7c8c478da036be27d1498ae0d5beb855aae2620577d9c541e36d2ba1ea62043c1911c37372e86bd8530a0ed3dc407c769f8f248a502e719428fb9706de6289e167378b6422d0155dbbd7bd3d1b677c64746ec51316ab8f37a836110ea3e2c62da428e3a53f8e90b0cd00523d05dcb5b1c1f00bc4c66f4e8ffe62842dc26273a383e45c3e23433bdaaee67b5f685a21cb5e85f331880fe736c4642f972ff17a4a947747c14f722f3136da4a6ef86769176b96a30ac6a3d11fe16860200fe29c194cb2630c988439903421dd883caf0e05e3f82e12fd09bc0415e0814c41d1ca9c349188b686207c5acf04a3ca7c62357ac5a36edffe672b5c7856f29fba11455c3a42f6b1615d934140f1125e8f3d0a813580c49e6f668e5b7078d6b3389145fa1296893fec25354e91427e890658f8e2c558b5f22499a5afc66f8106a8d1af7bd16c15088cbb53c0f95ee9222efdb4c3ade4c08ed74d0747077bc87b86a028e52618b80c87fd7819da4433ef2d6232011f94be93a335d44854e0051008f2bb43f5e84934e286032ffbe10716fac0266c7361df4464a26a832697780089c258e55ac985a00cdb2800aa4126b748825466e4bc0dc02f2b44da5217e6f628413d728dde6aab9d4d8ac72e8eebfa6d70cc8ba05532c707dc8ad0d151e7645dd1a35debd99c840c3c31f3b9c17b7d804c1463e41e9e8d5bc2434d3e863d4893a2fd7432b507d5d1a63d09c555d506799665c144b84d13301aa04d2c68a8069c628c6747241f57eb659356042cf5ecb6f917a6ea88bf2e01a70bfcb317c7a21cd20dec788c2a74ab921d7b14fc855204725acba5cec08a5ff8e3fbc44a95c1493","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_3"},{"Input":"20b961c884a0ad42964edc3b580f607681058a33aee25d60f7dae9f060217ec119d77c703d1a93059a1baf9c17319ca9f1c5fb4a04855d6aedc308d9070f9769103dd1a1691e5fd39ff70241d7569c17764a3824e49664f5145f956abdd559af262f0e5677944e1834c3b5459e0bfb126d41a8afd73bfa12ee3ef45f17e81ca62dc7a825213952eb841f8942a113a5cbda01359751726ef26ce05491815806cb2bbefb942c709bb4fd01181e99ae1716ee28d0453ef725cb8ea45f7a4f93c6b3203b2360ffca610432f4d1053e633004d7a822ebd940e9a546a7d0f2038728921f1aa6348e2cb062d5e193a49d60214f2f0752fd32b57319b877958ef0f7eb9c1b32607b1f0ce57571b1c609260f757911ed8bcc3896e6c95a13655d11b6a7f929b9eabda75ba7d16a31ae4b7af7d0eb1bb349169df32fa20f4cfc959e722e0f24d21c9b7e68ef0e4e941aaffc472a7fa0b9d03b7097df3c993d108fa69d0a8b189dc5d953fcae14f53a800fa624429d2bbb94ec56f371e65dce393dc602ea2a04b4e2312aacb2061b9d55e615845b085696a908d76944bc822ca909212ac6bd05bb2fd64a97c2dd2e76b9ed90febf4fc236d4c466ab7afa4050101828fa67a02b0d987e9d5d717c8be7757e34354fc9a57198e5d8e867a8ff08b3e3020c84cc015904ec31cea03557ca10f0b7f5ea8b5215c80c01f5cc47bcac78c0d2f1f312135154f38704a2b7db3dc7c86be733e8880e07aa0712c2050627bdf56acd40482f36f73badb30e320c12bb66096c8bd7b978410dd5449f250519c6a122ceda572357aea961fec54427a6a1e6cb2d509210e0a6c049d39eefcf09191b561f361a2d372ecd78676d8180b6120c432178bf969e889d2e386ceb20642a0ab00f1c28263dfd316e344e8f9c1380b0fc5b3ce5bf524cae2978f53e6dff5c6cdf0743ee0c68b4b256d924d9635a4cfe45948e1f5c137369b47439912b113818d26e766d0ccf83a1e2015a3ac6f355345d4ab03f710bda2bf76f42eb7ae70aff6f955184031af5d2333c1dfca388231e1d78a53749973f0a6a01f8ad0018704c7db134592cdc9744ad7b88e407d0eb04696b62fd57be16c249043f670d881e8fb7573f9d0cc54a632cf9de52ee862a07e0d9dc11a3f4df8c9fcde0bc80f9fda1b8a84efb0b67884d640c14cc63211267d15a405fe9d03f9713fc79cb787934a778928b1f244b2e84e926f2d5a7e6d9a154873abd1899a7b20df25ae018279ed525e939da2333d4121573982541bb5fdd5d6570676ca6a2979f92a21a563924eaf1b2cc3c16190560061e4eaf988b117bbef2a7380429021d7190eee78b725bc42613725d2a2e3d1167ad6d74508c5322df36e614e10eedb067f8cfcb9573e9281b0b827e282d3416ccc69d13090246f223e743d077d3d167af6558cd17b8110b422b70462dfe3d677fe5a72c5125c9e67534c06a4070f0bcbd8f6a5d93a7691d63a2811e1908a4f98042938fec22a2372ff55c50d679bdb4dcef9a1ecc1dba4fbb54f6ba153aff2b0ba9da968ff1b07f22e95f4404a924ce1d68c9d4cddb10aa9680cdc2064aacfd45913bcc620e4cf277913eaa093e4b3ce25f40d1ac255f863fcf59c2066e98d7ecc5219edfdec9bdb718416fc65e1b48f3a5e33f40a459600bdf13c8144d022ace69574477c864aaa2cf989a862940f80b2c85e5737926e3fd1e18411d06df3079e19f11b85b2634cb2788caa2fa53cfca5de8888091dbd8cb73b3651e0c60e28b318329e56ce5cc18581966834f960344fe9645682b800bc138e5670f8f23befa1ac5f9caba7fca7179167d2df6ea237dee29bccc80eacabd5a51dc06f245eb13c8ba185388b1de78b300308010a551df2b5311f3ae838318d20297179bbf83449adaf1360b0b10494e299a9cc71672ce5774394144435ed65a061b230c57e56d08af54493bae064a3d94aba34faeab22b8056c9d1fd0b499bdade429c428761820248450189abe14f51cf3274aae5e2d65644e9866e1e5342faff915cc65f72d8210c6aab304b14ce4036862bef5e03cc6269ec755cad0f1bf285f0a67d6a9e5be0d4fe6e50b707331076cd11d96ea9c1c98575d21ca59083964fc06691da6e6e12b833b3b126f159d4fe2619b89bd86423b6cb9bfcb34480fa78c","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_4"},{"Input":"1ee25d5450b2735be0093c130b7448d1e411e1e8b7913fcb232598dddfaaa687027cdf0955348f4e55caa9d82fd5bbb38b92f60579a83ac709966683f0d9793321506909285bb1b77ba279f0490a97f2161fe1ab39f7f049c1313a9c8811884e15a20e6fcaf1c0bb0715db97d4626fcc02f7ba2ca64b784b2d4f6f51ee71a03b24732d58be9c0dc31b21e1a719031094660c11a00221284d4163228d8c3cab2e1588d6aa85a6f8ae92b8f3cba309971208f66b4d0863ee99d1d5e8fc4c96cff02dcfc369cc91404a1b8309a63ce750324264c2a70c62ff062762ffd9cac5b91a03571ba65a25d22dc4b4313271c8dd74a99fecf08d6dc8e6b51333a428984b1d1129479779b4c500a4200354648d7fbfa67c9a0cb13a3d0e6137e31ba7ff47d207e78fa56ce0c8f6f4e5f31f678a1868f2c7054f796cadb4bd888681492ebc750bec8d2972a70ed03036210e367258f5cf446378f1a21029a48b152fda72fdb6141196031b0f6bff06f6500d7813f8e2dc009f3abf8c9f9160a9cf29fe29b7c11a3d61bc9163aa0403faa3151c0830b043408955dea0cd7e9af20095f4f5763d2acc09a5367d70eb2f0ae60297138f91ab9df8f73a32690ba35abfd521c0e7ce010d36307a14a7b50309a5a73da3fc8e5b6260531b90ade6a08c01cb6ab7c933102527adf2e3b149232e6870a91fe9a0824cad125ea9f88a531a4c39e6e3860b1c2f1e41fbe9e39f018bdd240daa5e7d050b1d72a31fcbd3c57af3fe7623ff3d194460469ef83ed14d5a5dcd75533364b4c3925bb576850e8abcba7e06441f4e2839f9e57171e29052d593183c38c820ec1f54569a5739f8f028321b725b8d6c001822e194f48d5f008f5ac96d24eb40d7bae35c1f7a87bce889392201b7753924aa964b9b82cc1ed724314f55fd3bfe84a79984448e0a309dbec6f202e707000167456b1aeff780eaf36fe6e74450efcb52c0bb8dd225ce291ea32afd792d4726de9cab205db543a5a385432ca762ea52e445402e3f8532377d89b1d99e17c2272e0742d0904dc8076735cac193377a3534a15229e89605c870331e8c4a325c1ae9f4f66b3f9f6744978a7e689fcfc60691e30e53aa60076b17d09c84624a4b19392f9dfda38b2a543220172bd50eb5ec710884855168a7072e2859252a58c82706373a30c0bf2aa918c2870d43c410f52d116fcc691df9b4c4d9f15a10494e10aa74c6d7e9351528814c724757c648365a3585981200e312fd1d5609b9a49702c6cfa5525ec1bb6df19cd838bdc322938b233c080c3e4885d608028ba06b6d2865e834807335d255e605fd759b0bb6834f946a31ec742859a3d72a6da48d9803095c0ab65997de12a21edb99c4892a6c316dfea1a30c4bb8cc601ecc32f1cd2073ac7bbf3e87be5b54b946001b7ac91092cef11b3b2b0e5163c3a605876c2d0628b3e51cb8edae03423b9bddd8e83315bc96135b725525f42965d283cdd3a0184b6465341456b5ebf3885665459aed84e3cbe071324f5016a9ea977561031719dde6c423f21beeaabb44124d104c07e52ac303b44119fab1262b31b90d47ce14fc4af949f7d3e70387fb7a06f7605665c600fdf6e42017a446621fb351e91b130a080b0636f94376096d780ae1123ddb2a7c474da4028e1585206dd9e2c20a075d52d462d5a10121f7dc323a7bc0c5183f79f337b863fbe0680b158ecf81402958c671823cfdd8f2a372fb01ab9a62d2e9926a7eaca029d96672a0cdaa60591e352d8b7b64557a1f8752efd985adfadc50cb8d84a7757521961bbf43aad9b9216148f860853968329a3712dbe936eb643ee767c8c57c1c39819832e7ee4aa920c013bce746641635ea715962af491bec1d478f1164b1daff3800998ebb24c011b9731675637ecfd7282ad4e7703dffccd386472f84af4bfd1613fa124894ae006f44be7e61d498ff3d57006afe82d3255fff68c4dbc0e3ae72b961061069d3140d573447252faa1bbf6628974a8f127cc2540c6fe651c1a4537b85843bce340f8d932a56ff47605daa21e2bea8bc07ebfe9152548fba7bde2664df992583a221a9678e3e3213636f1ea195108c161f3fa7c46d09b4b64918714c33ddf8087a2fd9da39ae9f5ab723fd232f646615af7682dc44bc2a7ef3e1665fd6db5246b6","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_5"},{"Input":"09d599a8c97251e5d1447076792891b8de719e3fc5b7b245e35c6a8af97dbe1b1cefb6a792c978414992b8c88ead994e8fc972bd8018a40e306eb0e5578b25ca21312203a90480fc7174fba1bb4b9802c930c38aff91399e78e2c76768daff422bbd652137299ba4ebda8016f7501bbfeb6de219561d3f924693efa9acfca2b81618159fd6039f1ee36963eedec8154312d592e6e1dba89d1747c4251bdb725a0c6d546f20a8b44f4517290a078310fd1af065e8ef20ed4ff94c0fd7b7c662be143e5a3414336749caf364eefdc5e30ee73d34d9462a44e90e085851eb93c9b6206426d020f657f36fec865bfa09bc619746c238c4cfa643529eea42aff19dc52abc81b078bdca796d77c4ac57e6a248fdf370d3cd12163c474aaa46eac2cec103b3cc21c58b94796153850268f274376dd6f1a36103e6f870a4eafa452923802c6ca8c36c4a0225e4d80bb8e62aaad963cc39bf9db92e1a0f45a353c82039dd1821a59d0fd59b5d0a0362128b95414fd2479542e1afab6e8fb23e06e9a547210cab4c461a3179e2f0a15c1a20e0569590bc30c8c7344a73db5fe76341efbd2c1d24f43820586b1449eaed8c7063d4e481a1a52d0c5a318f9b69e97c35a6393b016dcb50ee485d31c090cfffa92e709dd5bdf6b94dea9a7310f0c824ba78151003ae718fdd5682ae467b0f776cfdd02db3be8d2aa9d42c88638bffdf154920121c3580c8ff773cdab1bd6a9449d8f4d0dcfd5d3e1bd55acdaed1afb96e40c0dd233eee0523e0a36b7cbba083eb2919ab0a66836dabc699f6f5864d284090cba90d02024b706a23f6d6f84681ab223b9975aa15bfc6c37c1e1d43bcf8de71b10f2321d3e00d52c53f18415f2e87cd00940a6a3234e7400e170297db9b214a596d2a6ca0984a6e1aa1433eb239c30efb32093022dc1ec379567ebe899606324afd22052844ad5cb4241203b35d930334fdcb22754475653cf6138f51677c5759d0034a7400dc24da45b5d4c63576a83f299cc60b43b186de9d8d5120d36596299502031424a44c5970eeb1090ce35d7379a0654db261594a27b7ccec8bab8db83b26534bc2216a34a20a506366d22111acbf059df2198654cd69f1e4db9cf7d4801d135d29f2973a802ab859a35550a844ecde62bdb95e86bb6e974453b5bf2d031a4f3c2aa1d0be4af8f9013414719e7b75536b3e0dbd8250fa3feacd17416de420df18f7491d891ce8c01acf25ed54d37433830a1bb5f169716b07aeb67e4e6a05ce2b0d962f82ebcc36bca7c238f9e0d83f4b731b4447b2fb6f24636aee57260f1a72ebde3694aed548f7d8e20b3f9e119260b66d095e9992ca8645292eb45b01cb4b475f8cdb3126bd428b18745e504371be364d94d482648251a0ae1bb9ae2e976e6fd23476a9181e42a874a734f7efd1dc25037765e35a92885559ef4da81af8fe9ef0615a50760d000b68bff0f102771b9cf46bed9d5ce8565a558e41700bd46d271edbcac6a253a859cb7a172a3176fe6f9eebaf752417603efde4bda114f8788c8608a32c27fb3087189d92464788c4b62525f616c8725f6abf9d4dce07b8399a0e69860301eefd202480a12e054a920ea3dca3844f6309232c61fcfb17c43e311e336f593615004ed5a4b8be2ebde42af53d5fa32a7790ca80fc234a3034f9151c43f2eea7585049b4aad29392d9e9285b011b73cf2c6af955bb236b15857fb2789ec0149c253b23f8232627bd48f36ce4e8bea688d8b64d7469b2072912fb4719cb6894a14986fc875c84c220649f3c48e3a662f4c4f8b0c259529e2b6d3696c45cd1b11f02bd752022b0e7a2599bbca90cb8290789d71bd990d18418bba1e7fca94de8833d95851c88876c42fe817d3025a5076307c85b59add5f81771cbccfaaff14f7fb8807bf88902cdc39ca0a398f8bec67971cf8b771d941204515478d4c8dd014aa27e099cad61aa267ee25fb61d23e5d33ed0e2ad08bfd60c2af2b362d35851adaf161bf289d100b65ccf2050aab6c951e44a010140914e18ceed56ed6655d046f37dd75198590f95200ada816be88cff9fb1ed2aba308c1e556fa4987309f9d3c380432429cc72e8e146a24c339c989d2266c0f1503737075913b800fedc3b85ed5060567231334467aa6104824b79ca8f99ff263fff52","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_6"},{"Input":"19ca1f96e3289c5656d4c10352144f657c2a89428b7d4330f1e888e3b5b4c15f0de956aa5db71cb39c88cc5ff11e8df5787040891ffab3e2322331fe4e6201b02ffe5b0350192676ee46b31b41c8eaab8ff9122487b9a86b2147b709e37824d02d129f764acabf37e490ce7db53ce2b4209312ca1ad424ebe4eb2ffdaf952a1c1d2063094e546d7e3cbdd07683995f7cbeea09048a904620710923326bf480bd2ee7a2404a0ab6a5b20587081e2f0f630fcc68a139dc635f8207585b699423471ebb4494c2132e960ecee3fefe726c9e8eb207ec6fb922848c20f07f3b6d7ba82ed48a102edf74b6c2a0c3c00047d6678cbbb864407f6760c775ab1266a81c9f2af3cb009291b217d880b7ecf3b88892fde66c985b2b35301d57d9d318401d601e6a4284a43791de04f5c26f29e2e25cb24947adca7e56e1a1ba03cbcba210873004b001fee1e9c6bcd17fdc287f3fd7d4fb0e7f9bc7cd8913037bbb6cd274c21e18b879ae003f24a516097ab114e576c31fc502205e73c1683e0cbaf5fccfe4227f0999fa8cd65d01d281d748ead19540ad1357f3d274b9436d0613bb6482ca2e483b36141dd465a809d324e89a886b3f2a5ba534b0ed5cdead910976659e400f9f0c6e1f4818f8f3ece446e040cda8d27f2f4cfa38e5afb0ca691ab8d113912bd785bb3e736b6a7afacad81a158a57da7a97b1ee82552a94dc17ea082248871902e71297e8e6e985600d8f1deecdc5451f3479e26eea5b396c5352135dc756290ef2305fd43f9275f9b6178180c0ebf3491805239d48e69098c098bde9b6b020f6e519b7863257805e6ffd912294a88711da15864cb78e502c5b7b1c634549155ac0155509b12aaba95cdd1fceb1b5495d5939ea66d3eb606d69f55f22129717993d14d3e9ff2d2f28a318be897d946db05f16e0a705846da2456158d7b98516435dab869cae65a3c9ac4014e52bff7cf183dc4d82706c2ca79104e79aa37b04c3eac831729d837db141e3c7240a766af3fefd712513d9b7704e296ba3c66d2f5546d9a6e362e67593a4ff76ced1767499f61a0aaa35c9adc86e38c046fb0c1d550a18938b0b632ea33b06861bf18aa759f7d19948f4c24789908e40109c6d0ee52be9aa3f2c00131231041c97b788f3d548f8b28bad245a8d744a57bf5c9b137ca7b30ae3f2c8ae8fd3e84e2b8a4ccfe1904994adea9749f55ba7a9bdfef206d28044f2efdcce3ebfc443ae1d8052147eb6d42d0343c206e39292aea132250de83503b212199cf0bf67e3c2a54a54a30afe637ad5f6d74c7e0e0f4f881c1d1e05f6e8cec4135b5c008a1bb843dc3e857d52fcee2a520298bab9282ac416d30ee84e99ee78dfc286b28e60b703136a332de95908117b63c477f309b2ae97022ed6f1a54b994516164004cf5145fc37e8c4871a7055dd1668f797e6609b6d881f6794947b847bc4798f90f274abe605139ed0a8c08afdbe5fb842d7e0ad4e772cf7cb838b23413b68a82a4fd4cfd1f150cd6182df388bb9bfea8cb902ad77e108d7e448a55857369705cbe23efa58bcb60731c977443b7f3b7d78a144579b710ad1010ca4cb30c2d8e9f44f401461baf57093514172eec86e1f018971e28c8d2dd5b5b43873bc63509d967e2b30bd4bb162db4745faf47a64593bcb04c4dc342e7031b0e887aab9abfbe75f761421d85d00fa7a862eb129a79bc9a3dff1345b17b5e7516f4aabf9a49dcf75d6a53ad9420a22f793dd68d9c3aa7c942b6131fb01222d75b7a5d00f267c9bf23d1da0891520489adc3ba71e158d06e2b936a42915a91b7f99341ecbcd5f65b99e100a5d3f01031383ec05efa2ca4e8f8770283922c588ddd38c3efd160bdf296ae4aa7ba35c958fd9e2d77ab1774ec91a252906147e4d85e1493457ff7eaebb505f8522b556c4b6f487680fe918699737fc2dac2c517bf5f945c38479ceedab8ad69dfad8818d7f6986cdc40c40c7f254d837aa190518980d7b47c3078000f8fef69839a3946f26bcddce283c172a481ed4add416f39f0f0fe9c767d85d2c362ab71b74782c1cdb836a060da40adbdd3a401ee80727125b95da0ba13ce26d2dcf5e685924fca207e9600a9cdeb4a6e569638258300e435d19bca9105c292e6b1d3f4faa9c9640c014bbf0e537156e1140954609","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_7"},{"Input":"1fa11f66353f7a956254f3b98892d9c0f81a654f6609e0f0af13c2f8766c6f16194657ac1800e822825a05aa50d889e603dbbf4fc8e954f3b6f25fb23d5a5479142bf7baf53de4fc8668bbd4c019c0f6b7af0fec0c17ab0b107c83d25e4d18a52ef429052a9b35b41d51163583d57d79c420be96a1554051174b9ea1698878f11b36ee817c1b97813172a6418fb2937898fe7847dde90b6ff50b6b2a34cded942f593eeabb3b9d60300b894b05f78594a82af66d9117ea00224b4a21d7cf9ff51f70c6b295fa70454024594501c593b3aa26e4a93e66a0f38c3fa1d28c1612e91d631437bb2c0b4c9848a8fd19a1f8e3ac50ae338c9b516cc4049310ba4df21f287c33f63043e7e6ab5a1d13f7e759924743b9e46ded795c2c5ab93a909a3f7315530c731d9ba3398de10f401474f70944267f7ae20d0721edb0c0d48aacdfb501132d8f620cf839dfb326b07e46879ef281630486e202f6a5b3ee25d7bfa5d80e850b940cb4236be15cff7fa6f0afe97d4074d08cc84e9fc2c3af23892d61b63026dba27ff40c376fbe12ba44f231cb58178d22dcdf94e555a0fde0266d30002dab14694741bbac573bd15720b01915261df1442a03b424fbeea31b9f525c090ba172060641818b4c8221b8e01aab9788bb736e262e18f179221f6db532bae8058c38b8572443b8e87233d95e71b98acb2c946f149becc8ce8f259a89bffe962917050d74536bb197c472473578db35d84baa6c6ae9f9b436f0be637300423f198a1c23d945422603b6867cc421baac70d469d72d81380292e07297df63b5c01d61214817267145a6b0aea09a9bf2159d7360c4a7eb0a8c28a7e455c8ea3b540b5fc38fa57a56e8987689e52dc7ca0342c83789b8cb5717473014e1802469d01ca6d73befee617ad1012a1aeec56bc4d85e5f9fa8c98a49640c3543752250e921ef6447d718c318e7375b8903222b36ca85554932dcde661bd0e757a2037f4a1ab9425315926458a4e98d280cc10ed597c6c1af13de72471b52bc15d998d3232d2d849b0f73599c8ef253dda011b06a0f5c7fca436be16db5a6c389fb9cebbe0cf834015436d91dee19a986853c38160ad423bf52b4131487b5dafabece4d352e5dd076ceae35ef158c4b8016d3e4358c5e00a6d6fba7b3e319af57cfb7eeab1b64d4fd598ff6ebc85bf4e00445567736905d23864b42926019dcb0d924e90113ab53b025ba35c881dcf5a659f07632d4afae70d36c2cc547fb18898eca81401dba824b34f4ca6aef0edafa43b22378182199c62f74664f8bd35ab7836b318526669195ff4f5ce7fbad03d22789113e2328e4c4e49e3d81c78384315490295817075dbe3988e067faf0e1b4a9341a202e2616676282e029960b4ca852f528ed0e3f852bd6074625bfd6f46948973ac6ddf8fd1fa87a304eae65a6b2a53d50ea05db05521cfe31ec72869bcdfeb019276208720a0d33ee2ca8ab00ec09099e9c235971953f2ad06d4df01028cbe0f6449758345cb1d4ae4bedb642f84ca9e69e21eb1b1d639939504d29f7649423a34e9ddd3fc1e1648d3a6f717af7efd47f841696819248f6d189b54becec21119346431546391cba18e4badd3660cfba53a91c3b2218293d03778fafeff37c2ea71c3bb6823a1eeef64065f4b45cff7169a02a9934fda95861ec8d0768bc5cad58f79d854bd32503a3c72fe40417cee1d4d81a9d1e78f93cf5cafd2362809d788176ab88c1007a69f234a60a50cd0c00857600e144962a0dd987d284313a55210246ec51f3975707719aaa3f8cf6ef757c47226a5907f5019a2d5a82cea4b790e0deb00f70646c7124b8202b5f29f26dc20a12f4430158da8c567ff51da95b9e26ec39bbaac5e32f6a3cede1f933a8b76aa805201d3367d76248671d8412345ecaf89c599e5a88b93d347ad99a6e249af4a322a73a9d41b0d5350111018c3d9987926037a027101683dc814ec07f07daa46d103731cfa07557be53f12153ae35de17d65274fcc4bd4e15ac961d837a8e68ad24b542fa29ffca7fa5d883d1413e6c8183aff268ab0b4c516d88088a0102e2fb2999edcab90a5efc1a3f78ef830660161c361cf699eab00341565881f1c374f50b5012db4af00642b53ec15a66a315cfcbeac29674c3e619870d04810bea6268","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_8"},{"Input":"2f5a89469bfe012ba76459c35dc24c1aaddec05eee130879f7258d71c97f03072a93777508a68599449e21e16cf2b640f0ac5256231548f2c9e50ae4ab5e2ab7164dab50c5945811423d8717b2f08c524dca34fe055110059a5bb8efefef74e41c89dcffa0b3df9389dbab8f04a89cc52d76485eb690c5a80978f41c33be93f42a7f7a7181c0431613af56e9c2d461a71c0dca94aa3e19736d8cbc3467d841db2815f183c8fb13e36b83a9a6dd99238975ee7578b4dcd316c82818ce012092902aea97784a5249ed59850e30f1d7eb74e9526ad94f5c32af18124ae5913cf456064aae479b94db0dda94455282d2a5bcffd99d5e7749d631125dee0f33c3c0c1288aae58ea8eb4202f12883fc0fcdd68822e03f63a12d6242b0087a8a1495f7c1be646ac485c0d2b972185613095e4d65cc62bcf5b3913de994b430027ea5baf0e30fec7ffcd23b4d7a6adca3c8c994e17f94c76042af6b48441963bd5b478ab11aba040f130739fce83eb9c79c4a86eb251f79152f92afe5d81602744259567095129ca02f619571508cd4f14e669bc759afdf2a98f105ee1d19cc5c8666a7b2059b1d1d6524ebee64551aff3c1c058bc84ba359a85153c6840f9fb99406e4400750e00181c4c86d88d338f58ddd60e5a5b80f83d1926b199951fd27012377c28f1e937b0867a30e1617c85e1dc0a867aa5807b719aa1496cd41acd4a3cb2991018dfefe4eb662fa1a8c892d1971077cb3b81e43c75f412bf4ff67fa9e815cd179228ad0e86867ebd465e80e0523cae366cccf230dd02b58b4dc334f76d8efc1c1657225ab3c04d3e48ef50feb4b1133655c54465ace6d5f4d9222997430e302028fe47b83ec99feb177ddd4def70f8d7c3d774e6198476216ee7d9409b159426b7fcfe50427a47af55719f445ce0ac65f6b444825fa7fb4aa602fa343ae8040e62f09e7e4fd1db2662c0087ca9782f37626d91df409123399dd5c2823268f725da972ca1fae6732cfbf12bd9e5869b5dd7ade24e58d1914de52a6e6c24df6e25c64ab46e4bc7f94167fbe9daa225946046a1261b42fa6d1de0d471ec68aa9f22401b525d3f7e9847bd13103aa28e4bf2e1f47a545f3f7cdcf1621fd9c50cc10f0a2bb7c52925b718b23d79422d7990c7273427262817896b3aaaca889ded820c917c1610cdfcc5821b58b4aea58c86d5793d520d9d8da0a26503fed44591a409f0a9eb7fe6a724ac3f3f7e156c549fa4b51fd0a05231f3fc26b3754ef8aa031a9235db06ff5857739b5ac772d671f809e865cd6d9549bb2f4372f194ee2db50e8e538da6c93dc08c2e85e029c1bfaf6132f18723c06bda51310ef1b746078a2a81296acde9cd3c2de9c5085d319a33d367fb2d19db1b610a376087d1d4596b22909b27282665c97809407bce99fea04e3ebb87e95e0df79e41d67857447f71018c7682174152456cd66cdd61175e8d139985b502ef4a4730334311d794a9a219b49ef5a08c7d3e6b4142a36591873fe04e3f6c218982bb9a7892d8edfb33d90c10f844fde21c32be49991aff39b1c5d5a0875f0ca696b016821fde000d76711b26c07ccd8f5fb96f3a5a5be4d92116c5a786f1f0f762029fb04ad749802df01b91fe1767d07ad8d6fc7b12dacc12c1e0dd32301b60f5579b2e13d751ea08d620421abc8a1bf2246e62468498a1550687dffd7e15c5e7a74fa55b81d5bdeeb6236265d34c29d7d7ff5cbdf071e6489a784e75ed652aba5814530dc2fe2a065c299c3011037f4aec635d14bd3c9a6113b7411d745cdbe920133008642310a3231c7acee4ae528f9cbd2aa8e73f7fdc37067fa7eac6ba22d4bb9b40b96fde7d371d47cd1b8dbc4d5d4a8bf2b60721b9c1b21cb53e268751ec7212804ad98355dc12f7c8d64a99628ac80dac6acbe0b16220fa9ddba015ba16f06c8c57ccfd506418ebdf0ce1738762d3505949ffea9427406d55605b54d05e1c8f1f63cc760f7818f76c8f1552dd6e7572c2e2e84e7ecc83febedf5a789977325ab2fea33a61140baaa1f9d64fb647b4ed54db970b32eb9aec6aba90e6e8ad68b2293e39ef6768074635c4e095ae39e6374b99a6abc4e89155d6e1c5a0a135703c5be082fd44d715060535996f9642fecc139d06a140f8f78b100099740dc0325156b43727224a","Expected":"0000000000000000000000000000000000000000000000000000000000000000","Name":"bnpair_8_9"}] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/src/impls.rs b/src/components/contracts/modules/evm/src/impls.rs index bf405846c..3e8175c91 100644 --- a/src/components/contracts/modules/evm/src/impls.rs +++ b/src/components/contracts/modules/evm/src/impls.rs @@ -3,7 +3,7 @@ use crate::{App, Config}; use ethereum_types::{H160, H256, U256}; use fp_core::context::Context; use fp_evm::Account; -use fp_storage::{Borrow, BorrowMut}; +use fp_storage::BorrowMut; use fp_traits::{ account::AccountAsset, evm::{AddressMapping, OnChargeEVMTransaction}, @@ -16,8 +16,7 @@ impl App { /// Check whether an account is empty. pub fn is_account_empty(ctx: &Context, address: &HA160) -> bool { let account = Self::account_basic(ctx, address); - let code_len = - AccountCodes::decode_len(ctx.state.read().borrow(), address).unwrap_or(0); + let code_len = AccountCodes::decode_len(&ctx.state.read(), address).unwrap_or(0); account.nonce == U256::zero() && account.balance == U256::zero() && code_len == 0 } @@ -48,9 +47,9 @@ impl App { let version = height.unwrap_or(0); if version == 0 { - AccountCodes::get_bytes(ctx.state.read().borrow(), address) + AccountCodes::get_bytes(&ctx.state.read(), address) } else { - AccountCodes::get_ver_bytes(ctx.state.read().borrow(), address, version) + AccountCodes::get_ver_bytes(&ctx.state.read(), address, version) } } @@ -63,9 +62,9 @@ impl App { ) -> Option { let version = height.unwrap_or(0); if version == 0 { - AccountStorages::get(ctx.state.read().borrow(), address, index) + AccountStorages::get(&ctx.state.read(), address, index) } else { - AccountStorages::get_ver(ctx.state.read().borrow(), address, index, version) + AccountStorages::get_ver(&ctx.state.read(), address, index, version) } } diff --git a/src/components/contracts/modules/evm/src/lib.rs b/src/components/contracts/modules/evm/src/lib.rs index 979ac39b1..60d6400d2 100644 --- a/src/components/contracts/modules/evm/src/lib.rs +++ b/src/components/contracts/modules/evm/src/lib.rs @@ -11,6 +11,7 @@ pub mod system_contracts; pub mod utils; use abci::{RequestQuery, ResponseQuery}; +use config::abci::global_cfg::CFG; use ethabi::Token; use ethereum::{ Log, ReceiptV0 as Receipt, TransactionAction, TransactionSignature, TransactionV0, @@ -26,24 +27,33 @@ use fp_core::{ transaction::{ActionResult, Executable}, }; use fp_evm::TransactionStatus; -use fp_storage::Borrow; +use fp_storage::BorrowMut; +use fp_traits::evm::EthereumDecimalsMapping; use fp_traits::{ account::AccountAsset, evm::{AddressMapping, BlockHashMapping, DecimalsMapping, FeeCalculator}, }; +use fp_types::crypto::HA256; use fp_types::{ actions::evm::Action, crypto::{Address, HA160}, }; +use ledger::staking::evm::EVM_STAKING_MINTS; +use ledger::staking::FRA_PRE_ISSUE_AMOUNT; +use module_ethereum::storage::{TransactionIndex, DELIVER_PENDING_TRANSACTIONS}; use precompile::PrecompileSet; +use protobuf::RepeatedField; use ruc::*; use runtime::runner::ActionRunner; pub use runtime::*; use std::marker::PhantomData; use std::str::FromStr; use system_contracts::{SystemContracts, SYSTEM_ADDR}; +use utils::parse_evm_staking_coinbase_mint_event; use zei::xfr::sig::XfrPublicKey; +use crate::utils::parse_evm_staking_mint_event; + pub const MODULE_NAME: &str = "evm"; pub trait Config { @@ -77,11 +87,38 @@ pub mod storage { // Storage root hash related to the contract account. generate_storage!(EVM, AccountStorages => DoubleMap); } +pub struct ValidatorParam { + pub td_addr: H160, + pub td_pubkey: Vec, + pub keytype: U256, + pub memo: String, + pub rate: U256, + pub staker: H160, + pub staker_pk: Vec, + pub power: U256, + pub begin_block: U256, +} +pub struct DelegatorParam { + pub validator: H160, + pub delegator: H160, + pub delegator_pk: Vec, + pub bound_amount: U256, + pub unbound_amount: U256, +} + +pub struct UndelegationInfos { + pub validator: H160, + pub delegator: H160, + pub amount: U256, + pub height: U256, +} #[derive(Clone)] pub struct App { phantom: PhantomData, pub contracts: SystemContracts, + pub abci_begin_block: abci::RequestBeginBlock, + pub mint_ops: Vec<(H160, U256)>, } impl Default for App { @@ -89,6 +126,8 @@ impl Default for App { App { phantom: Default::default(), contracts: pnk!(SystemContracts::new()), + abci_begin_block: Default::default(), + mint_ops: Default::default(), } } } @@ -262,6 +301,868 @@ impl App { (tx, tx_status, receipt) } + fn execute_staking_contract( + &self, + ctx: &Context, + req: &abci::RequestBeginBlock, + ff_addr_balance: u64, + ) -> Result { + let pre_issue_amount = FRA_PRE_ISSUE_AMOUNT - ff_addr_balance; + let input = + utils::build_evm_staking_input(&self.contracts, req, pre_issue_amount)?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + tracing::info!( + target: "evm staking", + "trigger from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let trigger_on_contract_address = + get_trigger_on_contract_address::(&self.contracts, ctx, from)?; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + + let mut mints = vec![]; + + for log in logs.into_iter() { + if log.address != trigger_on_contract_address { + continue; + } + match parse_evm_staking_mint_event(&self.contracts.staking, log) { + Ok((pk, am)) => { + if am != 0 { + mints.push((pk, am)); + } + } + Err(e) => { + tracing::warn!("Parse evm staking mint error: {}", e); + } + } + } + + let amount = mints.iter().map(|v| v.1).sum::(); + let amount = + EthereumDecimalsMapping::from_native_token(U256::from(amount)).c(d!())?; + if !mints.is_empty() { + EVM_STAKING_MINTS.lock().extend(mints); + } + + Ok(amount) + } + + pub fn import_validators( + &self, + ctx: &Context, + from: H160, + validators: &[ValidatorParam], + ) -> Result<()> { + let function = self + .contracts + .staking + .function("importValidators") + .c(d!())?; + let mut vss = vec![]; + { + let vs = validators + .iter() + .map(|v| { + Token::Tuple(vec![ + Token::Address(v.td_addr), + Token::Bytes(v.td_pubkey.clone()), + Token::Uint(v.keytype), + Token::String(v.memo.clone()), + Token::Uint(v.rate), + Token::Address(v.staker), + Token::Bytes(v.staker_pk.clone()), + Token::Uint(v.power), + Token::Uint(v.begin_block), + ]) + }) + .collect::>(); + let mut tmp = vec![]; + for (index, v) in vs.iter().enumerate() { + tmp.push(v.clone()); + if index > 0 && 0 == index % 60 { + vss.push(tmp.clone()); + tmp.clear(); + } + } + if !tmp.is_empty() { + vss.push(tmp); + } + } + for vs in vss.iter() { + let input = function + .encode_input(&[Token::Array(vs.to_vec())]) + .c(d!())?; + let gas_limit = u64::MAX; + let value = U256::zero(); + tracing::info!( + target: "evm staking", + "importValidators from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + } + + Ok(()) + } + + pub fn import_delegators( + &self, + ctx: &Context, + from: H160, + delegators: &[DelegatorParam], + ) -> Result<()> { + let function = self + .contracts + .staking + .function("importDelegators") + .c(d!())?; + + let mut dss = vec![]; + { + let mut ds = vec![]; + for delegator in delegators.iter() { + ds.push(Token::Tuple(vec![ + Token::Address(delegator.validator), + Token::Address(delegator.delegator), + Token::Bytes(delegator.delegator_pk.clone()), + Token::Uint(delegator.bound_amount), + Token::Uint(delegator.unbound_amount), + ])); + } + let mut tmp = vec![]; + for (index, v) in ds.iter().enumerate() { + tmp.push(v.clone()); + if index > 0 && 0 == index % 60 { + dss.push(tmp.clone()); + tmp.clear(); + } + } + if !tmp.is_empty() { + dss.push(tmp); + } + } + for ds in dss.iter() { + let input = function + .encode_input(&[Token::Array(ds.to_vec())]) + .c(d!())?; + let gas_limit = u64::MAX; + let value = U256::zero(); + tracing::info!( + target: "evm staking", + "importDelegators from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + } + + Ok(()) + } + + pub fn import_undelegations( + &self, + ctx: &Context, + from: H160, + undelegationinfos: &[UndelegationInfos], + ) -> Result<()> { + let function = self + .contracts + .staking + .function("importUndelegations") + .c(d!())?; + let mut infos = vec![]; + { + let undelegation_infos = undelegationinfos + .iter() + .map(|v| { + Token::Tuple(vec![ + Token::Address(v.validator), + Token::Address(v.delegator), + Token::Uint(v.amount), + Token::Uint(v.height), + ]) + }) + .collect::>(); + let mut tmp = vec![]; + for (index, v) in undelegation_infos.iter().enumerate() { + tmp.push(v.clone()); + if index > 0 && 0 == index % 60 { + infos.push(tmp.clone()); + tmp.clear(); + } + } + if !tmp.is_empty() { + infos.push(tmp); + } + } + for info in infos.iter() { + let input = function + .encode_input(&[Token::Array(info.to_vec())]) + .c(d!())?; + let gas_limit = u64::MAX; + let value = U256::zero(); + tracing::info!( + target: "evm staking", + "importUndelegations from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + } + + Ok(()) + } + + pub fn import_reward( + &self, + ctx: &Context, + from: H160, + rewards: &[(H160, u64)], + ) -> Result<()> { + let function = self.contracts.staking.function("importReward").c(d!())?; + let mut reward_tokens = vec![]; + { + let tokens = rewards + .iter() + .map(|(addr, amount)| { + Token::Tuple(vec![ + Token::Address(*addr), + Token::Uint(U256::from(*amount)), + ]) + }) + .collect::>(); + let mut tmp = vec![]; + for (index, v) in tokens.iter().enumerate() { + tmp.push(v.clone()); + if index > 0 && 0 == index % 60 { + reward_tokens.push(tmp.clone()); + tmp.clear(); + } + } + if !tmp.is_empty() { + reward_tokens.push(tmp); + } + } + for reward_token in reward_tokens.iter() { + let input = function + .encode_input(&[Token::Array(reward_token.to_vec())]) + .c(d!())?; + let gas_limit = u64::MAX; + let value = U256::zero(); + tracing::info!( + target: "evm staking", + "importReward from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + } + Ok(()) + } + pub fn import_coinbase_balance( + &self, + ctx: &Context, + from: H160, + coinbase_balance: u64, + ) -> Result<()> { + let function = self.contracts.staking.function("importCoinBase").c(d!())?; + + let input = function + .encode_input(&[Token::Uint(U256::from(coinbase_balance))]) + .c(d!())?; + let gas_limit = u64::MAX; + let value = U256::zero(); + tracing::info!( + target: "evm staking", + "importCoinBase from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + Ok(()) + } + #[allow(clippy::too_many_arguments)] + pub fn stake( + &self, + ctx: &Context, + from: H160, + value: U256, + validator: H160, + td_pubkey: Vec, + staker: H160, + staker_pk: Vec, + memo: String, + rate: U256, + ) -> Result<()> { + let function = self.contracts.staking.function("systemStake").c(d!())?; + + let validator = Token::Address(validator); + let td_pubkey = Token::Bytes(td_pubkey); + let staker = Token::Address(staker); + let staker_pk = Token::Bytes(staker_pk); + let memo = Token::String(memo); + let rate = Token::Uint(rate); + + let input = function + .encode_input(&[validator, td_pubkey, staker, staker_pk, memo, rate]) + .c(d!())?; + + let gas_limit = u64::MAX; + + tracing::info!( + target: "evm staking", + "systemStake from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + Ok(()) + } + + pub fn delegate( + &self, + ctx: &Context, + from: H160, + validator: H160, + delegator: H160, + delegator_pk: Vec, + amount: U256, + ) -> Result<()> { + println!( + "Delegate from {:X} to {:X}, amount:{}", + &delegator, &validator, &amount + ); + + let function = self.contracts.staking.function("systemDelegate").c(d!())?; + let validator = Token::Address(validator); + let delegator = Token::Address(delegator); + let delegator_pk = Token::Bytes(delegator_pk); + let input = function + .encode_input(&[validator, delegator, delegator_pk]) + .c(d!())?; + + let gas_limit = u64::MAX; + tracing::info!( + target: "evm staking", + "systemDelegate from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + amount, + self.contracts.staking_address, + hex::encode(&input) + ); + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + amount, + )?; + + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + amount, + input, + &logs, + used_gas, + )?; + Ok(()) + } + + pub fn undelegate( + &self, + ctx: &Context, + from: H160, + validator: H160, + delegator: H160, + amount: U256, + ) -> Result<()> { + let function = self + .contracts + .staking + .function("systemUndelegate") + .c(d!())?; + + let validator = Token::Address(validator); + let delegator = Token::Address(delegator); + let amount = Token::Uint(amount); + let input = function + .encode_input(&[validator, delegator, amount]) + .c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + + tracing::info!( + target: "evm staking", + "systemUndelegate from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + Ok(()) + } + + pub fn claim( + &self, + ctx: &Context, + from: H160, + validator: H160, + delegator: H160, + delegator_pk: &XfrPublicKey, + ) -> Result<()> { + let function = self.contracts.staking.function("systemClaim").c(d!())?; + let input = function + .encode_input(&[Token::Address(validator), Token::Address(delegator)]) + .c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + + tracing::info!( + target: "evm staking", + "systemClaim from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + let claim_on_contract_address = + get_claim_on_contract_address::(&self.contracts, ctx, from)?; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + + let mut mints = Vec::new(); + + for log in logs.into_iter() { + if log.address != claim_on_contract_address { + continue; + } + let event = self + .contracts + .staking + .event("CoinbaseMint") + .map_err(|e| eg!(e))?; + match parse_evm_staking_coinbase_mint_event(event, log.topics, log.data) { + Ok((_delegator, _, am)) => { + if delegator != _delegator { + return Err(eg!("Invalid delegator.")); + } + if am != 0 { + mints.push((*delegator_pk, am)); + } + } + Err(e) => { + tracing::warn!("Parse claim mint error: {}", e); + } + } + } + + if !mints.is_empty() { + EVM_STAKING_MINTS.lock().extend(mints); + } + + Ok(()) + } + + pub fn update_validator( + &self, + ctx: &Context, + staker: H160, + validator: H160, + memo: String, + rate: U256, + ) -> Result<()> { + let func = self + .contracts + .staking + .function("systemUpdateValidator") + .c(d!())?; + + let validator = Token::Address(validator); + let staker = Token::Address(staker); + let memo = Token::String(memo); + let rate = Token::Uint(rate); + + let input = func + .encode_input(&[validator, staker, memo, rate]) + .c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + tracing::info!( + target: "evm staking", + "systemUpdateValidator from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + + Ok(()) + } + + pub fn replace_delegator( + &self, + ctx: &Context, + validator: H160, + staker: H160, + new_staker: H160, + ) -> Result<()> { + let func = self + .contracts + .staking + .function("systemReplaceDelegator") + .c(d!())?; + + let validator = Token::Array(vec![Token::Address(validator)]); + let staker = Token::Address(staker); + let new_staker = Token::Address(new_staker); + + let input = func + .encode_input(&[validator, staker, new_staker]) + .c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + tracing::info!( + target: "evm staking", + "systemReplaceStaker from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + self.contracts.staking_address, + hex::encode(&input) + ); + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + Self::store_transaction( + ctx, + U256::from(gas_limit), + from, + self.contracts.staking_address, + value, + input, + &logs, + used_gas, + )?; + + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn store_transaction( + ctx: &Context, + gas_limit: U256, + from: H160, + staking_address: H160, + value: U256, + input: Vec, + logs: &[Log], + used_gas: U256, + ) -> Result<()> { + let transaction = TransactionV0 { + nonce: U256::from(ctx.header.height), + gas_price: U256::zero(), + gas_limit, + action: TransactionAction::Call(staking_address), + value, + input, + signature: pnk!(TransactionSignature::new( + 27, + H256::from_low_u64_be(1), + H256::from_low_u64_be(2), + )), + }; + let transaction_hash = transaction.hash(); + tracing::info!( + "generate Transaction: {}:{:?}", + transaction_hash, + transaction + ); + let mut pending_txs = DELIVER_PENDING_TRANSACTIONS.lock().c(d!())?; + let transaction_index = pending_txs.len() as u32; + + let status = TransactionStatus { + transaction_hash, + transaction_index, + from, + to: Some(staking_address), + contract_address: None, + logs: logs.to_vec(), + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(logs, &mut bloom); + bloom + }, + }; + tracing::info!("generate TransactionStatus: {:?}", status); + + let receipt = ethereum::ReceiptV0 { + state_root: H256::from_low_u64_be(1), + used_gas, + logs_bloom: status.logs_bloom, + logs: status.logs.clone(), + }; + tracing::info!("generate TransactionReceipt: {:?}", receipt); + + pending_txs.push((transaction, status, receipt)); + + TransactionIndex::insert( + ctx.db.write().borrow_mut(), + &HA256::new(transaction_hash), + &(ctx.header.height.into(), transaction_index), + )?; + Ok(()) + } + fn get_validator_list(&self, ctx: &Context) -> Result> { + let func = self + .contracts + .staking + .function("getValidatorsList") + .c(d!())?; + let input = func.encode_input(&[]).c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + let from = H160::from_str(SYSTEM_ADDR).c(d!())?; + + let (data, _, _) = ActionRunner::::execute_systemc_contract( + ctx, + input, + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + utils::build_validator_updates(&self.contracts, &data) + } } impl AppModule for App { @@ -280,13 +1181,133 @@ impl AppModule for App { match path[0] { "contract-number" => { let contracts: Vec<(HA160, Vec)> = - storage::AccountCodes::iterate(ctx.state.read().borrow()); + storage::AccountCodes::iterate(&ctx.state.read()); resp.value = serde_json::to_vec(&contracts.len()).unwrap_or_default(); resp } _ => resp, } } + fn begin_block(&mut self, ctx: &mut Context, req: &abci::RequestBeginBlock) { + if ctx.header.height > CFG.checkpoint.evm_staking_inital_height { + self.abci_begin_block = req.clone(); + } + } + + fn end_block( + &mut self, + ctx: &mut Context, + _req: &abci::RequestEndBlock, + ff_addr_balance: u64, + ) -> (abci::ResponseEndBlock, U256) { + let mut resp = abci::ResponseEndBlock::default(); + let mut burn_amount = Default::default(); + if ctx.header.height > CFG.checkpoint.evm_staking_inital_height { + burn_amount = match self.execute_staking_contract( + ctx, + &self.abci_begin_block, + ff_addr_balance, + ) { + Ok(v) => v, + Err(e) => { + tracing::error!("Error on evm staking trigger: {}", e); + Default::default() + } + }; + match self.get_validator_list(ctx) { + Ok(r) => { + if !r.is_empty() { + resp.set_validator_updates(RepeatedField::from_vec(r)); + } + } + Err(e) => tracing::error!("Error on get validator list: {}", e), + } + } + + (resp, burn_amount) + } +} + +fn get_trigger_on_contract_address( + contract: &SystemContracts, + ctx: &Context, + from: H160, +) -> Result { + let function = contract + .staking + .function("getTriggerOnContractAddress") + .c(d!())?; + let input = function.encode_input(&[]).c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + + tracing::info!( + target: "evm staking", + "getTriggerOnContractAddress from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + contract.staking_address, + hex::encode(&input) + ); + + let (data, _, _) = ActionRunner::::execute_systemc_contract( + ctx, + input, + from, + gas_limit, + contract.staking_address, + value, + )?; + let ret = function.decode_output(&data).c(d!())?; + + if let Some(Token::Address(addr)) = ret.get(0) { + Ok(*addr) + } else { + Err(eg!("address not found")) + } +} + +pub fn get_claim_on_contract_address( + contract: &SystemContracts, + ctx: &Context, + from: H160, +) -> Result { + let function = contract + .staking + .function("getClaimOnContractAddress") + .c(d!())?; + let input = function.encode_input(&[]).c(d!())?; + + let gas_limit = u64::MAX; + let value = U256::zero(); + + tracing::info!( + target: "evm staking", + "getClaimOnContractAddress from:{:?} gas_limit:{} value:{} contracts_address:{:?} input:{}", + from, + gas_limit, + value, + contract.staking_address, + hex::encode(&input) + ); + + let (data, _, _) = ActionRunner::::execute_systemc_contract( + ctx, + input, + from, + gas_limit, + contract.staking_address, + value, + )?; + let ret = function.decode_output(&data).c(d!())?; + + if let Some(Token::Address(addr)) = ret.get(0) { + Ok(*addr) + } else { + Err(eg!("address not found")) + } } impl Executable for App { diff --git a/src/components/contracts/modules/evm/src/runtime/runner.rs b/src/components/contracts/modules/evm/src/runtime/runner.rs index 151d66f84..a91e6e06a 100644 --- a/src/components/contracts/modules/evm/src/runtime/runner.rs +++ b/src/components/contracts/modules/evm/src/runtime/runner.rs @@ -1,6 +1,7 @@ use super::stack::FindoraStackState; // use crate::precompile::PrecompileSet; use crate::{App, Config}; +use config::abci::global_cfg::CFG; use ethereum_types::{H160, H256, U256}; use evm::{ executor::stack::{StackExecutor, StackSubstateMetadata}, @@ -51,6 +52,12 @@ impl ActionRunner { gas_price >= C::FeeCalculator::min_gas_price(), "GasPriceTooLow" ); + if ctx.header.height > CFG.checkpoint.max_gas_price_limit { + ensure!( + gas_price <= C::FeeCalculator::max_gas_price(), + "GasPriceTooHigh" + ); + } gas_price } None => Default::default(), diff --git a/src/components/contracts/modules/evm/src/system_contracts.rs b/src/components/contracts/modules/evm/src/system_contracts.rs index 57c39cd99..9bbcb9eef 100644 --- a/src/components/contracts/modules/evm/src/system_contracts.rs +++ b/src/components/contracts/modules/evm/src/system_contracts.rs @@ -12,6 +12,8 @@ pub static SYSTEM_ADDR: &str = "0x0000000000000000000000000000000000002000"; pub struct SystemContracts { pub bridge: Contract, pub bridge_address: H160, + pub staking: Contract, + pub staking_address: H160, } impl SystemContracts { @@ -20,10 +22,16 @@ impl SystemContracts { let bridge = Contract::load(abi_str.as_bytes()).c(d!())?; let bridge_address = H160::from_str(&CFG.checkpoint.prism_bridge_address).unwrap_or_default(); + let abi_str = include_str!("../contracts/EVMStaking.abi.json"); + let staking = Contract::load(abi_str.as_bytes()).c(d!())?; + let staking_address = + H160::from_str(&CFG.checkpoint.evm_staking_address).unwrap_or_default(); Ok(Self { bridge, bridge_address, + staking, + staking_address, }) } } diff --git a/src/components/contracts/modules/evm/src/utils.rs b/src/components/contracts/modules/evm/src/utils.rs index 33f2e41fa..a10e5a9c8 100644 --- a/src/components/contracts/modules/evm/src/utils.rs +++ b/src/components/contracts/modules/evm/src/utils.rs @@ -1,4 +1,7 @@ -use ethabi::{Event, EventParam, ParamType, RawLog}; +use crate::system_contracts::SystemContracts; +use ethabi::{Contract, Event, EventParam, ParamType, RawLog, Token}; +use ethereum::Log; +use ethereum_types::{H160, H256, U256}; use fp_traits::evm::{DecimalsMapping, EthereumDecimalsMapping}; use fp_types::actions::xhub::NonConfidentialOutput; use ledger::data_model::ASSET_TYPE_FRA; @@ -90,3 +93,250 @@ pub fn parse_deposit_asset_event(data: Vec) -> Result max_supply: max_supply.as_u64(), }) } + +fn build_address(address: &[u8]) -> Result { + if address.len() != 20 { + return Err(eg!("Wrong address length from tendermint")); + } + + Ok(Token::Address(H160::from_slice(address))) +} + +pub fn build_evm_staking_input( + sc: &SystemContracts, + req: &abci::RequestBeginBlock, + pre_issue_amount: u64, +) -> Result> { + let header = req.get_header(); + let commit_info = req.get_last_commit_info(); + let vote_infos = commit_info.get_votes(); + let byzantine_validators = req.get_byzantine_validators(); + + let proposer = build_address(header.get_proposer_address())?; + + let mut signed = Vec::with_capacity(vote_infos.len()); + let mut unsigned = Vec::with_capacity(vote_infos.len()); + + for info in vote_infos { + let validator = info.get_validator(); + let address = build_address(validator.get_address())?; + + if info.signed_last_block { + signed.push(address); + } else { + unsigned.push(address); + } + } + + let mut byzantines = Vec::with_capacity(byzantine_validators.len()); + let mut behaviors = Vec::with_capacity(byzantine_validators.len()); + + for evidence in byzantine_validators { + let ty: u64 = match evidence.get_field_type() { + "DUPLICATE_VOTE" => 0, + "LIGHT_CLIENT_ATTACK" => 1, + "UNKNOWN" => 2, + _ => return Err(eg!()), + }; + behaviors.push(Token::Int(U256::from(ty))); + + let validator = evidence.get_validator(); + let addr = build_address(validator.get_address())?; + byzantines.push(addr); + } + + let func = sc.staking.function("trigger").c(d!())?; + + let input = func + .encode_input(&[ + proposer, + Token::Array(signed), + Token::Array(unsigned), + Token::Array(byzantines), + Token::Array(behaviors), + Token::Uint(U256::from(pre_issue_amount)), + ]) + .c(d!())?; + + Ok(input) +} + +fn build_update_info(tk: &Token) -> Result { + if let Token::Tuple(v) = tk { + let mut update = abci::ValidatorUpdate::default(); + + let mut pub_key = abci::PubKey::default(); + + if let Token::Bytes(pk) = v.get(0).ok_or(eg!("update info 0 must bytes"))? { + pub_key.set_data(pk.clone()); + } else { + return Err(eg!("Error type of public key")); + } + + if let Token::Uint(ty) = v.get(1).ok_or(eg!("update info 1 must int"))? { + let ty = match ty.as_u32() { + 1 => "secp256k1", + 2 => "ed25519", + _ => return Err(eg!("Error number of public key type")), + }; + + pub_key.set_field_type(String::from(ty)); + } else { + return Err(eg!("Error type of public key type")); + } + + if let Token::Uint(p) = v.get(3).ok_or(eg!("update info 3 must int"))? { + update.set_power(p.as_u64() as i64); + } else { + return Err(eg!("Error type of public key type")); + } + + update.set_pub_key(pub_key); + + Ok(update) + } else { + Err(eg!( + "Parse staking contract abi error: Update info must be a tuple" + )) + } +} + +pub fn build_validator_updates( + sc: &SystemContracts, + data: &[u8], +) -> Result> { + let func = sc.staking.function("getValidatorsList").c(d!())?; + let dp = func.decode_output(data).c(d!())?; + + if let Token::Array(output) = dp.get(0).c(d!())? { + let mut res = Vec::with_capacity(output.len()); + + for o in output.iter() { + let r = build_update_info(o)?; + res.push(r); + } + + Ok(res) + } else { + Err(eg!("Parse staking contract abi error")) + } +} + +pub fn parse_evm_staking_mint_event( + staking_contracts: &Contract, + log: Log, +) -> Result<(XfrPublicKey, u64)> { + let event = staking_contracts.event("MintOps").map_err(|e| eg!(e))?; + let log = RawLog { + topics: log.topics, + data: log.data, + }; + + let result = event.parse_log(log).map_err(|e| eg!(e))?; + let public_key_bytes = result.params[0].value.clone().into_bytes().c(d!())?; + + let public_key = XfrPublicKey::zei_from_bytes(public_key_bytes.as_slice())?; + + let amount = result.params[1].value.clone().into_uint().c(d!())?.as_u64(); + + Ok((public_key, amount)) +} + +pub fn coinbase_mint_event() -> Event { + Event { + name: "CoinbaseMint".to_owned(), + inputs: vec![ + EventParam { + name: "validator".to_owned(), + kind: ParamType::Address, + indexed: true, + }, + EventParam { + name: "delegator".to_owned(), + kind: ParamType::Address, + indexed: true, + }, + EventParam { + name: "public_key".to_owned(), + kind: ParamType::Bytes, + indexed: false, + }, + EventParam { + name: "amount".to_owned(), + kind: ParamType::Uint(256), + indexed: false, + }, + ], + anonymous: false, + } +} + +pub fn coinbase_mint_event_topic_str() -> String { + let topic = coinbase_mint_event().signature(); + let temp = hex::encode(topic.as_bytes()); + "[0x".to_owned() + &*temp + &*"]".to_owned() +} + +pub fn parse_evm_staking_coinbase_mint_event( + event: &Event, + topics: Vec, + data: Vec, +) -> Result<(H160, Option, u64)> { + let log = RawLog { topics, data }; + let result = event.parse_log(log).map_err(|e| eg!(e))?; + let delegator = result.params[1].value.clone().into_address().c(d!())?; + + let public_key_bytes = result.params[2].value.clone().into_bytes().c(d!())?; + let amount = result.params[3].value.clone().into_uint().c(d!())?.as_u64(); + + if public_key_bytes.is_empty() { + return Ok((delegator, None, amount)); + } + let public_key = XfrPublicKey::zei_from_bytes(public_key_bytes.as_slice())?; + + Ok((delegator, Some(public_key), amount)) +} + +fn build_claim_info(tk: &Token) -> Result<(H160, U256)> { + if let Token::Tuple(v) = tk { + let addr = if let Token::Address(addr) = + v.get(0).ok_or(eg!("update info 0 must bytes"))? + { + *addr + } else { + return Err(eg!("Error type of public key")); + }; + + let amount = if let Token::Uint(amount) = + v.get(1).ok_or(eg!("update info 1 must int"))? + { + *amount + } else { + return Err(eg!("Error type of public key type")); + }; + + Ok((addr, amount)) + } else { + Err(eg!( + "Parse staking contract abi error: Update info must be a truple" + )) + } +} + +pub fn build_claim_ops(sc: &SystemContracts, data: &[u8]) -> Result> { + let func = sc.staking.function("getClaimOps").c(d!())?; + let dp = func.decode_output(data).c(d!())?; + + if let Token::Array(output) = dp.get(0).c(d!())? { + let mut res = Vec::with_capacity(output.len()); + + for o in output.iter() { + let r = build_claim_info(o)?; + res.push(r); + } + + Ok(res) + } else { + Err(eg!("Parse staking contract abi error")) + } +} diff --git a/src/components/contracts/modules/template/src/lib.rs b/src/components/contracts/modules/template/src/lib.rs index 9c528595e..59e8bc87d 100644 --- a/src/components/contracts/modules/template/src/lib.rs +++ b/src/components/contracts/modules/template/src/lib.rs @@ -11,7 +11,7 @@ use fp_core::{ }; // use fp_storage::{hash::StoragePrefixKey, Deref, StatelessStore}; use abci::{RequestQuery, ResponseQuery}; -use fp_storage::{Borrow, BorrowMut}; +use fp_storage::BorrowMut; use fp_types::{actions::template::Action, crypto::Address}; use ruc::Result; use std::marker::PhantomData; @@ -56,7 +56,7 @@ impl AppModule for App { return resp; } - let value = ValueStore::get(ctx.state.read().borrow()).unwrap_or_default(); + let value = ValueStore::get(&ctx.state.read()).unwrap_or_default(); // let value: u64 = ::get_obj( // ctx.store.read().deref(), diff --git a/src/components/contracts/modules/template/tests/template_integration.rs b/src/components/contracts/modules/template/tests/template_integration.rs index 481f22b98..755d9f412 100644 --- a/src/components/contracts/modules/template/tests/template_integration.rs +++ b/src/components/contracts/modules/template/tests/template_integration.rs @@ -127,7 +127,7 @@ fn test_abci_deliver_tx() { fn test_abci_end_block() { let mut req = RequestEndBlock::default(); req.height = 2; - let _ = BASE_APP.lock().unwrap().end_block(&req); + let _ = BASE_APP.lock().unwrap().end_block(&req, 0); } fn test_abci_commit() { diff --git a/src/components/contracts/modules/xhub/src/impls.rs b/src/components/contracts/modules/xhub/src/impls.rs index 0b4887b5a..5b402ce3c 100644 --- a/src/components/contracts/modules/xhub/src/impls.rs +++ b/src/components/contracts/modules/xhub/src/impls.rs @@ -1,7 +1,7 @@ use crate::storage::*; use crate::{App, Config}; use fp_core::{context::Context, ensure, transaction::ActionResult}; -use fp_storage::{Borrow, BorrowMut}; +use fp_storage::BorrowMut; use fp_traits::{account::AccountAsset, evm::DecimalsMapping}; use fp_types::actions::xhub::NonConfidentialTransfer; use fp_types::{actions::xhub::NonConfidentialOutput, crypto::Address}; @@ -52,13 +52,12 @@ impl App { ctx: &Context, mut outputs: Vec, ) -> Result<()> { - let ops = - if let Some(mut ori_outputs) = PendingUTXOs::get(ctx.db.read().borrow()) { - ori_outputs.append(&mut outputs); - ori_outputs - } else { - outputs - }; + let ops = if let Some(mut ori_outputs) = PendingUTXOs::get(&ctx.db.read()) { + ori_outputs.append(&mut outputs); + ori_outputs + } else { + outputs + }; PendingUTXOs::put(ctx.db.write().borrow_mut(), &ops) } diff --git a/src/components/contracts/primitives/core/src/module.rs b/src/components/contracts/primitives/core/src/module.rs index 16d1d5832..7a7e843a4 100644 --- a/src/components/contracts/primitives/core/src/module.rs +++ b/src/components/contracts/primitives/core/src/module.rs @@ -1,5 +1,6 @@ use crate::context::Context; use abci::*; +use primitive_types::U256; use ruc::Result; /// AppModuleBasic is the standard form for basic non-dependant elements of an application module. @@ -40,7 +41,8 @@ pub trait AppModule: AppModuleBasic { &mut self, _ctx: &mut Context, _req: &RequestEndBlock, - ) -> ResponseEndBlock { + _ff_addr_balance: u64, + ) -> (ResponseEndBlock, U256) { Default::default() } } diff --git a/src/components/contracts/primitives/traits/src/evm.rs b/src/components/contracts/primitives/traits/src/evm.rs index b2103fa97..656f253c3 100644 --- a/src/components/contracts/primitives/traits/src/evm.rs +++ b/src/components/contracts/primitives/traits/src/evm.rs @@ -51,6 +51,7 @@ impl DecimalsMapping for EthereumDecimalsMapping { pub trait FeeCalculator { /// Return the minimal required gas price. fn min_gas_price() -> U256; + fn max_gas_price() -> U256; } impl FeeCalculator for () { @@ -58,6 +59,9 @@ impl FeeCalculator for () { // 10 GWEI, min gas limit: 21000, min gas price must > 50_0000_0000 U256::from(100_0000_0000_u64) } + fn max_gas_price() -> U256 { + U256::from(5_000_000_000_000_u64) + } } /// Handle withdrawing, refunding and depositing of transaction fees. diff --git a/src/components/finutils/Cargo.toml b/src/components/finutils/Cargo.toml index 1c0f0c7a3..d76cef1cd 100644 --- a/src/components/finutils/Cargo.toml +++ b/src/components/finutils/Cargo.toml @@ -6,7 +6,7 @@ build = "build.rs" edition = "2021" [dependencies] -tokio = "1.10.1" + hex = "0.4.2" base64 = "0.12" clap = { version = "2.33.3", features = ["yaml"] } @@ -25,7 +25,6 @@ zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } ruc = "1.0" rucv4 = { package = "ruc", version = "4.0" } nix = "0.25" - ledger = { path = "../../ledger" } globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } @@ -42,6 +41,8 @@ tendermint-rpc = { git = "https://github.com/FindoraNetwork/tendermint-rs", feat [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # chaindev = { path = "../../../../chaindev" } chaindev = { git = "https://github.com/FindoraNetwork/chaindev", branch = "platform", default-features = false, features = ["tendermint_based", "vsdb_sled_engine"] } +web3 = "0.19.0" +tokio = "1.10.1" [dev-dependencies] diff --git a/src/components/finutils/src/bins/fn.rs b/src/components/finutils/src/bins/fn.rs index 155899ca5..40889dc9f 100644 --- a/src/components/finutils/src/bins/fn.rs +++ b/src/components/finutils/src/bins/fn.rs @@ -25,6 +25,10 @@ #![deny(warnings)] +use std::str::FromStr; + +use fp_types::H160; + use { clap::{crate_authors, load_yaml, App}, finutils::common::{self, evm::*}, @@ -280,7 +284,14 @@ fn run() -> Result<()> { } None => None, }; - common::claim(am, seckey.as_deref()).c(d!())?; + let td_addr = match m.value_of("validator-td-addr") { + Some(v) => v, + None => { + println!("{}", m.usage()); + return Ok(()); + } + }; + common::claim(td_addr, am, seckey.as_deref()).c(d!())?; } else if let Some(m) = matches.subcommand_matches("show") { let basic = m.is_present("basic"); common::show(basic).c(d!())?; @@ -409,30 +420,15 @@ fn run() -> Result<()> { let target = m .value_of("target") .c(d!()) - .and_then(wallet::public_key_from_base64)?; - // let new_td_addr_pk = if let Some(new_td_address_str) = m.value_of("td_address") { - // let new_td_address = hex::decode(new_td_address_str) - // .c(d!("`td_address` is invalid hex. "))?; - - // if new_td_address.len() != 20 { - // return Err(eg!("Invalid tendermint address.")); - // } - - // if let Some(new_td_pk) = m.value_of("td_pubkey") { - // let pk_bytes = - // base64::decode(new_td_pk).c(d!("`td_pubkey` is invalid base64."))?; - - // let _ = tendermint::PublicKey::from_raw_ed25519(&pk_bytes) - // .c(d!("Invalid tendermint public key."))?; - - // Some((new_td_address, pk_bytes)) - // } else { - // return Err(eg!("missing `td_pubkey`")); - // } - // } else { - // None - // }; - common::replace_staker(target, None)?; + .and_then(|val| H160::from_str(val).c(d!()))?; + let td_addr = match m.value_of("validator-td-addr") { + Some(v) => v, + None => { + println!("{}", m.usage()); + return Ok(()); + } + }; + common::replace_staker(target, td_addr)?; } else if let Some(m) = matches.subcommand_matches("dev") { #[cfg(not(target_arch = "wasm32"))] { diff --git a/src/components/finutils/src/bins/fn.yml b/src/components/finutils/src/bins/fn.yml index 3ce7a560b..98a3a95b8 100644 --- a/src/components/finutils/src/bins/fn.yml +++ b/src/components/finutils/src/bins/fn.yml @@ -143,6 +143,12 @@ subcommands: - claim: about: Claim accumulated FRA rewards args: + - validator-td-addr: + help: stake FRAs to a custom validator + short: A + long: validator-td-addr + takes_value: true + value_name: TendermintAddr - amount: help: how much `FRA unit`s to claim short: n @@ -482,6 +488,12 @@ subcommands: - replace_staker: about: Replace the staker of the validator with target address args: + - validator-td-addr: + help: stake FRAs to a custom validator + short: A + long: validator-td-addr + takes_value: true + value_name: TendermintAddr - target: help: the public key of new staker, you must be the staker of the validator, you could use `fn setup` to configure your secret key and public key. short: t @@ -489,18 +501,6 @@ subcommands: takes_value: true value_name: TARGET PUBLIC KEY required: true - - td_address: - help: the tendermint address that you may want to replace. - long: td_address - takes_value: true - value_name: TENDERMINT ADDRESS - required: false - - td_pubkey: - help: the tendermint public key that you may want to replace. - long: td_pubkey - takes_value: true - value_name: TENDERMINT PUBKEY - required: false - dev: about: Manage development clusters on your localhost args: diff --git a/src/components/finutils/src/bins/stt/stt.rs b/src/components/finutils/src/bins/stt/stt.rs index 267ec6f70..84eb29643 100644 --- a/src/components/finutils/src/bins/stt/stt.rs +++ b/src/components/finutils/src/bins/stt/stt.rs @@ -355,7 +355,7 @@ mod claim { common::utils::gen_fee_op(owner_kp).c(d!()).map(|op| { builder.add_operation(op); - builder.add_operation_claim(owner_kp, amount); + builder.add_operation_claim(None, owner_kp, amount); })?; Ok(builder.take_transaction()) diff --git a/src/components/finutils/src/common/mod.rs b/src/components/finutils/src/common/mod.rs index f80da89e2..534364f6c 100644 --- a/src/components/finutils/src/common/mod.rs +++ b/src/components/finutils/src/common/mod.rs @@ -16,6 +16,7 @@ pub mod evm; pub mod utils; use { + self::utils::{get_evm_staking_address, get_validator_memo_and_rate}, crate::api::DelegationInfo, globutils::wallet, lazy_static::lazy_static, @@ -33,10 +34,8 @@ use { ruc::*, std::{env, fs}, tendermint::PrivateKey, - utils::{ - get_block_height, get_local_block_height, get_validator_detail, - parse_td_validator_keys, - }, + utils::{get_block_height, get_local_block_height, parse_td_validator_keys}, + web3::types::H160, zei::{ setup::PublicParams, xfr::{ @@ -63,17 +62,24 @@ lazy_static! { /// Updating the information of a staker includes commission_rate and staker_memo pub fn staker_update(cr: Option<&str>, memo: Option) -> Result<()> { - let addr = get_td_pubkey().map(|i| td_pubkey_to_td_addr(&i)).c(d!())?; - let vd = get_validator_detail(&addr).c(d!())?; + let pub_key = get_td_pubkey() + .map(|i| td_pubkey_to_td_addr_bytes(&i)) + .c(d!())?; + let validator_address = H160::from_slice(&pub_key); + + let evm_staking_address = get_evm_staking_address()?; + let url = format!("{}:8545", get_serv_addr()?); + let (validator_memo, rate) = + get_validator_memo_and_rate(&url, evm_staking_address, validator_address)?; let cr = cr - .map_or(Ok(vd.commission_rate), |s| { + .map_or(Ok(rate), |s| { s.parse::() .c(d!("commission rate must be a float number")) .and_then(convert_commission_rate) }) .c(d!())?; - let memo = memo.unwrap_or(vd.memo); + let memo = memo.unwrap_or(validator_memo); let td_pubkey = get_td_pubkey().c(d!())?; @@ -249,7 +255,9 @@ pub fn unstake( } /// Claim rewards from findora network -pub fn claim(am: Option<&str>, sk_str: Option<&str>) -> Result<()> { +pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<()> { + let td_addr = hex::decode(td_addr).c(d!())?; + let am = if let Some(i) = am { Some(i.parse::().c(d!("'amount' must be an integer"))?) } else { @@ -262,7 +270,7 @@ pub fn claim(am: Option<&str>, sk_str: Option<&str>) -> Result<()> { utils::gen_fee_op(&kp).c(d!()).map(|op| { builder.add_operation(op); - builder.add_operation_claim(&kp, am); + builder.add_operation_claim(Some(td_addr), &kp, am); })?; let mut tx = builder.take_transaction(); @@ -829,10 +837,8 @@ pub fn version() -> &'static str { } ///operation to replace the staker. -pub fn replace_staker( - target_pubkey: XfrPublicKey, - new_td_addr_pk: Option<(Vec, Vec)>, -) -> Result<()> { +pub fn replace_staker(target_addr: fp_types::H160, td_addr: &str) -> Result<()> { + let td_addr = hex::decode(td_addr).c(d!())?; let keypair = get_keypair()?; let mut builder = utils::new_tx_builder().c(d!())?; @@ -841,7 +847,7 @@ pub fn replace_staker( builder.add_operation(op); })?; - builder.add_operation_replace_staker(&keypair, target_pubkey, new_td_addr_pk)?; + builder.add_operation_replace_staker(&keypair, target_addr, td_addr)?; let mut tx = builder.take_transaction(); tx.sign_to_map(&keypair); diff --git a/src/components/finutils/src/common/utils.rs b/src/components/finutils/src/common/utils.rs index 9e1085d50..9b0dfc99a 100644 --- a/src/components/finutils/src/common/utils.rs +++ b/src/components/finutils/src/common/utils.rs @@ -2,8 +2,6 @@ //! Some handful function and data structure for findora cli tools //! -use std::collections::BTreeMap; - use { crate::{ api::{DelegationInfo, ValidatorDetail}, @@ -17,13 +15,26 @@ use { Transaction, TransferType, TxoRef, TxoSID, Utxo, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, TX_FEE_MIN, }, - staking::{init::get_inital_validators, TendermintAddrRef, FRA_TOTAL_AMOUNT}, + staking::{ + init::get_inital_validators, StakerMemo, TendermintAddrRef, FRA_TOTAL_AMOUNT, + }, }, ruc::*, serde::{self, Deserialize, Serialize}, + serde_json::Value, sha2::{Digest, Sha256}, - std::collections::HashMap, + std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, + }, tendermint::{PrivateKey, PublicKey}, + tokio::runtime::Runtime, + web3::{ + ethabi::{Function, Param, ParamType, StateMutability, Token}, + transports::Http, + types::{BlockId, BlockNumber, Bytes, CallRequest, H160}, + Web3, + }, zei::xfr::{ asset_record::{open_blind_asset_record, AssetRecordType}, sig::{XfrKeyPair, XfrPublicKey}, @@ -616,3 +627,121 @@ pub struct ValidatorKey { pub fn parse_td_validator_keys(key_data: &str) -> Result { serde_json::from_str(key_data).c(d!()) } + +#[allow(missing_docs)] +pub fn get_evm_staking_address() -> Result { + let url = format!("{}:8668/display_checkpoint", get_serv_addr()?); + let val = attohttpc::get(url) + .send() + .c(d!())? + .error_for_status() + .c(d!())? + .json::() + .c(d!())?; + let address = match val["evm_staking_address"].as_str() { + Some(val) => val, + None => { + return Err(eg!("evm_staking_address json value not found")); + } + }; + H160::from_str(address).c(d!()) +} + +#[allow(missing_docs)] +pub fn get_validator_memo_and_rate( + url: &str, + staking_address: H160, + validator_address: H160, +) -> Result<(StakerMemo, [u64; 2])> { + let transport = Http::new(url).c(d!())?; + let web3 = Web3::new(transport); + + #[allow(deprecated)] + let function = Function { + name: "getValidator".to_owned(), + inputs: vec![Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }], + outputs: vec![ + Param { + name: String::new(), + kind: ParamType::Bytes, + internal_type: Some(String::from("bytes")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(8), + internal_type: Some(String::from("enum IBaseEnum.PublicKeyType")), + }, + Param { + name: String::new(), + kind: ParamType::String, + internal_type: Some(String::from("string")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + ], + constant: None, + state_mutability: StateMutability::View, + }; + let data = function + .encode_input(&[Token::Address(validator_address)]) + .map_err(|e| eg!("{:?}", e))?; + + let ret_data = Runtime::new() + .c(d!())? + .block_on(web3.eth().call( + CallRequest { + to: Some(staking_address), + data: Some(Bytes(data)), + ..Default::default() + }, + Some(BlockId::Number(BlockNumber::Latest)), + )) + .c(d!())?; + + let ret = function.decode_output(&ret_data.0).c(d!())?; + let memo = if let Some(Token::String(memo)) = ret.get(2) { + serde_json::from_str::(memo.as_str()).unwrap_or_default() + } else { + return Err(eg!("memo not found")); + }; + let rate = if let Some(Token::Uint(rate)) = ret.get(3) { + let deciamls = 1_000_000_u64; + let tmp = 10_000_u64; + [rate.as_u64() * tmp / deciamls, tmp] + } else { + return Err(eg!("rate not found")); + }; + Ok((memo, rate)) +} diff --git a/src/components/finutils/src/txn_builder/mod.rs b/src/components/finutils/src/txn_builder/mod.rs index 8a3168566..dc4e37c45 100644 --- a/src/components/finutils/src/txn_builder/mod.rs +++ b/src/components/finutils/src/txn_builder/mod.rs @@ -8,7 +8,7 @@ use { credentials::CredUserSecretKey, curve25519_dalek::scalar::Scalar, - fp_types::crypto::MultiSigner, + fp_types::{crypto::MultiSigner, H160}, globutils::SignatureOf, ledger::{ converter::ConvertAccount, @@ -569,10 +569,11 @@ impl TransactionBuilder { /// Add a operation to claim all the rewards pub fn add_operation_claim( &mut self, + td_addr: Option>, keypair: &XfrKeyPair, am: Option, ) -> &mut Self { - let op = ClaimOps::new(keypair, am, self.txn.body.no_replay_token); + let op = ClaimOps::new(td_addr, keypair, am, self.txn.body.no_replay_token); self.add_operation(Operation::Claim(op)) } @@ -622,13 +623,13 @@ impl TransactionBuilder { pub fn add_operation_replace_staker( &mut self, keypair: &XfrKeyPair, - new_public_key: XfrPublicKey, - new_td_addr: Option<(Vec, Vec)>, + new_staker_address: H160, + td_addr: Vec, ) -> Result<&mut Self> { let ops = ReplaceStakerOps::new( keypair, - new_public_key, - new_td_addr, + new_staker_address, + td_addr, self.txn.body.no_replay_token, ); self.add_operation(Operation::ReplaceStaker(ops)); diff --git a/src/components/wallet_mobile/src/rust/transaction.rs b/src/components/wallet_mobile/src/rust/transaction.rs index bc85c42aa..a7c22f5be 100644 --- a/src/components/wallet_mobile/src/rust/transaction.rs +++ b/src/components/wallet_mobile/src/rust/transaction.rs @@ -304,15 +304,18 @@ impl TransactionBuilder { #[allow(missing_docs)] pub fn add_operation_claim( mut self, + td_addr: Vec, keypair: &XfrKeyPair, ) -> RucResult { - self.get_builder_mut().add_operation_claim(keypair, None); + self.get_builder_mut() + .add_operation_claim(Some(td_addr), keypair, None); Ok(self) } #[allow(missing_docs)] pub fn add_operation_claim_custom( mut self, + td_addr: Vec, keypair: &XfrKeyPair, am: u64, ) -> RucResult { @@ -320,7 +323,7 @@ impl TransactionBuilder { return Err(eg!("Amount can not be zero")); } self.get_builder_mut() - .add_operation_claim(keypair, Some(am)); + .add_operation_claim(Some(td_addr), keypair, Some(am)); Ok(self) } diff --git a/src/components/wasm/src/wasm.rs b/src/components/wasm/src/wasm.rs index 8db856ccf..4bf909c71 100644 --- a/src/components/wasm/src/wasm.rs +++ b/src/components/wasm/src/wasm.rs @@ -485,8 +485,10 @@ impl TransactionBuilder { pub fn add_operation_claim( mut self, keypair: &XfrKeyPair, + td_addr: Vec, ) -> Result { - self.get_builder_mut().add_operation_claim(keypair, None); + self.get_builder_mut() + .add_operation_claim(Some(td_addr), keypair, None); Ok(self) } @@ -494,13 +496,14 @@ impl TransactionBuilder { pub fn add_operation_claim_custom( mut self, keypair: &XfrKeyPair, + td_addr: Vec, am: u64, ) -> Result { if 0 == am { return Err(error_to_jsvalue("Amount can not be zero")); } self.get_builder_mut() - .add_operation_claim(keypair, Some(am)); + .add_operation_claim(Some(td_addr), keypair, Some(am)); Ok(self) } diff --git a/src/ledger/Cargo.toml b/src/ledger/Cargo.toml index 40336b4e7..9ea5afd6c 100644 --- a/src/ledger/Cargo.toml +++ b/src/ledger/Cargo.toml @@ -36,7 +36,7 @@ bulletproofs = { package = "bulletproofs", git = "https://github.com/FindoraNetw noah-algebra = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } noah-crypto = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } fbnc = { version = "0.2.9", default-features = false} - +once_cell = "1" num-bigint = "0.4.3" globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } diff --git a/src/ledger/src/staking/evm.rs b/src/ledger/src/staking/evm.rs new file mode 100644 index 000000000..dd048d4fa --- /dev/null +++ b/src/ledger/src/staking/evm.rs @@ -0,0 +1,59 @@ +//! For interact with BaseApp (EVM) + +use super::{Delegation, Validator}; +use fp_types::H160; +use once_cell::sync::{Lazy, OnceCell}; +use parking_lot::{Mutex, RwLock}; +use ruc::Result; +use std::{collections::BTreeMap, sync::Arc}; +use zei::xfr::sig::XfrPublicKey; + +///EVM staking interface +pub static EVM_STAKING: OnceCell>> = OnceCell::new(); + +///Mints from EVM staking +pub static EVM_STAKING_MINTS: Lazy>> = + Lazy::new(|| Mutex::new(Vec::with_capacity(64))); + +/// For account base app +pub trait EVMStaking: Sync + Send + 'static { + /// import_validators call + fn import_validators( + &self, + validators: &[Validator], + delegations: &BTreeMap, + coinbase_balance: u64, + ) -> Result<()>; + /// stake call + fn stake( + &self, + from: &XfrPublicKey, + value: u64, + td_addr: &[u8], + td_pubkey: Vec, + memo: String, + rate: [u64; 2], + ) -> Result<()>; + /// delegate call + fn delegate(&self, from: &XfrPublicKey, value: u64, td_addr: &[u8]) -> Result<()>; + /// undelegate call + fn undelegate(&self, from: &XfrPublicKey, td_addr: &[u8], amount: u64) + -> Result<()>; + ///update the memo and rate of the validator + fn update_validator( + &self, + staker: &XfrPublicKey, + validator: &[u8], + memo: String, + rate: [u64; 2], + ) -> Result<()>; + /// + fn replace_delegator( + &self, + validator: &[u8], + staker: &XfrPublicKey, + new_staker_address: H160, + ) -> Result<()>; + /// claim call + fn claim(&self, td_addr: &[u8], delegator_pk: &XfrPublicKey) -> Result<()>; +} diff --git a/src/ledger/src/staking/mod.rs b/src/ledger/src/staking/mod.rs index 91b51d827..3ec4585d3 100644 --- a/src/ledger/src/staking/mod.rs +++ b/src/ledger/src/staking/mod.rs @@ -16,6 +16,7 @@ use {num_bigint::BigUint, std::convert::TryFrom}; pub mod cosig; +pub mod evm; pub mod init; pub mod ops; @@ -98,8 +99,8 @@ lazy_static! { pub static ref CHAN_D_RWD_HIST: DRHCP = chan!(); } -// Reserved accounts of Findora Foundation. -const FF_ADDR_LIST: [&str; 8] = [ +/// Reserved accounts of Findora Foundation. +pub const FF_ADDR_LIST: [&str; 8] = [ "fra1s9c6p0656as48w8su2gxntc3zfuud7m66847j6yh7n8wezazws3s68p0m9", "fra1zjfttcnvyv9ypy2d4rcg7t4tw8n88fsdzpggr0y2h827kx5qxmjshwrlx7", "fra18rfyc9vfyacssmr5x7ku7udyd5j5vmfkfejkycr06e4as8x7n3dqwlrjrc", @@ -109,8 +110,8 @@ const FF_ADDR_LIST: [&str; 8] = [ "fra1mjdr0mgn2e0670hxptpzu9tmf0ary8yj8nv90znjspwdupv9aacqwrg3dx", "fra1whn756rtqt3gpsmdlw6pvns75xdh3ttqslvxaf7eefwa83pcnlhsree9gv", ]; - -const FF_ADDR_EXTRA_120_0000: &str = +/// +pub const FF_ADDR_EXTRA_120_0000: &str = "fra1dkn9w5c674grdl6gmvj0s8zs0z2nf39zrmp3dpq5rqnnf9axwjrqexqnd6"; /// SEE: @@ -363,11 +364,7 @@ impl Staking { &self, h: BlockHeight, ) -> Option<&ValidatorData> { - self.validator_info - .range(0..=h) - .rev() - .next() - .map(|(_, v)| v) + self.validator_info.range(0..=h).next_back().map(|(_, v)| v) } /// Remove the validators that will be used for the specified height. @@ -390,8 +387,7 @@ impl Staking { ) -> Option<&mut ValidatorData> { self.validator_info .range_mut(0..=h) - .rev() - .next() + .next_back() .map(|(_, v)| v) } diff --git a/src/ledger/src/staking/ops/claim.rs b/src/ledger/src/staking/ops/claim.rs index 8e038d68f..bbdd9ce71 100644 --- a/src/ledger/src/staking/ops/claim.rs +++ b/src/ledger/src/staking/ops/claim.rs @@ -5,7 +5,11 @@ //! use { - crate::{data_model::NoReplayToken, staking::Staking}, + crate::{ + data_model::NoReplayToken, + staking::{evm::EVM_STAKING, Staking}, + }, + config::abci::global_cfg::CFG, ruc::*, serde::{Deserialize, Serialize}, zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, @@ -17,6 +21,9 @@ pub struct ClaimOps { pub(crate) body: Data, pub(crate) pubkey: XfrPublicKey, signature: XfrSignature, + /// + #[serde(skip_serializing_if = "Option::is_none")] + pub td_addr: Option>, } impl ClaimOps { @@ -28,9 +35,21 @@ impl ClaimOps { /// Apply new claim to the target `Staking` instance. pub fn apply(&self, staking: &mut Staking) -> Result<()> { - self.verify() - .c(d!()) - .and_then(|_| staking.claim(self.pubkey, self.body.amount).c(d!())) + let cur_height = staking.cur_height() as i64; + if cur_height > CFG.checkpoint.evm_staking_inital_height { + self.verify()?; + let td_addr = self.td_addr.clone().c(d!(eg!("Missing validator addr.")))?; + EVM_STAKING + .get() + .c(d!())? + .write() + .claim(&td_addr, &self.pubkey)?; + Ok(()) + } else { + self.verify() + .c(d!()) + .and_then(|_| staking.claim(self.pubkey, self.body.amount).c(d!())) + } } /// Verify signature. @@ -55,13 +74,19 @@ impl ClaimOps { #[inline(always)] #[allow(missing_docs)] - pub fn new(keypair: &XfrKeyPair, amount: Option, nonce: NoReplayToken) -> Self { + pub fn new( + td_addr: Option>, + keypair: &XfrKeyPair, + amount: Option, + nonce: NoReplayToken, + ) -> Self { let body = Data::new(amount, nonce); let signature = keypair.sign(&body.to_bytes()); ClaimOps { body, pubkey: keypair.get_pk(), signature, + td_addr, } } diff --git a/src/ledger/src/staking/ops/delegation.rs b/src/ledger/src/staking/ops/delegation.rs index d0eded8bd..528a5eaaf 100644 --- a/src/ledger/src/staking/ops/delegation.rs +++ b/src/ledger/src/staking/ops/delegation.rs @@ -11,10 +11,11 @@ use { BLACK_HOLE_PUBKEY_STAKING, }, staking::{ - deny_relative_inputs, td_addr_to_string, Amount, Staking, TendermintAddr, - Validator, STAKING_VALIDATOR_MIN_POWER, + deny_relative_inputs, evm::EVM_STAKING, td_addr_to_bytes, td_addr_to_string, + Amount, Staking, TendermintAddr, Validator, STAKING_VALIDATOR_MIN_POWER, }, }, + config::abci::global_cfg::CFG, ed25519_dalek::Signer, ruc::*, serde::{Deserialize, Serialize}, @@ -48,14 +49,36 @@ impl DelegationOps { /// Apply new delegation to the target `Staking` instance. pub fn apply(&self, staking: &mut Staking, tx: &Transaction) -> Result<()> { - self.verify() - .c(d!()) - .and_then(|_| self.check_set_context(staking, tx).c(d!())) - .and_then(|am| { + let cur_height = staking.cur_height() as i64; + self.verify()?; + if cur_height > CFG.checkpoint.evm_staking_inital_height { + let am = check_delegation_context(tx).c(d!())?; + if let Some(new_validator) = self.body.new_validator.as_ref() { + let memo = serde_json::to_string(&new_validator.memo).c(d!())?; + EVM_STAKING.get().c(d!())?.write().stake( + &self.pubkey, + am, + &new_validator.td_addr, + new_validator.td_pubkey.to_owned(), + memo, + new_validator.commission_rate, + )?; + } else { + EVM_STAKING.get().c(d!())?.write().delegate( + &self.pubkey, + am, + &td_addr_to_bytes(&self.body.validator)?, + )?; + } + + Ok(()) + } else { + self.check_set_context(staking, tx).c(d!()).and_then(|am| { staking .delegate(self.pubkey, &self.body.validator, am) .c(d!()) }) + } } /// Verify signature. diff --git a/src/ledger/src/staking/ops/replace_staker.rs b/src/ledger/src/staking/ops/replace_staker.rs index e4752fbe5..b88b3b44b 100644 --- a/src/ledger/src/staking/ops/replace_staker.rs +++ b/src/ledger/src/staking/ops/replace_staker.rs @@ -4,8 +4,12 @@ //! use { - crate::data_model::{NoReplayToken, Transaction}, - crate::staking::Staking, + crate::{ + data_model::{NoReplayToken, Transaction}, + staking::{evm::EVM_STAKING, Staking}, + }, + config::abci::global_cfg::CFG, + fp_types::H160, ruc::*, serde::{Deserialize, Serialize}, zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, @@ -23,18 +27,15 @@ impl ReplaceStakerOps { ///create a new replace operation. pub fn new( keypair: &XfrKeyPair, - new_public_key: XfrPublicKey, - new_tendermint_params: Option<(Vec, Vec)>, + new_staker_address: H160, + td_addr: Vec, nonce: NoReplayToken, ) -> Self { - let new_tendermint_params = new_tendermint_params.map(|p| TendermintParams { - address: p.0, - pubkey: p.1, - }); - let body = Data { - new_public_key, - new_tendermint_params, + new_public_key: XfrPublicKey::default(), + new_tendermint_params: None, + new_staker_address: Some(new_staker_address), + td_addr: Some(td_addr), nonce, }; @@ -70,14 +71,34 @@ impl ReplaceStakerOps { _tx: &Transaction, ) -> Result<()> { self.verify()?; - dbg!(staking_simulator.check_and_replace_staker( - &self.pubkey, - self.body.new_public_key, - self.body - .new_tendermint_params + let cur_height = staking_simulator.cur_height() as i64; + if cur_height > CFG.checkpoint.evm_staking_inital_height { + let validator = self + .body + .td_addr .clone() - .map(|p| (p.address, p.pubkey)), - )) + .ok_or(eg!("replace staker validator not found"))?; + + let new_staker_address = self + .body + .new_staker_address + .ok_or(eg!("replace staker new_staker_address not found"))?; + + EVM_STAKING.get().c(d!())?.write().replace_delegator( + &validator, + &self.pubkey, + new_staker_address, + ) + } else { + dbg!(staking_simulator.check_and_replace_staker( + &self.pubkey, + self.body.new_public_key, + self.body + .new_tendermint_params + .clone() + .map(|p| (p.address, p.pubkey)), + )) + } } #[inline(always)] @@ -104,6 +125,10 @@ impl ReplaceStakerOps { pub struct Data { pub new_public_key: XfrPublicKey, pub new_tendermint_params: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub new_staker_address: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub td_addr: Option>, nonce: NoReplayToken, } diff --git a/src/ledger/src/staking/ops/undelegation.rs b/src/ledger/src/staking/ops/undelegation.rs index 89d88b0d3..9ccd6c1ea 100644 --- a/src/ledger/src/staking/ops/undelegation.rs +++ b/src/ledger/src/staking/ops/undelegation.rs @@ -7,8 +7,9 @@ use { crate::{ data_model::{NoReplayToken, Operation, Transaction}, - staking::{PartialUnDelegation, Staking}, + staking::{evm::EVM_STAKING, PartialUnDelegation, Staking}, }, + config::abci::global_cfg::CFG, ruc::*, serde::{Deserialize, Serialize}, zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, @@ -35,10 +36,23 @@ impl UnDelegationOps { /// Apply new delegation to the target `Staking` instance. pub fn apply(&self, staking: &mut Staking, tx: &Transaction) -> Result<()> { - self.verify() - .c(d!()) - .and_then(|_| Self::check_context(tx).c(d!())) - .and_then(|pu| staking.undelegate(&self.pubkey, pu).c(d!())) + let cur_height = staking.cur_height() as i64; + if cur_height > CFG.checkpoint.evm_staking_inital_height { + self.verify()?; + let pu = + Self::check_context(tx)?.c(d!(eg!("Missing partial undelegation.")))?; + EVM_STAKING.get().c(d!())?.write().undelegate( + &self.pubkey, + &pu.target_validator, + pu.am, + )?; + Ok(()) + } else { + self.verify() + .c(d!()) + .and_then(|_| Self::check_context(tx).c(d!())) + .and_then(|pu| staking.undelegate(&self.pubkey, pu).c(d!())) + } } /// Verify signature. diff --git a/src/ledger/src/staking/ops/update_staker.rs b/src/ledger/src/staking/ops/update_staker.rs index c5ca3d049..b64d7a1d5 100644 --- a/src/ledger/src/staking/ops/update_staker.rs +++ b/src/ledger/src/staking/ops/update_staker.rs @@ -7,8 +7,11 @@ use { crate::{ data_model::{NoReplayToken, Transaction}, - staking::{td_addr_to_string, Staking, TendermintAddr, Validator}, + staking::{ + evm::EVM_STAKING, td_addr_to_string, Staking, TendermintAddr, Validator, + }, }, + config::abci::global_cfg::CFG, ed25519_dalek::Signer, ruc::*, serde::{Deserialize, Serialize}, @@ -37,10 +40,23 @@ impl UpdateStakerOps { } fn apply(&self, staking: &mut Staking, _tx: &Transaction) -> Result<()> { - self.verify() - .c(d!()) - .and_then(|_| self.check_update_context(staking).c(d!())) - .and_then(|_| staking.update_staker(&self.body.new_validator).c(d!())) + let cur_height = staking.cur_height() as i64; + if cur_height > CFG.checkpoint.evm_staking_inital_height { + self.pubkey + .verify(&self.body.to_bytes(), &self.signature) + .c(d!())?; + EVM_STAKING.get().c(d!())?.write().update_validator( + &self.pubkey, + &self.body.new_validator.td_addr, + serde_json::to_string(&self.body.new_validator.memo).c(d!())?, + self.body.new_validator.commission_rate, + ) + } else { + self.verify() + .c(d!()) + .and_then(|_| self.check_update_context(staking).c(d!())) + .and_then(|_| staking.update_staker(&self.body.new_validator).c(d!())) + } } /// verify signature