diff --git a/Cargo.toml b/Cargo.toml index 6f2aa4fc0..4f2eee1f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ members = [ "src/components/contracts/primitives/rpc-server", "src/components/contracts/primitives/utils", "src/components/contracts/primitives/wasm", + "src/components/contracts/primitives/enterprise-web3", "src/components/contracts/rpc", ] diff --git a/Makefile b/Makefile index fd7319541..8e707bdf2 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,14 @@ build_release_debug: tendermint_goleveldb cargo build --features debug_env --release --bins -p abciapp -p finutils $(call pack,release) +build_release_web3_goleveldb: tendermint_goleveldb + cargo build --features="web3_service debug_env" --release --bins -p abciapp -p finutils + $(call pack,release) + +build_release_web3: tendermint_cleveldb + cargo build --features="web3_service debug_env" --release --bins -p abciapp -p finutils + $(call pack,release) + tendermint_cleveldb: bash tools/download_tendermint.sh 'tools/tendermint' mkdir -p $(shell go env GOPATH)/bin diff --git a/container/Dockerfile-enterprise-web3 b/container/Dockerfile-enterprise-web3 new file mode 100644 index 000000000..665d43599 --- /dev/null +++ b/container/Dockerfile-enterprise-web3 @@ -0,0 +1,30 @@ +FROM binary-rust-base +ENV WORK_DIR /platform +ENV WASM_DIR /tmp/wasm-js-bindings +ENV VERGEN_SHA_EXTERN web3_service_build +ENV PATH=$PATH:/root/.cargo/bin/ +ENV REDIS_ADDR=redis://127.0.0.1 + +COPY . $WORK_DIR +WORKDIR $WORK_DIR + +RUN rustup toolchain install stable && \ + rustup component add clippy --toolchain stable && \ + rustup component add rustfmt + +RUN mkdir /binary +RUN mkdir -p /binary/cleveldb && mkdir -p /binary/goleveldb + +RUN make fmt +RUN make lint +RUN make test + +RUN mkdir -p /root/.cargo/bin/ && \ + make build_release_web3 && \ + if [ -d /platform/release/bin ] ; then mv /platform/release/bin/* /binary/cleveldb ; rm -rf /platform/release/; else mv /platform/debug/bin/* /binary/cleveldb ; rm -rf /platform/debug/ ;fi + +RUN mkdir -p /root/.cargo/bin/ && \ + make build_release_web3_goleveldb && \ + if [ -d /platform/release/bin ] ; then mv /platform/release/bin/* /binary/goleveldb ; rm -rf /platform/release/; else mv /platform/debug/bin/* /binary/goleveldb ; rm -rf /platform/debug/ ;fi + +CMD ["sleep", "999999"] \ No newline at end of file diff --git a/src/components/abciapp/Cargo.toml b/src/components/abciapp/Cargo.toml index 5594a4428..aa1499ebe 100644 --- a/src/components/abciapp/Cargo.toml +++ b/src/components/abciapp/Cargo.toml @@ -60,6 +60,10 @@ fc-rpc = { path = "../contracts/rpc" } fp-storage = { path = "../contracts/primitives/storage" } fp-utils = { path = "../contracts/primitives/utils" } + +enterprise-web3 = { path = "../contracts/primitives/enterprise-web3", optional = true } + + [target.'cfg(target_os= "linux")'.dependencies] btm = "0.1.6" @@ -72,4 +76,4 @@ vergen = "=3.1.0" default = ["diskcache"] diskcache = ["ledger/diskcache"] debug_env = ["ledger/debug_env", "config/debug_env"] - +web3_service = ["enterprise-web3", "baseapp/web3_service"] \ No newline at end of file diff --git a/src/components/abciapp/src/abci/server/callback/mod.rs b/src/components/abciapp/src/abci/server/callback/mod.rs index 4af08a588..c5bb47911 100644 --- a/src/components/abciapp/src/abci/server/callback/mod.rs +++ b/src/components/abciapp/src/abci/server/callback/mod.rs @@ -499,6 +499,124 @@ pub fn commit(s: &mut ABCISubmissionServer, req: &RequestCommit) -> ResponseComm } IN_SAFE_ITV.store(false, Ordering::Release); + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{ + BALANCE_MAP, BLOCK, CODE_MAP, NONCE_MAP, RECEIPTS, STATE_UPDATE_LIST, TXS, + WEB3_SERVICE_START_HEIGHT, + }; + use std::collections::HashMap; + use std::mem::replace; + + let height = state.get_tendermint_height() as u32; + if height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut setter = enterprise_web3::setter().expect("connection redis failed"); + + let nonce_map = if let Ok(mut nonce_map) = NONCE_MAP.lock() { + replace(&mut *nonce_map, HashMap::new()) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + let code_map = if let Ok(mut code_map) = CODE_MAP.lock() { + replace(&mut *code_map, HashMap::new()) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + let balance_map = if let Ok(mut balance_map) = BALANCE_MAP.lock() { + replace(&mut *balance_map, HashMap::new()) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + let state_list = if let Ok(mut state_list) = STATE_UPDATE_LIST.lock() { + replace(&mut *state_list, vec![]) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + let block = if let Ok(mut block) = BLOCK.lock() { + block.take() + } else { + None + }; + + let txs = if let Ok(mut txs) = TXS.lock() { + replace(&mut *txs, vec![]) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + let receipts = if let Ok(mut receipts) = RECEIPTS.lock() { + replace(&mut *receipts, vec![]) + } else { + tracing::error!("{}", ""); + Default::default() + }; + + if !code_map.is_empty() + || !nonce_map.is_empty() + || !balance_map.is_empty() + || !state_list.is_empty() + || !txs.is_empty() + || !receipts.is_empty() + || block.is_some() + { + setter + .set_height(height) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + + for (addr, code) in code_map.iter() { + setter + .set_byte_code(height, *addr, code.clone()) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + + for (addr, nonce) in nonce_map.iter() { + setter + .set_nonce(height, *addr, *nonce) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + + for (addr, balance) in balance_map.iter() { + setter + .set_balance(height, *addr, *balance) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + + for state in state_list.iter() { + setter + .set_state( + height, + state.address.clone(), + state.index.clone(), + state.value.clone(), + ) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + + if let Some(block) = block { + setter + .set_block_info(block, receipts, txs) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + } + } + } + r } diff --git a/src/components/contracts/baseapp/Cargo.toml b/src/components/contracts/baseapp/Cargo.toml index 74ae4e87c..cbc641ceb 100644 --- a/src/components/contracts/baseapp/Cargo.toml +++ b/src/components/contracts/baseapp/Cargo.toml @@ -27,6 +27,7 @@ base64 = "0.13" once_cell = "1.10.0" storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } +sha3 = "0.8" config = { path = "../../config"} @@ -36,6 +37,7 @@ fp-evm = {path = "../primitives/evm"} fp-traits = {path = "../primitives/traits"} fp-types = {path = "../primitives/types"} fp-utils = {path = "../primitives/utils"} +enterprise-web3 = { path = "../primitives/enterprise-web3", optional = true } # modules module-account = {path = "../modules/account"} @@ -54,3 +56,4 @@ evm-precompile = {path = "../modules/evm/precompile"} [features] abci_mock = [] +web3_service = ["enterprise-web3", "module-account/web3_service", "module-ethereum/web3_service", "module-evm/web3_service"] \ No newline at end of file diff --git a/src/components/contracts/baseapp/src/app.rs b/src/components/contracts/baseapp/src/app.rs index bf43fcc88..685bbb00d 100644 --- a/src/components/contracts/baseapp/src/app.rs +++ b/src/components/contracts/baseapp/src/app.rs @@ -74,6 +74,8 @@ impl crate::BaseApp { }; if let Ok(tx) = convert_unchecked_transaction::(raw_tx) { + #[cfg(feature = "enterprise-web3")] + let tmp_tx = tx.clone(); let check_fn = |mode: RunTxMode| { let ctx = { let mut ctx = self.check_state.clone(); @@ -83,6 +85,57 @@ impl crate::BaseApp { let result = self.modules.process_tx::(ctx, tx); match result { Ok(ar) => { + #[cfg(feature = "enterprise-web3")] + { + use enterprise_web3::{ + PENDING_CODE_MAP, PENDING_STATE_UPDATE_LIST, + }; + use std::{collections::HashMap, mem::replace}; + let code_map = + if let Ok(mut code_map) = PENDING_CODE_MAP.lock() { + replace(&mut *code_map, HashMap::new()) + } else { + tracing::error!("{}", ""); + Default::default() + }; + let state_list = if let Ok(mut state_list) = + PENDING_STATE_UPDATE_LIST.lock() + { + replace(&mut *state_list, vec![]) + } else { + tracing::error!("{}", ""); + Default::default() + }; + if 0 == ar.code { + if let fp_types::actions::Action::Ethereum( + fp_types::actions::ethereum::Action::Transact(tx), + ) = tmp_tx.function + { + let mut setter = enterprise_web3::setter() + .expect("connection redis failed"); + setter + .set_pending_tx(tx) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + for (addr, code) in code_map.iter() { + setter + .set_pending_code(*addr, code.clone()) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + for state in state_list.iter() { + setter + .set_pending_state( + state.address.clone(), + state.index.clone(), + state.value.clone(), + ) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + } + } + } resp.code = ar.code; if ar.code != 0 { info!(target: "baseapp", "Transaction check error, action result {:?}", ar); @@ -153,10 +206,68 @@ impl crate::BaseApp { if let Ok(tx) = convert_unchecked_transaction::(raw_tx) { let ctx = self.retrieve_context(RunTxMode::Deliver).clone(); - + #[cfg(feature = "enterprise-web3")] + let tmp_tx = tx.clone(); let ret = self.modules.process_tx::(ctx, tx); match ret { Ok(ar) => { + #[cfg(feature = "enterprise-web3")] + { + use enterprise_web3::{ + REMOVE_PENDING_CODE_MAP, REMOVE_PENDING_STATE_UPDATE_LIST, + }; + use std::{mem::replace, ops::DerefMut}; + let code_map = + if let Ok(mut code_map) = REMOVE_PENDING_CODE_MAP.lock() { + let m = code_map.deref_mut(); + let map = replace(m, vec![]); + map.clone() + } else { + tracing::error!("{}", ""); + Default::default() + }; + let state_list = if let Ok(mut state_list) = + REMOVE_PENDING_STATE_UPDATE_LIST.lock() + { + let v = state_list.deref_mut(); + let v2 = replace(v, vec![]); + v2.clone() + } else { + tracing::error!("{}", ""); + Default::default() + }; + if 0 == ar.code { + if let fp_types::actions::Action::Ethereum( + fp_types::actions::ethereum::Action::Transact(tx), + ) = tmp_tx.function + { + let mut setter = enterprise_web3::setter() + .expect("connection redis failed"); + setter + .remove_pending_tx(tx) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + + for addr in code_map.iter() { + setter + .remove_pending_code(*addr) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + + for (address, index) in state_list.iter() { + setter + .remove_pending_state( + address.clone(), + index.clone(), + ) + .map_err(|e| tracing::error!("{:?}", e)) + .unwrap_or(()); + } + } + } + } + if ar.code != 0 { info!(target: "baseapp", "deliver tx with result: {:?}", ar); } else { diff --git a/src/components/contracts/baseapp/src/lib.rs b/src/components/contracts/baseapp/src/lib.rs index 02de696a1..9788ba24c 100644 --- a/src/components/contracts/baseapp/src/lib.rs +++ b/src/components/contracts/baseapp/src/lib.rs @@ -23,19 +23,23 @@ use fp_core::{ ensure, parameter_types, transaction::{ActionResult, Executable, ValidateUnsigned}, }; + use fp_evm::BlockId; use fp_traits::{ account::{AccountAsset, FeeCalculator}, base::BaseProvider, evm::{DecimalsMapping, EthereumAddressMapping, EthereumDecimalsMapping}, }; + use fp_types::{actions::xhub::NonConfidentialOutput, actions::Action, crypto::Address}; use lazy_static::lazy_static; use ledger::data_model::{Transaction as FindoraTransaction, ASSET_TYPE_FRA}; +use ledger::store::LedgerState; +use module_evm::utils::address_mapping; use notify::*; use parking_lot::RwLock; use primitive_types::{H160, H256, U256}; -use ruc::{eg, Result}; +use ruc::{d, eg, Result, RucResult}; use std::{borrow::BorrowMut, path::Path, sync::Arc}; use storage::state::ChainState; @@ -351,6 +355,57 @@ impl BaseApp { .process_findora_tx(&self.deliver_state, tx, H256::from_slice(hash)) } + pub fn init_staking_from_ledger( + &self, + ledger: Arc>, + ) -> Result<()> { + let (staking, delegation_bonds, delegation_unbond) = ledger + .write() + .get_staking_mut() + .make_transfer_data() + .c(d!())?; + + for (td_addr, td_public_key, staker, _power, memo, rate) in staking.into_iter() { + self.modules + .evm_module + .admin_stake( + &self.deliver_state, + H160::from_slice(&td_addr[..20]), + td_public_key, + address_mapping(&staker), + serde_json::to_string(&memo).unwrap(), + U256::from(rate), + ) + .c(d!())?; + } + for (delegator, validator, amount) in delegation_bonds { + self.modules + .evm_module + .system_set_delegation( + &self.deliver_state, + address_mapping(&validator), + address_mapping(&delegator), + U256::from(amount), + ) + .c(d!())?; + } + + for (delegator, validator, amount, target_height) in delegation_unbond { + self.modules + .evm_module + .system_set_delegation_unbound( + &self.deliver_state, + address_mapping(&validator), + address_mapping(&delegator), + U256::from(amount), + U256::from(target_height), + ) + .c(d!())?; + } + + Ok(()) + } + pub fn consume_mint(&self) -> Option> { let mut outputs = self.modules.evm_module.consume_mint(&self.deliver_state); diff --git a/src/components/contracts/modules/account/Cargo.toml b/src/components/contracts/modules/account/Cargo.toml index 2f55cb5ab..73fe420c1 100644 --- a/src/components/contracts/modules/account/Cargo.toml +++ b/src/components/contracts/modules/account/Cargo.toml @@ -22,7 +22,7 @@ fp-core = { path = "../../primitives/core" } fp-storage = { path = "../../primitives/storage" } fp-traits = { path = "../../primitives/traits" } fp-types = { path = "../../primitives/types" } - +enterprise-web3 = { path = "../../primitives/enterprise-web3", optional = true } config = {path = "../../../config"} [dev-dependencies] @@ -31,3 +31,6 @@ rand_chacha = "0.3" storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } noah = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.3.0" } + +[features] +web3_service = ["enterprise-web3"] \ No newline at end of file diff --git a/src/components/contracts/modules/account/src/impls.rs b/src/components/contracts/modules/account/src/impls.rs index 20166a718..c0ff983cf 100644 --- a/src/components/contracts/modules/account/src/impls.rs +++ b/src/components/contracts/modules/account/src/impls.rs @@ -74,7 +74,27 @@ impl AccountAsset
for App { .checked_add(balance) .c(d!("balance overflow"))?; AccountStore::insert(ctx.state.write().borrow_mut(), sender, &from_account)?; - AccountStore::insert(ctx.state.write().borrow_mut(), dest, &to_account) + AccountStore::insert(ctx.state.write().borrow_mut(), dest, &to_account)?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; + use primitive_types::H160; + + if ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut balance_map = BALANCE_MAP.lock().c(d!())?; + let sender_slice: &[u8] = sender.as_ref(); + let sender_h160 = H160::from_slice(&sender_slice[4..24]); + + let to_slice: &[u8] = dest.as_ref(); + let to_h160 = H160::from_slice(&to_slice[4..24]); + + balance_map.insert(sender_h160, from_account.balance); + balance_map.insert(to_h160, to_account.balance); + } + } + + Ok(()) } fn mint(ctx: &Context, target: &Address, balance: U256) -> Result<()> { @@ -93,7 +113,23 @@ impl AccountAsset
for App { let issuance = Self::total_issuance(ctx) .checked_add(balance) .c(d!("issuance overflow"))?; - TotalIssuance::put(ctx.state.write().borrow_mut(), &issuance) + TotalIssuance::put(ctx.state.write().borrow_mut(), &issuance)?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; + use primitive_types::H160; + + if ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut balance_map = BALANCE_MAP.lock().c(d!())?; + let target_slice: &[u8] = target.as_ref(); + let target_h160 = H160::from_slice(&target_slice[4..24]); + + balance_map.insert(target_h160, target_account.balance); + } + } + + Ok(()) } fn burn(ctx: &Context, target: &Address, balance: U256) -> Result<()> { @@ -113,7 +149,22 @@ impl AccountAsset
for App { let issuance = Self::total_issuance(ctx) .checked_sub(balance) .c(d!("insufficient issuance"))?; - TotalIssuance::put(ctx.state.write().borrow_mut(), &issuance) + TotalIssuance::put(ctx.state.write().borrow_mut(), &issuance)?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; + use primitive_types::H160; + if ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut balance_map = BALANCE_MAP.lock().c(d!())?; + let target_slice: &[u8] = target.as_ref(); + let target_h160 = H160::from_slice(&target_slice[4..24]); + + balance_map.insert(target_h160, target_account.balance); + } + } + + Ok(()) } fn withdraw(ctx: &Context, who: &Address, value: U256) -> Result<()> { @@ -131,7 +182,23 @@ impl AccountAsset
for App { .checked_add(value) .c(d!("reserved balance overflow"))?; - AccountStore::insert(ctx.state.write().borrow_mut(), who, &sa) + AccountStore::insert(ctx.state.write().borrow_mut(), who, &sa)?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; + use primitive_types::H160; + + if ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut balance_map = BALANCE_MAP.lock().c(d!())?; + let target_slice: &[u8] = who.as_ref(); + let target_h160 = H160::from_slice(&target_slice[4..24]); + + balance_map.insert(target_h160, sa.balance); + } + } + + Ok(()) } fn refund(ctx: &Context, who: &Address, value: U256) -> Result<()> { @@ -145,7 +212,22 @@ impl AccountAsset
for App { .checked_sub(value) .c(d!("insufficient reserved balance"))?; sa.balance = sa.balance.checked_add(value).c(d!("balance overflow"))?; - AccountStore::insert(ctx.state.write().borrow_mut(), who, &sa) + AccountStore::insert(ctx.state.write().borrow_mut(), who, &sa)?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{BALANCE_MAP, WEB3_SERVICE_START_HEIGHT}; + use primitive_types::H160; + if ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut balance_map = BALANCE_MAP.lock().c(d!())?; + let target_slice: &[u8] = who.as_ref(); + let target_h160 = H160::from_slice(&target_slice[4..24]); + + balance_map.insert(target_h160, sa.balance); + } + } + + Ok(()) } fn allowance(ctx: &Context, owner: &Address, spender: &Address) -> U256 { diff --git a/src/components/contracts/modules/account/src/lib.rs b/src/components/contracts/modules/account/src/lib.rs index 8d55de8e8..7429c47dc 100644 --- a/src/components/contracts/modules/account/src/lib.rs +++ b/src/components/contracts/modules/account/src/lib.rs @@ -1,6 +1,8 @@ #![deny(warnings)] #![allow(missing_docs)] +extern crate core; + mod basic; mod impls; diff --git a/src/components/contracts/modules/ethereum/Cargo.toml b/src/components/contracts/modules/ethereum/Cargo.toml index 5bd019011..8f6ccaf0c 100644 --- a/src/components/contracts/modules/ethereum/Cargo.toml +++ b/src/components/contracts/modules/ethereum/Cargo.toml @@ -31,6 +31,7 @@ fp-traits = { path = "../../primitives/traits" } fp-types = { path = "../../primitives/types" } fp-utils = { path = "../../primitives/utils" } config = { path = "../../../config"} +enterprise-web3 = { path = "../../primitives/enterprise-web3", optional = true } [dev-dependencies] baseapp = { path = "../../baseapp" } @@ -42,3 +43,4 @@ fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" [features] default = [] debug_env = [] +web3_service = ["enterprise-web3"] diff --git a/src/components/contracts/modules/ethereum/src/impls.rs b/src/components/contracts/modules/ethereum/src/impls.rs index e5f48a48a..61654416b 100644 --- a/src/components/contracts/modules/ethereum/src/impls.rs +++ b/src/components/contracts/modules/ethereum/src/impls.rs @@ -162,6 +162,54 @@ impl App { &block_hash, &statuses, )?; + + #[cfg(feature = "web3_service")] + { + use enterprise_web3::{ + TxState, BLOCK, RECEIPTS, TXS, WEB3_SERVICE_START_HEIGHT, + }; + + use ethereum::{BlockAny, FrontierReceiptData, ReceiptAny}; + + if block_number.as_u64() > *WEB3_SERVICE_START_HEIGHT { + if let Ok(mut b) = BLOCK.lock() { + if b.is_none() { + let block = BlockAny::from(block); + b.replace(block); + } else { + tracing::error!("the block is not none"); + } + } + if let Ok(mut txs) = TXS.lock() { + for status in statuses.iter() { + let tx_status = TxState { + transaction_hash: status.transaction_hash, + transaction_index: status.transaction_index, + from: status.from, + to: status.to, + contract_address: status.contract_address, + logs: status.logs.clone(), + logs_bloom: status.logs_bloom.clone(), + }; + + txs.push(tx_status); + } + } + + if let Ok(mut rs) = RECEIPTS.lock() { + for receipt in receipts.iter() { + let f = FrontierReceiptData { + state_root: receipt.state_root, + used_gas: receipt.used_gas, + logs_bloom: receipt.logs_bloom, + logs: receipt.logs.clone(), + }; + + rs.push(ReceiptAny::Frontier(f)); + } + } + } + } } debug!(target: "ethereum", "store new ethereum block: {}", block_number); diff --git a/src/components/contracts/modules/evm/Cargo.toml b/src/components/contracts/modules/evm/Cargo.toml index e64f9ce64..6bac465ee 100644 --- a/src/components/contracts/modules/evm/Cargo.toml +++ b/src/components/contracts/modules/evm/Cargo.toml @@ -38,6 +38,8 @@ config = { path = "../../../config"} storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.0.0" } ledger = { path = "../../../../ledger" } +enterprise-web3 = { path = "../../primitives/enterprise-web3", optional = true } + [dev-dependencies] baseapp = { path = "../../baseapp" } @@ -45,3 +47,6 @@ fp-mocks = { path = "../../primitives/mocks" } module-account = { path = "../account" } module-ethereum = { path = "../ethereum" } serde_json = "1.0.64" + +[features] +web3_service = ["enterprise-web3"] \ No newline at end of file diff --git a/src/components/contracts/modules/evm/src/lib.rs b/src/components/contracts/modules/evm/src/lib.rs index 3e9de83cb..1a8574c6e 100644 --- a/src/components/contracts/modules/evm/src/lib.rs +++ b/src/components/contracts/modules/evm/src/lib.rs @@ -2,6 +2,8 @@ #![allow(missing_docs)] #![allow(clippy::too_many_arguments)] +extern crate core; + mod basic; pub mod impls; pub mod precompile; @@ -228,6 +230,220 @@ impl App { pending_outputs } + pub fn admin_stake( + &self, + ctx: &Context, + validator: H160, + public_key: Vec, + staker: H160, + memo: String, + rate: U256, + ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + let function = self.contracts.staking.function("adminStake").c(d!())?; + + let validator = Token::Address(validator); + let public_key = Token::Bytes(public_key); + let staker = Token::Address(staker); + let memo = Token::String(memo); + let rate = Token::Uint(rate); + + let input = function + .encode_input(&[validator, public_key, staker, memo, rate]) + .c(d!())?; + + let from = H160::zero(); + let value = U256::zero(); + let gas_limit = 9999999; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + let action = TransactionAction::Call(self.contracts.staking_address); + let gas_price = U256::one(); + let transaction_index: u32 = 0; + let transaction_hash: H256 = H256::zero(); + + Ok(Self::system_transaction( + transaction_hash, + input, + value, + action, + U256::from(gas_limit), + gas_price, + used_gas, + transaction_index, + from, + self.contracts.staking_address, + logs, + )) + } + + pub fn system_set_delegation( + &self, + ctx: &Context, + validator: H160, + delegator: H160, + amount: U256, + ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + let function = self + .contracts + .staking + .function("systemSetDelegation") + .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 from = H160::zero(); + let value = U256::zero(); + let gas_limit = 9999999; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + let action = TransactionAction::Call(self.contracts.staking_address); + let gas_price = U256::one(); + let transaction_index: u32 = 0; + let transaction_hash: H256 = H256::zero(); + + Ok(Self::system_transaction( + transaction_hash, + input, + value, + action, + U256::from(gas_limit), + gas_price, + used_gas, + transaction_index, + from, + self.contracts.staking_address, + logs, + )) + } + + pub fn system_set_delegation_unbound( + &self, + ctx: &Context, + validator: H160, + delegator: H160, + amount: U256, + target_height: U256, + ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + let function = self + .contracts + .staking + .function("systemSetDelegationUnbound") + .c(d!())?; + + let validator = Token::Address(validator); + let delegator = Token::Address(delegator); + let amount = Token::Uint(amount); + let target_height = Token::Uint(target_height); + + let input = function + .encode_input(&[validator, delegator, amount, target_height]) + .c(d!())?; + + let from = H160::zero(); + let value = U256::zero(); + let gas_limit = 9999999; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + let action = TransactionAction::Call(self.contracts.staking_address); + let gas_price = U256::one(); + let transaction_index: u32 = 0; + let transaction_hash: H256 = H256::zero(); + + Ok(Self::system_transaction( + transaction_hash, + input, + value, + action, + U256::from(gas_limit), + gas_price, + used_gas, + transaction_index, + from, + self.contracts.staking_address, + logs, + )) + } + + pub fn system_set_rewards( + &self, + ctx: &Context, + delegator: &H160, + amount: U256, + ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + let function = self + .contracts + .staking + .function("systemSetRewards") + .c(d!())?; + + let delegator = Token::Address(*delegator); + let amount = Token::Uint(amount); + + let input = function.encode_input(&[delegator, amount]).c(d!())?; + + let from = H160::zero(); + let value = U256::zero(); + let gas_limit = 9999999; + + let (_, logs, used_gas) = ActionRunner::::execute_systemc_contract( + ctx, + input.clone(), + from, + gas_limit, + self.contracts.staking_address, + value, + )?; + + let action = TransactionAction::Call(self.contracts.staking_address); + let gas_price = U256::one(); + let transaction_index: u32 = 0; + let transaction_hash: H256 = H256::zero(); + + Ok(Self::system_transaction( + transaction_hash, + input, + value, + action, + U256::from(gas_limit), + gas_price, + used_gas, + transaction_index, + from, + self.contracts.staking_address, + logs, + )) + } + fn logs_bloom(logs: &[ethereum::Log], bloom: &mut Bloom) { for log in logs { bloom.accrue(BloomInput::Raw(&log.address[..])); diff --git a/src/components/contracts/modules/evm/src/runtime/runner.rs b/src/components/contracts/modules/evm/src/runtime/runner.rs index 9e853f7aa..7525ac9b1 100644 --- a/src/components/contracts/modules/evm/src/runtime/runner.rs +++ b/src/components/contracts/modules/evm/src/runtime/runner.rs @@ -309,6 +309,7 @@ impl Runner for ActionRunner { let address = executor.create_address(evm::CreateScheme::Legacy { caller: args.source, }); + ( executor .transact_create( diff --git a/src/components/contracts/modules/evm/src/runtime/stack.rs b/src/components/contracts/modules/evm/src/runtime/stack.rs index f65e96e1b..ed8f9fd2d 100644 --- a/src/components/contracts/modules/evm/src/runtime/stack.rs +++ b/src/components/contracts/modules/evm/src/runtime/stack.rs @@ -268,7 +268,18 @@ impl<'context, 'vicinity, 'config, C: Config> StackState<'config> fn inc_nonce(&mut self, address: H160) { let account_id = C::AddressMapping::convert_to_account_id(address); - let _ = C::AccountAsset::inc_nonce(self.ctx, &account_id); + let _nonce = C::AccountAsset::inc_nonce(self.ctx, &account_id); + + #[cfg(feature = "enterprise-web3")] + { + use enterprise_web3::{NONCE_MAP, WEB3_SERVICE_START_HEIGHT}; + if self.ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + let mut nonce_map = NONCE_MAP.lock().expect("get nonce map error"); + if let Ok(nonce) = _nonce { + nonce_map.insert(address, nonce); + } + } + } } fn set_storage(&mut self, address: H160, index: H256, value: H256) { @@ -308,6 +319,49 @@ impl<'context, 'vicinity, 'config, C: Config> StackState<'config> ); } } + + #[cfg(feature = "enterprise-web3")] + { + use enterprise_web3::{ + State, PENDING_STATE_UPDATE_LIST, REMOVE_PENDING_STATE_UPDATE_LIST, + STATE_UPDATE_LIST, WEB3_SERVICE_START_HEIGHT, + }; + use fp_core::context::RunTxMode; + if self.ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + if RunTxMode::Deliver == self.ctx.run_mode { + let mut remove_pending_state_list = REMOVE_PENDING_STATE_UPDATE_LIST + .lock() + .expect("get code map fail"); + remove_pending_state_list.push((address, index)); + + if let Ok(mut state_list) = STATE_UPDATE_LIST.lock() { + state_list.push(State { + height: self.ctx.header.height as u32, + address, + index, + value, + }); + } else { + tracing::error!( + target: "evm", + "Failed push state update to STATE_UPDATE_LIST for {:?} [index: {:?}, value: {:?}]", + address, + index, + value, + ) + } + } else { + let mut state_list = + PENDING_STATE_UPDATE_LIST.lock().expect("get code map fail"); + state_list.push(State { + height: 0, + address, + index, + value, + }); + } + } + } } fn reset_storage(&mut self, address: H160) { @@ -333,7 +387,11 @@ impl<'context, 'vicinity, 'config, C: Config> StackState<'config> code_len, address ); - if let Err(e) = App::::create_account(self.ctx, address.into(), code) { + #[cfg(feature = "enterprise-web3")] + let code_clone = code.clone(); + + let result = App::::create_account(self.ctx, address.into(), code); + if let Err(e) = result { tracing::error!( target: "evm", "Failed inserting code ({} bytes) at {:?}, error: {:?}", @@ -341,6 +399,28 @@ impl<'context, 'vicinity, 'config, C: Config> StackState<'config> address, e ); + } else { + #[cfg(feature = "enterprise-web3")] + { + use enterprise_web3::{ + CODE_MAP, PENDING_CODE_MAP, REMOVE_PENDING_CODE_MAP, + WEB3_SERVICE_START_HEIGHT, + }; + use fp_core::context::RunTxMode; + if self.ctx.header.height as u64 > *WEB3_SERVICE_START_HEIGHT { + if RunTxMode::Deliver == self.ctx.run_mode { + let mut code_map = CODE_MAP.lock().expect("get code map fail"); + code_map.insert(address, code_clone.clone()); + let mut remove_pending_code_map = + REMOVE_PENDING_CODE_MAP.lock().expect("get code map fail"); + remove_pending_code_map.push(address); + } else { + let mut code_map = + PENDING_CODE_MAP.lock().expect("get code map fail"); + code_map.insert(address, code_clone); + } + } + } } } diff --git a/src/components/contracts/modules/evm/src/utils.rs b/src/components/contracts/modules/evm/src/utils.rs index 526fde4a0..7c066c3c0 100644 --- a/src/components/contracts/modules/evm/src/utils.rs +++ b/src/components/contracts/modules/evm/src/utils.rs @@ -135,3 +135,9 @@ pub fn compute_create2(caller: H160, salt: H256, code_hash: H256) -> H160 { hasher.update(&code_hash[..]); H256::from_slice(hasher.finalize().as_slice()).into() } + +pub fn address_mapping(pk: &XfrPublicKey) -> H160 { + let mut hasher = Keccak256::new(); + hasher.update(pk.to_bytes()); + return H160::from_slice(&hasher.finalize().as_slice()[..20]); +} diff --git a/src/components/contracts/primitives/enterprise-web3/Cargo.toml b/src/components/contracts/primitives/enterprise-web3/Cargo.toml new file mode 100644 index 000000000..4be8d4549 --- /dev/null +++ b/src/components/contracts/primitives/enterprise-web3/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "enterprise-web3" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4.0" +evm-exporter = { package = "evm-exporter", git = "https://github.com/FindoraNetwork/enterprise-web3.git"} +ethereum = { version = "0.12.0", default-features = false, features = ["with-serde"] } +primitive-types = "0.11.1" +redis = { version = "0.21" } +ruc = "1.0" diff --git a/src/components/contracts/primitives/enterprise-web3/src/lib.rs b/src/components/contracts/primitives/enterprise-web3/src/lib.rs new file mode 100644 index 000000000..ac69896e3 --- /dev/null +++ b/src/components/contracts/primitives/enterprise-web3/src/lib.rs @@ -0,0 +1,59 @@ +use evm_exporter::{ + Block as EnterpriseBlock, Getter, Receipt as EnterpriseReceipt, Setter, + State as EnterpriseState, TransactionStatus as EnterpriseTxState, +}; +use lazy_static::lazy_static; +use primitive_types::{H160, H256, U256}; +use redis::{Client, Connection}; +use ruc::*; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +pub type State = EnterpriseState; +pub type Block = EnterpriseBlock; +pub type Receipt = EnterpriseReceipt; +pub type TxState = EnterpriseTxState; + +lazy_static! { + pub static ref STATE_UPDATE_LIST: Arc>> = + Arc::new(Mutex::new(vec![])); + pub static ref NONCE_MAP: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref BALANCE_MAP: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref CODE_MAP: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref BLOCK: Arc>> = Arc::new(Mutex::new(None)); + pub static ref RECEIPTS: Arc>> = Arc::new(Mutex::new(vec![])); + pub static ref TXS: Arc>> = Arc::new(Mutex::new(vec![])); + static ref REDIS_CLIENT: Arc> = + Arc::new(Mutex::new(gen_redis_client())); + pub static ref WEB3_SERVICE_START_HEIGHT: u64 = load_start_height(); + pub static ref PENDING_CODE_MAP: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref PENDING_STATE_UPDATE_LIST: Arc>> = + Arc::new(Mutex::new(vec![])); + pub static ref REMOVE_PENDING_CODE_MAP: Arc>> = + Arc::new(Mutex::new(vec![])); + pub static ref REMOVE_PENDING_STATE_UPDATE_LIST: Arc>> = + Arc::new(Mutex::new(vec![])); +} + +fn gen_redis_client() -> Client { + let redis_addr = std::env::var("REDIS_ADDR").unwrap(); + let client = Client::open(redis_addr).c(d!()).unwrap(); + client +} + +fn load_start_height() -> u64 { + let client = REDIS_CLIENT.lock().c(d!()).unwrap(); + let mut conn = client.get_connection().c(d!()).unwrap(); + let mut getter = Getter::new(&mut conn, "evm".to_string()); + getter.latest_height().unwrap() as u64 +} + +pub fn setter() -> Result> { + let client = REDIS_CLIENT.lock().c(d!())?; + let con = client.get_connection().c(d!())?; + Ok(Setter::new(con, "evm".to_string())) +} diff --git a/src/ledger/src/staking/mod.rs b/src/ledger/src/staking/mod.rs index 3ea34e5c2..f9a53f843 100644 --- a/src/ledger/src/staking/mod.rs +++ b/src/ledger/src/staking/mod.rs @@ -18,6 +18,7 @@ use {num_bigint::BigUint, std::convert::TryFrom}; pub mod cosig; pub mod init; pub mod ops; +pub mod transfer_staking; use { crate::{ diff --git a/src/ledger/src/staking/transfer_staking.rs b/src/ledger/src/staking/transfer_staking.rs new file mode 100644 index 000000000..4760fec88 --- /dev/null +++ b/src/ledger/src/staking/transfer_staking.rs @@ -0,0 +1,111 @@ +//! transfer staking data to evm-staking + +use super::{Delegation, DelegationState, StakerMemo, Staking, BLOCK_HEIGHT_MAX}; +use noah::xfr::sig::XfrPublicKey; +use ruc::{d, Result, RucResult}; + +use std::collections::HashMap; + +impl Staking { + ///make date for transfer. + pub fn make_transfer_data( + &mut self, + ) -> Result<( + Vec<(Vec, Vec, XfrPublicKey, u64, StakerMemo, u64)>, + Vec<(XfrPublicKey, XfrPublicKey, u64)>, + Vec<(XfrPublicKey, XfrPublicKey, u64, u64)>, + )> { + let mut delegation_bond = HashMap::new(); + let mut delegation_unbond = HashMap::new(); + + let keys: Vec<_> = self + .delegation_info + .global_delegation_records_map + .keys() + .map(|k| *k) + .collect(); + + for pk in keys.into_iter() { + let d = self + .delegation_info + .global_delegation_records_map + .remove(&pk) + .unwrap(); + if d.state == DelegationState::Bond { + if d.end_height == BLOCK_HEIGHT_MAX { + delegation_bond.insert(pk, d); + } else { + delegation_unbond.insert(pk, d); + } + } else { + self.delegation_info + .global_delegation_records_map + .insert(pk, d); + } + } + + let height_last = *self.validator_info.keys().rev().next().c(d!())?; + println!("Data at height: {}", height_last); + + let validator_data = self.validator_info.remove(&height_last).c(d!())?; + + let mut stake_date = vec![]; + for (pk, v) in validator_data.body.into_iter() { + stake_date.push(( + v.td_addr, + v.td_pubkey, + pk, + v.td_power, + v.memo, + mapping_rate(v.commission_rate), + )); + } + + Ok(( + stake_date, + flatten_filter_delegations(delegation_bond), + flatten_filter_delegations_unbond(delegation_unbond)?, + )) + } +} + +// remove delgations that delegate to 0, flatten nested map to list. +// return (delegator public key, validator public key, amount). +fn flatten_filter_delegations( + delegations: HashMap, +) -> Vec<(XfrPublicKey, XfrPublicKey, u64)> { + let mut results = vec![]; + for (pk, d) in delegations.into_iter() { + for (v, am) in d.delegations { + if am != 0 { + results.push((pk, v, am)); + } + } + } + return results; +} + +// get unbond delegation +fn flatten_filter_delegations_unbond( + delegations: HashMap, +) -> Result> { + let mut results = vec![]; + for (_, d) in delegations.into_iter() { + let (v, am) = d.delegations.into_iter().next().c(d!())?; + if am != 0 { + let pk = d.receiver_pk.c(d!())?; + results.push((pk, v, am, d.end_height)); + } + } + return Ok(results); +} + +/// mapping [u64;2] to evm rate. +fn mapping_rate(rate: [u64; 2]) -> u64 { + if rate[0] == 0 { + return 0; + } + + let deciamls = 1000_000_u64; + rate[0] * deciamls / rate[1] +}