From 4ce62e70fa293c89e5a97cdda7a1f07e132a0b5d Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 25 May 2020 20:57:45 -0400 Subject: [PATCH 01/30] switched state_manager db to a chainstore --- blockchain/chain_sync/src/sync.rs | 2 +- blockchain/message_pool/src/errors.rs | 0 blockchain/message_pool/src/msgpool.rs | 0 blockchain/state_manager/Cargo.toml | 1 + blockchain/state_manager/src/lib.rs | 23 ++++++++++++----------- forest/src/cli/genesis.rs | 2 +- 6 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 blockchain/message_pool/src/errors.rs create mode 100644 blockchain/message_pool/src/msgpool.rs diff --git a/blockchain/chain_sync/src/sync.rs b/blockchain/chain_sync/src/sync.rs index 5c6fed05b6e2..c2a0ce2b7ce8 100644 --- a/blockchain/chain_sync/src/sync.rs +++ b/blockchain/chain_sync/src/sync.rs @@ -110,7 +110,7 @@ where network_rx: Receiver, genesis: Tipset, ) -> Result { - let state_manager = StateManager::new(chain_store.db.clone()); + let state_manager = StateManager::new(ChainStore::new(chain_store.db.clone())); // Split incoming channel to handle blocksync requests let (rpc_send, rpc_rx) = channel(20); diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/blockchain/state_manager/Cargo.toml b/blockchain/state_manager/Cargo.toml index c4859dee1787..8ab02f042b5a 100644 --- a/blockchain/state_manager/Cargo.toml +++ b/blockchain/state_manager/Cargo.toml @@ -17,3 +17,4 @@ forest_blocks = { path = "../../blockchain/blocks" } thiserror = "1.0" interpreter = { path = "../../vm/interpreter/" } ipld_amt = { path = "../../ipld/amt/" } +chain = { path = "../chain" } diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index df8d47643ddd..4a0d7242900f 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -17,10 +17,11 @@ use num_bigint::BigUint; use state_tree::StateTree; use std::error::Error as StdError; use std::sync::Arc; +use chain::ChainStore; /// Intermediary for retrieving state objects and updating actor states pub struct StateManager { - bs: Arc, + cs: ChainStore, } impl StateManager @@ -28,8 +29,8 @@ where DB: BlockStore, { /// constructor - pub fn new(bs: Arc) -> Self { - Self { bs } + pub fn new(cs: ChainStore) -> Self { + Self { cs } } /// Loads actor state from IPLD Store fn load_actor_state(&self, addr: &Address, state_cid: &Cid) -> Result @@ -40,14 +41,14 @@ where .get_actor(addr, state_cid)? .ok_or_else(|| Error::ActorNotFound(addr.to_string()))?; let act: D = self - .bs + .cs.blockstore() .get(&actor.state) .map_err(|e| Error::State(e.to_string()))? .ok_or_else(|| Error::ActorStateNotFound(actor.state.to_string()))?; Ok(act) } fn get_actor(&self, addr: &Address, state_cid: &Cid) -> Result, Error> { - let state = StateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?; + let state = StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; state.get_actor(addr).map_err(Error::State) } /// Returns the network name from the init actor state @@ -63,7 +64,7 @@ where } let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?; - match ps.get_claim(self.bs.as_ref(), addr)? { + match ps.get_claim(self.cs.blockstore(), addr)? { Some(_) => Ok(false), None => Ok(true), } @@ -72,9 +73,9 @@ where pub fn get_miner_work_addr(&self, state_cid: &Cid, addr: &Address) -> Result { let ms: miner::State = self.load_actor_state(addr, state_cid)?; - let state = StateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?; + let state = StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; // Note: miner::State info likely to be changed to CID - let addr = resolve_to_key_addr(&state, self.bs.as_ref(), &ms.info.worker) + let addr = resolve_to_key_addr(&state, self.cs.blockstore(), &ms.info.worker) .map_err(|e| Error::Other(format!("Failed to resolve key address; error: {}", e)))?; Ok(addr) } @@ -82,7 +83,7 @@ where pub fn get_power(&self, state_cid: &Cid, addr: &Address) -> Result<(BigUint, BigUint), Error> { let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?; - if let Some(claim) = ps.get_claim(self.bs.as_ref(), addr)? { + if let Some(claim) = ps.get_claim(self.cs.blockstore(), addr)? { Ok((claim.power, ps.total_network_power)) } else { Err(Error::State( @@ -94,7 +95,7 @@ where /// Performs the state transition for the tipset and applies all unique messages in all blocks. /// This function returns the state root and receipt root of the transition. pub fn apply_blocks(&self, ts: &FullTipset) -> Result<(Cid, Cid), Box> { - let mut buf_store = BufferedBlockStore::new(self.bs.as_ref()); + let mut buf_store = BufferedBlockStore::new(self.cs.blockstore()); // TODO possibly switch out syscalls to be saved at state manager level let mut vm = VM::new( ts.parent_state(), @@ -107,7 +108,7 @@ where let receipts = vm.apply_tip_set_messages(ts)?; // Construct receipt root from receipts - let rect_root = Amt::new_from_slice(self.bs.as_ref(), &receipts)?; + let rect_root = Amt::new_from_slice(self.cs.blockstore(), &receipts)?; // Flush changes to blockstore let state_root = vm.flush()?; diff --git a/forest/src/cli/genesis.rs b/forest/src/cli/genesis.rs index 05b83bcbb5a2..104a3fec7f3e 100644 --- a/forest/src/cli/genesis.rs +++ b/forest/src/cli/genesis.rs @@ -42,7 +42,7 @@ where // This is just a workaround to get the network name before the sync process starts to use in // the pubsub topics, hopefully can be removed in future. - let sm = StateManager::new(chain_store.db.clone()); + let sm = StateManager::new(ChainStore::new(chain_store.db.clone())); let network_name = sm.get_network_name(genesis.state_root()).expect( "Genesis not initialized properly, failed to retrieve network name. \ Requires either a previously initialized genesis or with genesis config option set", From 3235b0c8b61c10b9a34b9111fceebfb3caad9eb8 Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 25 May 2020 22:16:32 -0400 Subject: [PATCH 02/30] started messagepool, added code structure and started mempool provider and mempool --- Cargo.toml | 1 + blockchain/message_pool/Cargo.toml | 22 ++++ blockchain/message_pool/src/errors.rs | 26 +++++ blockchain/message_pool/src/lib.rs | 2 + blockchain/message_pool/src/msgpool.rs | 155 +++++++++++++++++++++++++ blockchain/state_manager/src/lib.rs | 15 ++- 6 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 blockchain/message_pool/Cargo.toml create mode 100644 blockchain/message_pool/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 43c91217f12a..8eb6b62d25be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "blockchain/state_manager", "blockchain/chain_sync", "blockchain/beacon", + "blockchain/message_pool", "vm", "vm/actor", "vm/address", diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml new file mode 100644 index 000000000000..dc9b90ddd031 --- /dev/null +++ b/blockchain/message_pool/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "message_pool" +version = "0.1.0" +authors = ["ChainSafe Systems "] +edition = "2018" + +[dependencies] +address = { package = "forest_address", path = "../../vm/address" } +vm = { package = "forest_vm", path = "../../vm" } +blocks = { package = "forest_blocks", path = "../blocks" } +message = { package = "forest_message", path = "../../vm/message" } +thiserror = "1.0" +state_manager = { path = "../state_manager" } +cid = { package = "forest_cid", path = "../../ipld/cid", version = "0.1" } +encoding = { package = "forest_encoding", path = "../../encoding", version = "0.1" } +blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" } +num-bigint = { path = "../../utils/bigint", package = "forest_bigint" } +lru = "0.4.5" +crypto = { package = "forest_crypto", path = "../../crypto", version = "0.1" } +chain = { path = "../chain"} +state_tree = { path = "../../vm/state_tree/" } +interpreter = { path = "../../vm/interpreter/" } \ No newline at end of file diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index e69de29bb2d1..b8bc92e0986e 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -0,0 +1,26 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use thiserror::Error; + +// /// MessagePool error +#[derive(Debug, PartialEq, Error)] +pub enum Error { + /// Error indicating message that's too large + #[error("Message is too big")] + MessageTooBig, + #[error("Cannot send more Filecoin than will ever exist")] + MessageValueTooHigh, + #[error("Message nonce too low")] + NonceTooLow, + #[error("not enough funds to execute transaction")] + NotEnoughFunds, + #[error("invalid to address for message")] + InvalidToAddr, + #[error("message with nonce already in mempool")] + DuplicateNonce, + #[error("broadcasting message despite validation fail")] + BroadcastAnyway, + #[error("{0}")] + Other(String), +} diff --git a/blockchain/message_pool/src/lib.rs b/blockchain/message_pool/src/lib.rs new file mode 100644 index 000000000000..5115cd42791f --- /dev/null +++ b/blockchain/message_pool/src/lib.rs @@ -0,0 +1,2 @@ +mod errors; +mod msgpool; diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index e69de29bb2d1..9af333ecdfdf 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -0,0 +1,155 @@ +use super::errors::Error; +use crate::errors::Error::DuplicateNonce; +use address::Address; +use blocks::{BlockHeader, Tipset, TipsetKeys}; +use blockstore::BlockStore; +use chain::ChainStore; +use cid::multihash::Blake2b256; +use cid::Cid; +use crypto::Signature; +use interpreter::{resolve_to_key_addr, DefaultSyscalls, VM}; +use lru::LruCache; +use message::{Message, SignedMessage, UnsignedMessage}; +use num_bigint::{BigInt, ToBigInt}; +use state_manager::StateManager; +use state_tree::StateTree; +use std::collections::HashMap; +use vm::ActorState; + +struct MsgSet { + msgs: HashMap, + next_nonce: u64, +} + +impl MsgSet { + pub fn new() -> Self { + MsgSet { + msgs: HashMap::new(), + next_nonce: 0, + } + } + + pub fn add(&mut self, m: SignedMessage) -> Result<(), Error> { + if self.msgs.is_empty() || m.sequence() >= self.next_nonce { + self.next_nonce = m.sequence() + 1; + } + if self.msgs.contains_key(&m.sequence()) { + // need to fix in the event that there's an err raised from calling this next line + return Err(DuplicateNonce); + } + self.msgs.insert(m.sequence(), m); + return Ok(()); + } +} + +trait Provider { + fn put_message(&self, msg: &SignedMessage) -> Result; + fn state_get_actor(&self, addr: &Address) -> Result, Error>; + fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result; // TODO dunno how to do this + fn messages_for_block( + &self, + h: &BlockHeader, + ) -> Result<(Vec, Vec), Error>; + fn messages_for_tipset(&self, h: &Tipset) -> Result, Error>; + fn load_tipset(&self, tsk: &TipsetKeys) -> Result; // TODO dunno how to do this +} + +struct MpoolProvider { + sm: StateManager, +} + +impl<'db, DB> MpoolProvider +where + DB: BlockStore, +{ + pub fn new(sm: StateManager) -> Self + where + DB: BlockStore, + { + MpoolProvider { sm } + } +} + +impl Provider for MpoolProvider +where + DB: BlockStore, +{ + fn put_message(&self, msg: &SignedMessage) -> Result { + let cid = self + .sm + .get_cs() + .db + .put(msg, Blake2b256) + .map_err(|err| Error::Other(err.to_string()))?; + return Ok(cid); + } + + fn state_get_actor(&self, addr: &Address) -> Result, Error> { + let state = StateTree::new(self.sm.get_cs().db.as_ref()); + //TODO need to have this error be an Error::Other from state_manager errs + state.get_actor(addr).map_err(|err| Error::Other(err)) + } + + fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result { + unimplemented!() + } + + fn messages_for_block( + &self, + h: &BlockHeader, + ) -> Result<(Vec, Vec), Error> { + self.sm + .get_cs() + .messages(h) + .map_err(|err| Error::Other(err.to_string())) + } + + fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { + unimplemented!() + } + + fn load_tipset(&self, tsk: &TipsetKeys) -> Result { + self.sm + .get_cs() + .tipset_from_keys(tsk) + .map_err(|err| Error::Other(err.to_string())) + } +} + +struct MessagePool { + // need to inquire about closer in golang and rust equivalent + local_addrs: HashMap, + pending: HashMap, + cur_tipset: String, // need to wait until pubsub is done + api: MpoolProvider, // will need to replace with provider type + min_gas_price: BigInt, + max_tx_pool_size: i64, + network_name: String, + bls_sig_cache: LruCache, + sig_val_cache: LruCache, +} + +impl MessagePool +where + DB: BlockStore, +{ + pub fn new(api: MpoolProvider, network_name: String) -> Self + where + DB: BlockStore, + { + // LruCache sizes have been taken from the lotus implementation + let bls_sig_cache = LruCache::new(40000); + let sig_val_cache = LruCache::new(32000); + MessagePool { + local_addrs: HashMap::new(), + pending: HashMap::new(), + cur_tipset: "tmp".to_string(), // cannnot do this yet, need pubsub done + api, + min_gas_price: ToBigInt::to_bigint(&0).unwrap(), + max_tx_pool_size: 5000, + network_name, + bls_sig_cache, + sig_val_cache, + } + } +} diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 4a0d7242900f..759f52512288 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -8,6 +8,7 @@ use actor::{init, miner, power, ActorState, INIT_ACTOR_ADDR, STORAGE_POWER_ACTOR use address::{Address, Protocol}; use blockstore::BlockStore; use blockstore::BufferedBlockStore; +use chain::ChainStore; use cid::Cid; use encoding::de::DeserializeOwned; use forest_blocks::FullTipset; @@ -17,7 +18,6 @@ use num_bigint::BigUint; use state_tree::StateTree; use std::error::Error as StdError; use std::sync::Arc; -use chain::ChainStore; /// Intermediary for retrieving state objects and updating actor states pub struct StateManager { @@ -32,6 +32,10 @@ where pub fn new(cs: ChainStore) -> Self { Self { cs } } + /// get ChainStore to modify + pub fn get_cs(&self) -> &ChainStore { + &self.cs + } /// Loads actor state from IPLD Store fn load_actor_state(&self, addr: &Address, state_cid: &Cid) -> Result where @@ -41,14 +45,16 @@ where .get_actor(addr, state_cid)? .ok_or_else(|| Error::ActorNotFound(addr.to_string()))?; let act: D = self - .cs.blockstore() + .cs + .blockstore() .get(&actor.state) .map_err(|e| Error::State(e.to_string()))? .ok_or_else(|| Error::ActorStateNotFound(actor.state.to_string()))?; Ok(act) } fn get_actor(&self, addr: &Address, state_cid: &Cid) -> Result, Error> { - let state = StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; + let state = + StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; state.get_actor(addr).map_err(Error::State) } /// Returns the network name from the init actor state @@ -73,7 +79,8 @@ where pub fn get_miner_work_addr(&self, state_cid: &Cid, addr: &Address) -> Result { let ms: miner::State = self.load_actor_state(addr, state_cid)?; - let state = StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; + let state = + StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; // Note: miner::State info likely to be changed to CID let addr = resolve_to_key_addr(&state, self.cs.blockstore(), &ms.info.worker) .map_err(|e| Error::Other(format!("Failed to resolve key address; error: {}", e)))?; From 9303053433330c6db22c6ce8a0080f0b047aaf9c Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 26 May 2020 10:09:13 -0400 Subject: [PATCH 03/30] updated mpool cargo.toml --- blockchain/message_pool/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index dc9b90ddd031..33a62286083c 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -16,7 +16,7 @@ encoding = { package = "forest_encoding", path = "../../encoding", version = "0. blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" } num-bigint = { path = "../../utils/bigint", package = "forest_bigint" } lru = "0.4.5" -crypto = { package = "forest_crypto", path = "../../crypto", version = "0.1" } +crypto = { package = "forest_crypto", path = "../../crypto", version = "0.2" } chain = { path = "../chain"} state_tree = { path = "../../vm/state_tree/" } interpreter = { path = "../../vm/interpreter/" } \ No newline at end of file From 1b1d3ed184e91c0053f1583a805c18be7592c030 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 26 May 2020 10:24:55 -0400 Subject: [PATCH 04/30] fixed dependency and some clippy --- blockchain/message_pool/src/msgpool.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 9af333ecdfdf..81bdd51cf67c 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,11 +3,10 @@ use crate::errors::Error::DuplicateNonce; use address::Address; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; -use chain::ChainStore; +// use chain::ChainStore; use cid::multihash::Blake2b256; use cid::Cid; use crypto::Signature; -use interpreter::{resolve_to_key_addr, DefaultSyscalls, VM}; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; use num_bigint::{BigInt, ToBigInt}; @@ -38,7 +37,7 @@ impl MsgSet { return Err(DuplicateNonce); } self.msgs.insert(m.sequence(), m); - return Ok(()); + Ok(()) } } @@ -81,13 +80,13 @@ where .db .put(msg, Blake2b256) .map_err(|err| Error::Other(err.to_string()))?; - return Ok(cid); + Ok(cid) } fn state_get_actor(&self, addr: &Address) -> Result, Error> { let state = StateTree::new(self.sm.get_cs().db.as_ref()); //TODO need to have this error be an Error::Other from state_manager errs - state.get_actor(addr).map_err(|err| Error::Other(err)) + state.get_actor(addr).map_err(Error::Other) } fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result { From 5d6f622c39ec5647e962d8fa363392b19d65fedf Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 26 May 2020 10:33:02 -0400 Subject: [PATCH 05/30] added license --- blockchain/message_pool/src/lib.rs | 3 +++ blockchain/message_pool/src/msgpool.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/blockchain/message_pool/src/lib.rs b/blockchain/message_pool/src/lib.rs index 5115cd42791f..c4fe5bb91ac9 100644 --- a/blockchain/message_pool/src/lib.rs +++ b/blockchain/message_pool/src/lib.rs @@ -1,2 +1,5 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + mod errors; mod msgpool; diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 81bdd51cf67c..44a9f8b9c912 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -1,3 +1,6 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + use super::errors::Error; use crate::errors::Error::DuplicateNonce; use address::Address; From bba60ea08f965789f1cbdb46ec292ab5d04a9a6b Mon Sep 17 00:00:00 2001 From: flodesi Date: Thu, 28 May 2020 01:22:35 -0400 Subject: [PATCH 06/30] added add to msgpool --- blockchain/message_pool/Cargo.toml | 3 +- blockchain/message_pool/src/errors.rs | 8 +- blockchain/message_pool/src/lib.rs | 3 + blockchain/message_pool/src/msgpool.rs | 176 +++++++++++++++++++++++-- 4 files changed, 176 insertions(+), 14 deletions(-) diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index 33a62286083c..e50201fc942b 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -19,4 +19,5 @@ lru = "0.4.5" crypto = { package = "forest_crypto", path = "../../crypto", version = "0.2" } chain = { path = "../chain"} state_tree = { path = "../../vm/state_tree/" } -interpreter = { path = "../../vm/interpreter/" } \ No newline at end of file +interpreter = { path = "../../vm/interpreter/" } +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index b8bc92e0986e..86e8a2f56e19 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -19,8 +19,12 @@ pub enum Error { InvalidToAddr, #[error("message with nonce already in mempool")] DuplicateNonce, - #[error("broadcasting message despite validation fail")] - BroadcastAnyway, + #[error("signature validation failed")] + SigVerification, + #[error("Unknown signature type")] + UnknownSigType, + #[error("BLS signature too short")] + BLSSigTooShort, #[error("{0}")] Other(String), } diff --git a/blockchain/message_pool/src/lib.rs b/blockchain/message_pool/src/lib.rs index c4fe5bb91ac9..4bb1c40b6f61 100644 --- a/blockchain/message_pool/src/lib.rs +++ b/blockchain/message_pool/src/lib.rs @@ -3,3 +3,6 @@ mod errors; mod msgpool; + +pub use self::errors::*; +pub use self::msgpool::*; diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 44a9f8b9c912..298fad6fc3d5 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -9,13 +9,15 @@ use blockstore::BlockStore; // use chain::ChainStore; use cid::multihash::Blake2b256; use cid::Cid; -use crypto::Signature; +use crypto::{Signature, SignatureType}; +use encoding::Cbor; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{BigInt, BigUint, ToBigInt, ToBigUint}; +// use serde::Serialize; use state_manager::StateManager; use state_tree::StateTree; -use std::collections::HashMap; +use std::{collections::HashMap, str::from_utf8}; use vm::ActorState; struct MsgSet { @@ -31,22 +33,35 @@ impl MsgSet { } } - pub fn add(&mut self, m: SignedMessage) -> Result<(), Error> { + pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { if self.msgs.is_empty() || m.sequence() >= self.next_nonce { self.next_nonce = m.sequence() + 1; } if self.msgs.contains_key(&m.sequence()) { // need to fix in the event that there's an err raised from calling this next line - return Err(DuplicateNonce); + let exms = self.msgs.get(&m.sequence()).unwrap(); + if m.cid().map_err(|err| Error::Other(err.to_string()))? + != exms.cid().map_err(|err| Error::Other(err.to_string()))? + { + let mut gas_price = exms.message().gas_price(); + let replace_by_fee_ratio: f32 = 1.25; + let rbf_num = + BigUint::from(((replace_by_fee_ratio - 1 as f32) * 256 as f32) as u64); + let rbf_denom = BigUint::from(256 as u64); + let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom; + if m.message().gas_price() <= &min_price { + return Err(DuplicateNonce); + } + } } - self.msgs.insert(m.sequence(), m); + self.msgs.insert(m.sequence(), m.clone()); Ok(()) } } trait Provider { fn put_message(&self, msg: &SignedMessage) -> Result; - fn state_get_actor(&self, addr: &Address) -> Result, Error>; + fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result; fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result; // TODO dunno how to do this fn messages_for_block( &self, @@ -86,10 +101,15 @@ where Ok(cid) } - fn state_get_actor(&self, addr: &Address) -> Result, Error> { - let state = StateTree::new(self.sm.get_cs().db.as_ref()); + fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { + let state = StateTree::new_from_root(self.sm.get_cs().db.as_ref(), ts.parent_state()) + .map_err(|err| Error::Other(err))?; //TODO need to have this error be an Error::Other from state_manager errs - state.get_actor(addr).map_err(Error::Other) + let actor = state.get_actor(addr).map_err(Error::Other)?; + match actor { + Some(actor_state) => Ok(actor_state), + None => Err(Error::Other("No actor state".to_string())), + } } fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result { @@ -121,7 +141,7 @@ where struct MessagePool { // need to inquire about closer in golang and rust equivalent local_addrs: HashMap, - pending: HashMap, + pending: HashMap, cur_tipset: String, // need to wait until pubsub is done api: MpoolProvider, // will need to replace with provider type min_gas_price: BigInt, @@ -139,6 +159,7 @@ where where DB: BlockStore, { + // TODO create tipset // LruCache sizes have been taken from the lotus implementation let bls_sig_cache = LruCache::new(40000); let sig_val_cache = LruCache::new(32000); @@ -154,4 +175,137 @@ where sig_val_cache, } } + + pub fn push(&mut self, msg: &SignedMessage) -> Result { + // TODO will be used to addlocal which still needs to be implemented + let msg_serial = msg + .marshal_cbor() + .map_err(|err| return Error::Other(err.to_string()))?; + self.add(msg)?; + // TODO do pubsub publish with mp.netName and msg_serial + msg.cid().map_err(|err| Error::Other(err.to_string())) + } + + pub fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { + let size = msg + .marshal_cbor() + .map_err(|err| return Error::Other(err.to_string()))? + .len(); + if size > 32 * 1024 { + return Err(Error::MessageTooBig); + } + if msg + .value() + .gt(&ToBigUint::to_biguint(&2_000_000_000).unwrap()) + { + return Err(Error::MessageValueTooHigh); + } + + self.verify_msg_sig(msg)?; + + // TODO uncomment this when cur tipset is implemented + // self.add_tipset(msg, self.cur_tipset)?; + Ok(()) + } + + fn sig_cache_key(&mut self, msg: &SignedMessage) -> Result { + match msg.signature().signature_type() { + SignatureType::Secp256 => Ok(msg.cid().unwrap().to_string()), + SignatureType::BLS => { + if msg.signature().bytes().len() < 90 { + return Err(Error::BLSSigTooShort); + } + let slice = from_utf8(&msg.signature().bytes()[64..]).unwrap(); + let mut beginning = from_utf8(&msg.cid().unwrap().to_bytes()) + .unwrap() + .to_string(); + beginning.push_str(slice); + Ok(beginning) + } + } + } + + fn verify_msg_sig(&mut self, msg: &SignedMessage) -> Result<(), Error> { + let sck = self.sig_cache_key(msg)?; + let is_verif = self.sig_val_cache.get(&sck); + match is_verif { + Some(()) => return Ok(()), + None => { + let verif = msg + .signature() + .verify(&msg.message().cid().unwrap().to_bytes(), msg.from()); + match verif { + Ok(()) => { + self.sig_val_cache.put(sck, ()); + Ok(()) + } + Err(value) => Err(Error::Other(value)), + } + } + } + } + + fn add_tipset(&mut self, msg: &SignedMessage, cur_ts: &Tipset) -> Result<(), Error> { + let snonce = self.get_state_nonce(msg.from(), cur_ts)?; + + if snonce > msg.message().sequence() { + return Err(Error::NonceTooLow); + } + + let balance = self.get_state_balance(msg.from(), cur_ts)?; + let msg_balance = BigInt::from(msg.message().required_funds()); + if balance.lt(&msg_balance) { + return Err(Error::NotEnoughFunds); + } + self.add_locked(msg) + } + + fn add_locked(&mut self, msg: &SignedMessage) -> Result<(), Error> { + if msg.signature().signature_type() == SignatureType::BLS { + self.bls_sig_cache.put( + msg.cid().map_err(|err| Error::Other(err.to_string()))?, + msg.signature().clone(), + ); + } + if msg.message().gas_limit() > 100_000_000 { + return Err(Error::Other( + "given message has too high of a gas limit".to_string(), + )); + } + self.api.put_message(msg)?; + + let msett = self.pending.get_mut(msg.message().from()); + match msett { + Some(mset) => mset.add(msg).map_err(|err| Error::Other(err.to_string()))?, + None => { + let mut mset = MsgSet::new(); + mset.add(msg).map_err(|err| Error::Other(err.to_string()))?; + self.pending.insert(msg.message().from().clone(), mset); + } + } + // TODO pubsub msg + Ok(()) + } + + fn get_state_nonce(&self, addr: &Address, cur_ts: &Tipset) -> Result { + let actor = self.api.state_get_actor(&addr, cur_ts)?; + + let base_nonce = actor.sequence; + + // TODO will need to chang e this to set cur_ts to chain.head + // will implement this once we have subscribe to head change done + let msgs = self.api.messages_for_tipset(cur_ts).unwrap(); + + // TODO will need to call messages_for_tipset after it is implemented + // and iterate over the messages, and check whether or not the from + // addr from each message equals addr, if it is not throw error, otherwise + // increase base_nonce by 1 and then after loop termpinates return base_nonce + + Ok(base_nonce) + } + + fn get_state_balance(&self, addr: &Address, ts: &Tipset) -> Result { + let actor = self.api.state_get_actor(&addr, &ts)?; + return Ok(BigInt::from(actor.balance)); + } } From 30ea67014f2e9a5a4877bceb8b2ab623714bdbc6 Mon Sep 17 00:00:00 2001 From: flodesi Date: Thu, 28 May 2020 14:16:36 -0400 Subject: [PATCH 07/30] finished add and reverted state_manager back to normal --- blockchain/chain_sync/src/sync.rs | 16 ++++---- blockchain/message_pool/src/msgpool.rs | 57 ++++++++++++++++++-------- blockchain/state_manager/src/lib.rs | 38 +++++++---------- forest/src/cli/genesis.rs | 2 +- 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/blockchain/chain_sync/src/sync.rs b/blockchain/chain_sync/src/sync.rs index c2a0ce2b7ce8..cc6debc795de 100644 --- a/blockchain/chain_sync/src/sync.rs +++ b/blockchain/chain_sync/src/sync.rs @@ -99,9 +99,9 @@ struct MsgMetaData { } impl ChainSyncer -where - TBeacon: Beacon, - DB: BlockStore + Sync + Send + 'static, + where + TBeacon: Beacon, + DB: BlockStore + Sync + Send + 'static, { pub fn new( chain_store: ChainStore, @@ -110,7 +110,7 @@ where network_rx: Receiver, genesis: Tipset, ) -> Result { - let state_manager = StateManager::new(ChainStore::new(chain_store.db.clone())); + let state_manager = StateManager::new(chain_store.db.clone()); // Split incoming channel to handle blocksync requests let (rpc_send, rpc_rx) = channel(20); @@ -529,8 +529,8 @@ where msg_meta_data: &mut HashMap, tree: &StateTree, ) -> Result<(), Error> - where - M: Message, + where + M: Message, { let updated_state: MsgMetaData = match msg_meta_data.get(msg.from()) { // address is present begin validity checks @@ -943,7 +943,7 @@ mod tests { event_receiver, genesis_ts, ) - .unwrap(), + .unwrap(), event_sender, ) } @@ -1017,4 +1017,4 @@ mod tests { let root = compute_msg_data(cs.chain_store.blockstore(), &[bls], &[secp]).unwrap(); assert_eq!(root, expected_root); } -} +} \ No newline at end of file diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 298fad6fc3d5..10881ccbad47 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -6,7 +6,7 @@ use crate::errors::Error::DuplicateNonce; use address::Address; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; -// use chain::ChainStore; +use chain::ChainStore; use cid::multihash::Blake2b256; use cid::Cid; use crypto::{Signature, SignatureType}; @@ -18,7 +18,7 @@ use num_bigint::{BigInt, BigUint, ToBigInt, ToBigUint}; use state_manager::StateManager; use state_tree::StateTree; use std::{collections::HashMap, str::from_utf8}; -use vm::ActorState; +use vm::{ActorState, TokenAmount, MethodNum, Serialized}; struct MsgSet { msgs: HashMap, @@ -67,23 +67,23 @@ trait Provider { &self, h: &BlockHeader, ) -> Result<(Vec, Vec), Error>; - fn messages_for_tipset(&self, h: &Tipset) -> Result, Error>; + fn messages_for_tipset(&self, h: &Tipset) -> Result, Error>; fn load_tipset(&self, tsk: &TipsetKeys) -> Result; // TODO dunno how to do this } struct MpoolProvider { - sm: StateManager, + cs: ChainStore, } impl<'db, DB> MpoolProvider where DB: BlockStore, { - pub fn new(sm: StateManager) -> Self + pub fn new(cs: ChainStore) -> Self where DB: BlockStore, { - MpoolProvider { sm } + MpoolProvider { cs } } } @@ -92,17 +92,14 @@ where DB: BlockStore, { fn put_message(&self, msg: &SignedMessage) -> Result { - let cid = self - .sm - .get_cs() - .db + let cid = self.cs.db .put(msg, Blake2b256) .map_err(|err| Error::Other(err.to_string()))?; Ok(cid) } fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { - let state = StateTree::new_from_root(self.sm.get_cs().db.as_ref(), ts.parent_state()) + let state = StateTree::new_from_root(self.cs.db.as_ref(), ts.parent_state()) .map_err(|err| Error::Other(err))?; //TODO need to have this error be an Error::Other from state_manager errs let actor = state.get_actor(addr).map_err(Error::Other)?; @@ -120,19 +117,30 @@ where &self, h: &BlockHeader, ) -> Result<(Vec, Vec), Error> { - self.sm - .get_cs() + self.cs .messages(h) .map_err(|err| Error::Other(err.to_string())) } - fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { + fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { + // let mut umsg: Vec = Vec::new(); + // let mut msg: Vec = Vec::new(); + // for bh in h.blocks().iter() { + // let (mut bh_umsg_tmp, mut bh_msg_tmp) = self.messages_for_block(bh)?; + // let mut bh_umsg = bh_umsg_tmp.as_mut(); + // let mut bh_msg = bh_msg_tmp.as_mut(); + // umsg.append(bh_umsg); + // msg.append(bh_msg); + // } + // for msg in &msg { + // umsg.push(msg.message().clone()); + // } + // Ok(umsg) unimplemented!() } fn load_tipset(&self, tsk: &TipsetKeys) -> Result { - self.sm - .get_cs() + self.cs .tipset_from_keys(tsk) .map_err(|err| Error::Other(err.to_string())) } @@ -309,3 +317,20 @@ where return Ok(BigInt::from(actor.balance)); } } + +struct MessageQuery { + from: Option
, + to: Option
, + + method: Option, // equiv to message method_num + params: Option, + + value_min: Option, + value_max: Option, + gas_price_min: Option, + gas_price_max: Option, + gas_limit_min: Option, + gas_limit_max: Option +} + + diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 759f52512288..55cbcf234259 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -8,7 +8,6 @@ use actor::{init, miner, power, ActorState, INIT_ACTOR_ADDR, STORAGE_POWER_ACTOR use address::{Address, Protocol}; use blockstore::BlockStore; use blockstore::BufferedBlockStore; -use chain::ChainStore; use cid::Cid; use encoding::de::DeserializeOwned; use forest_blocks::FullTipset; @@ -21,40 +20,34 @@ use std::sync::Arc; /// Intermediary for retrieving state objects and updating actor states pub struct StateManager { - cs: ChainStore, + bs: Arc, } impl StateManager -where - DB: BlockStore, + where + DB: BlockStore, { /// constructor - pub fn new(cs: ChainStore) -> Self { - Self { cs } - } - /// get ChainStore to modify - pub fn get_cs(&self) -> &ChainStore { - &self.cs + pub fn new(bs: Arc) -> Self { + Self { bs } } /// Loads actor state from IPLD Store fn load_actor_state(&self, addr: &Address, state_cid: &Cid) -> Result - where - D: DeserializeOwned, + where + D: DeserializeOwned, { let actor = self .get_actor(addr, state_cid)? .ok_or_else(|| Error::ActorNotFound(addr.to_string()))?; let act: D = self - .cs - .blockstore() + .bs .get(&actor.state) .map_err(|e| Error::State(e.to_string()))? .ok_or_else(|| Error::ActorStateNotFound(actor.state.to_string()))?; Ok(act) } fn get_actor(&self, addr: &Address, state_cid: &Cid) -> Result, Error> { - let state = - StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; + let state = StateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?; state.get_actor(addr).map_err(Error::State) } /// Returns the network name from the init actor state @@ -70,7 +63,7 @@ where } let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?; - match ps.get_claim(self.cs.blockstore(), addr)? { + match ps.get_claim(self.bs.as_ref(), addr)? { Some(_) => Ok(false), None => Ok(true), } @@ -79,10 +72,9 @@ where pub fn get_miner_work_addr(&self, state_cid: &Cid, addr: &Address) -> Result { let ms: miner::State = self.load_actor_state(addr, state_cid)?; - let state = - StateTree::new_from_root(self.cs.blockstore(), state_cid).map_err(Error::State)?; + let state = StateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?; // Note: miner::State info likely to be changed to CID - let addr = resolve_to_key_addr(&state, self.cs.blockstore(), &ms.info.worker) + let addr = resolve_to_key_addr(&state, self.bs.as_ref(), &ms.info.worker) .map_err(|e| Error::Other(format!("Failed to resolve key address; error: {}", e)))?; Ok(addr) } @@ -90,7 +82,7 @@ where pub fn get_power(&self, state_cid: &Cid, addr: &Address) -> Result<(BigUint, BigUint), Error> { let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?; - if let Some(claim) = ps.get_claim(self.cs.blockstore(), addr)? { + if let Some(claim) = ps.get_claim(self.bs.as_ref(), addr)? { Ok((claim.power, ps.total_network_power)) } else { Err(Error::State( @@ -102,7 +94,7 @@ where /// Performs the state transition for the tipset and applies all unique messages in all blocks. /// This function returns the state root and receipt root of the transition. pub fn apply_blocks(&self, ts: &FullTipset) -> Result<(Cid, Cid), Box> { - let mut buf_store = BufferedBlockStore::new(self.cs.blockstore()); + let mut buf_store = BufferedBlockStore::new(self.bs.as_ref()); // TODO possibly switch out syscalls to be saved at state manager level let mut vm = VM::new( ts.parent_state(), @@ -115,7 +107,7 @@ where let receipts = vm.apply_tip_set_messages(ts)?; // Construct receipt root from receipts - let rect_root = Amt::new_from_slice(self.cs.blockstore(), &receipts)?; + let rect_root = Amt::new_from_slice(self.bs.as_ref(), &receipts)?; // Flush changes to blockstore let state_root = vm.flush()?; diff --git a/forest/src/cli/genesis.rs b/forest/src/cli/genesis.rs index 104a3fec7f3e..05b83bcbb5a2 100644 --- a/forest/src/cli/genesis.rs +++ b/forest/src/cli/genesis.rs @@ -42,7 +42,7 @@ where // This is just a workaround to get the network name before the sync process starts to use in // the pubsub topics, hopefully can be removed in future. - let sm = StateManager::new(ChainStore::new(chain_store.db.clone())); + let sm = StateManager::new(chain_store.db.clone()); let network_name = sm.get_network_name(genesis.state_root()).expect( "Genesis not initialized properly, failed to retrieve network name. \ Requires either a previously initialized genesis or with genesis config option set", From 2822083505bd4caeecb7c59d471d21b6bbb4bd8c Mon Sep 17 00:00:00 2001 From: flodesi Date: Thu, 28 May 2020 15:39:20 -0400 Subject: [PATCH 08/30] commenting and fmt --- blockchain/chain_sync/src/sync.rs | 14 ++-- blockchain/message_pool/Cargo.toml | 1 - blockchain/message_pool/src/msgpool.rs | 94 +++++++++++++++----------- blockchain/state_manager/src/lib.rs | 8 +-- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/blockchain/chain_sync/src/sync.rs b/blockchain/chain_sync/src/sync.rs index cc6debc795de..5c6fed05b6e2 100644 --- a/blockchain/chain_sync/src/sync.rs +++ b/blockchain/chain_sync/src/sync.rs @@ -99,9 +99,9 @@ struct MsgMetaData { } impl ChainSyncer - where - TBeacon: Beacon, - DB: BlockStore + Sync + Send + 'static, +where + TBeacon: Beacon, + DB: BlockStore + Sync + Send + 'static, { pub fn new( chain_store: ChainStore, @@ -529,8 +529,8 @@ impl ChainSyncer msg_meta_data: &mut HashMap, tree: &StateTree, ) -> Result<(), Error> - where - M: Message, + where + M: Message, { let updated_state: MsgMetaData = match msg_meta_data.get(msg.from()) { // address is present begin validity checks @@ -943,7 +943,7 @@ mod tests { event_receiver, genesis_ts, ) - .unwrap(), + .unwrap(), event_sender, ) } @@ -1017,4 +1017,4 @@ mod tests { let root = compute_msg_data(cs.chain_store.blockstore(), &[bls], &[secp]).unwrap(); assert_eq!(root, expected_root); } -} \ No newline at end of file +} diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index e50201fc942b..a27bf0d8c451 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -10,7 +10,6 @@ vm = { package = "forest_vm", path = "../../vm" } blocks = { package = "forest_blocks", path = "../blocks" } message = { package = "forest_message", path = "../../vm/message" } thiserror = "1.0" -state_manager = { path = "../state_manager" } cid = { package = "forest_cid", path = "../../ipld/cid", version = "0.1" } encoding = { package = "forest_encoding", path = "../../encoding", version = "0.1" } blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" } diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 10881ccbad47..8b0d00ed3f0e 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -14,18 +14,19 @@ use encoding::Cbor; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; use num_bigint::{BigInt, BigUint, ToBigInt, ToBigUint}; -// use serde::Serialize; -use state_manager::StateManager; use state_tree::StateTree; use std::{collections::HashMap, str::from_utf8}; -use vm::{ActorState, TokenAmount, MethodNum, Serialized}; +use vm::ActorState; +/// Simple struct that contains a hashmap of messages where k: a message from address, v: a message +/// which corresponds to that address struct MsgSet { msgs: HashMap, next_nonce: u64, } impl MsgSet { + /// Generate a new MsgSet with an empty hashmap and a default next_nonce of 0 pub fn new() -> Self { MsgSet { msgs: HashMap::new(), @@ -33,6 +34,8 @@ impl MsgSet { } } + /// Add a signed message to the MsgSet. Increase next_nonce if the message has a nonce greater + /// than any existing message nonce. pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { if self.msgs.is_empty() || m.sequence() >= self.next_nonce { self.next_nonce = m.sequence() + 1; @@ -43,13 +46,14 @@ impl MsgSet { if m.cid().map_err(|err| Error::Other(err.to_string()))? != exms.cid().map_err(|err| Error::Other(err.to_string()))? { - let mut gas_price = exms.message().gas_price(); + let gas_price = exms.message().gas_price(); let replace_by_fee_ratio: f32 = 1.25; let rbf_num = BigUint::from(((replace_by_fee_ratio - 1 as f32) * 256 as f32) as u64); let rbf_denom = BigUint::from(256 as u64); let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom; if m.message().gas_price() <= &min_price { + // message with duplicate nonce is already in mpool return Err(DuplicateNonce); } } @@ -71,6 +75,9 @@ trait Provider { fn load_tipset(&self, tsk: &TipsetKeys) -> Result; // TODO dunno how to do this } +/// This is the mpool provider struct that will let us access and add messages to messagepool. +/// future TODO is to add a pubsub field to allow for publishing updates. Future TODO is also to +/// add a subscribe_head_change function in order to actually get a functioning messagepool struct MpoolProvider { cs: ChainStore, } @@ -79,7 +86,7 @@ impl<'db, DB> MpoolProvider where DB: BlockStore, { - pub fn new(cs: ChainStore) -> Self + fn new(cs: ChainStore) -> Self where DB: BlockStore, { @@ -91,13 +98,18 @@ impl Provider for MpoolProvider where DB: BlockStore, { + /// Add a message to the MpoolProvider, return either Cid or Error depending on successful put fn put_message(&self, msg: &SignedMessage) -> Result { - let cid = self.cs.db + let cid = self + .cs + .db .put(msg, Blake2b256) .map_err(|err| Error::Other(err.to_string()))?; Ok(cid) } + /// Return state actor for given address given the tipset that the a temp StateTree will be rooted + /// at. Return ActorState or Error depending on whether or not ActorState is found fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { let state = StateTree::new_from_root(self.cs.db.as_ref(), ts.parent_state()) .map_err(|err| Error::Other(err))?; @@ -109,10 +121,12 @@ where } } + /// TODO implement this method when we can resolve to key address given a temp StateManager fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result { unimplemented!() } + /// Return the signed messages for given blockheader fn messages_for_block( &self, h: &BlockHeader, @@ -122,23 +136,25 @@ where .map_err(|err| Error::Other(err.to_string())) } + /// Return all messages for a tipset fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { - // let mut umsg: Vec = Vec::new(); - // let mut msg: Vec = Vec::new(); - // for bh in h.blocks().iter() { - // let (mut bh_umsg_tmp, mut bh_msg_tmp) = self.messages_for_block(bh)?; - // let mut bh_umsg = bh_umsg_tmp.as_mut(); - // let mut bh_msg = bh_msg_tmp.as_mut(); - // umsg.append(bh_umsg); - // msg.append(bh_msg); - // } - // for msg in &msg { - // umsg.push(msg.message().clone()); - // } - // Ok(umsg) - unimplemented!() + let mut umsg: Vec = Vec::new(); + let mut msg: Vec = Vec::new(); + for bh in h.blocks().iter() { + let (mut bh_umsg_tmp, mut bh_msg_tmp) = self.messages_for_block(bh)?; + let bh_umsg = bh_umsg_tmp.as_mut(); + let bh_msg = bh_msg_tmp.as_mut(); + umsg.append(bh_umsg); + msg.append(bh_msg); + } + for msg in &msg { + umsg.push(msg.message().clone()); + } + Ok(umsg) + // unimplemented!() } + /// Return a tipset given the tipset keys from the ChainStore fn load_tipset(&self, tsk: &TipsetKeys) -> Result { self.cs .tipset_from_keys(tsk) @@ -146,6 +162,8 @@ where } } +/// This is the main MessagePool struct TODO async safety as well as get a tipset for the cur_tipset +/// field. This can only be done when subscribe to new heads has been completed struct MessagePool { // need to inquire about closer in golang and rust equivalent local_addrs: HashMap, @@ -163,6 +181,7 @@ impl MessagePool where DB: BlockStore, { + /// Create a new MessagePool. This is not yet functioning as per the outlined TODO above pub fn new(api: MpoolProvider, network_name: String) -> Self where DB: BlockStore, @@ -184,6 +203,7 @@ where } } + /// Push a signed message to the MessagePool pub fn push(&mut self, msg: &SignedMessage) -> Result { // TODO will be used to addlocal which still needs to be implemented let msg_serial = msg @@ -194,7 +214,9 @@ where msg.cid().map_err(|err| Error::Other(err.to_string())) } - pub fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { + /// This is a helper to push that will help to make sure that the message fits the parameters + /// to be pushed to the MessagePool + fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { let size = msg .marshal_cbor() .map_err(|err| return Error::Other(err.to_string()))? @@ -216,6 +238,7 @@ where Ok(()) } + /// Return the string representation of the message signature fn sig_cache_key(&mut self, msg: &SignedMessage) -> Result { match msg.signature().signature_type() { SignatureType::Secp256 => Ok(msg.cid().unwrap().to_string()), @@ -233,6 +256,8 @@ where } } + /// Verify the message signature. first check if it has already been verified and put into + /// cache. If it has not, then manually verify it then put it into cache for future use fn verify_msg_sig(&mut self, msg: &SignedMessage) -> Result<(), Error> { let sck = self.sig_cache_key(msg)?; let is_verif = self.sig_val_cache.get(&sck); @@ -253,6 +278,8 @@ where } } + /// Verify the state_nonce and balance for the sender of the message given then call add_locked + /// to finish adding the signed_message to pending fn add_tipset(&mut self, msg: &SignedMessage, cur_ts: &Tipset) -> Result<(), Error> { let snonce = self.get_state_nonce(msg.from(), cur_ts)?; @@ -268,6 +295,9 @@ where self.add_locked(msg) } + /// Finish verifying signed message before adding it to the pending mset hashmap. If an entry + /// in the hashmap does not yet exist, create a new mset that will correspond to the from message + /// and push it to the pending hashmap fn add_locked(&mut self, msg: &SignedMessage) -> Result<(), Error> { if msg.signature().signature_type() == SignatureType::BLS { self.bls_sig_cache.put( @@ -295,6 +325,7 @@ where Ok(()) } + /// Get the state of the base_nonce for a given address in cur_ts fn get_state_nonce(&self, addr: &Address, cur_ts: &Tipset) -> Result { let actor = self.api.state_get_actor(&addr, cur_ts)?; @@ -302,7 +333,7 @@ where // TODO will need to chang e this to set cur_ts to chain.head // will implement this once we have subscribe to head change done - let msgs = self.api.messages_for_tipset(cur_ts).unwrap(); + // let msgs = self.api.messages_for_tipset(cur_ts).unwrap(); // TODO will need to call messages_for_tipset after it is implemented // and iterate over the messages, and check whether or not the from @@ -312,25 +343,10 @@ where Ok(base_nonce) } + /// Get the state balance for the actor that corresponds to the supplied address and tipset, + /// if this actor does not exist, return an error fn get_state_balance(&self, addr: &Address, ts: &Tipset) -> Result { let actor = self.api.state_get_actor(&addr, &ts)?; return Ok(BigInt::from(actor.balance)); } } - -struct MessageQuery { - from: Option
, - to: Option
, - - method: Option, // equiv to message method_num - params: Option, - - value_min: Option, - value_max: Option, - gas_price_min: Option, - gas_price_max: Option, - gas_limit_min: Option, - gas_limit_max: Option -} - - diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 7bed8e3893ff..dd17dd2c3321 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -24,8 +24,8 @@ pub struct StateManager { } impl StateManager - where - DB: BlockStore, +where + DB: BlockStore, { /// constructor pub fn new(bs: Arc) -> Self { @@ -33,8 +33,8 @@ impl StateManager } /// Loads actor state from IPLD Store fn load_actor_state(&self, addr: &Address, state_cid: &Cid) -> Result - where - D: DeserializeOwned, + where + D: DeserializeOwned, { let actor = self .get_actor(addr, state_cid)? From c01e669ffc7f6b21044d5f4fafdbd9c6ac0aca16 Mon Sep 17 00:00:00 2001 From: flodesi Date: Sat, 30 May 2020 20:20:42 -0400 Subject: [PATCH 09/30] added some unimplemented functions --- blockchain/message_pool/src/msgpool.rs | 177 ++++++++++++++++++++++--- vm/message/src/signed_message.rs | 3 + 2 files changed, 163 insertions(+), 17 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 8b0d00ed3f0e..1027d385d98a 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -20,6 +20,7 @@ use vm::ActorState; /// Simple struct that contains a hashmap of messages where k: a message from address, v: a message /// which corresponds to that address +#[derive(Clone)] struct MsgSet { msgs: HashMap, next_nonce: u64, @@ -49,7 +50,7 @@ impl MsgSet { let gas_price = exms.message().gas_price(); let replace_by_fee_ratio: f32 = 1.25; let rbf_num = - BigUint::from(((replace_by_fee_ratio - 1 as f32) * 256 as f32) as u64); + BigUint::from(((replace_by_fee_ratio - 1_f32) * 256_f32) as u64); let rbf_denom = BigUint::from(256 as u64); let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom; if m.message().gas_price() <= &min_price { @@ -112,7 +113,7 @@ where /// at. Return ActorState or Error depending on whether or not ActorState is found fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { let state = StateTree::new_from_root(self.cs.db.as_ref(), ts.parent_state()) - .map_err(|err| Error::Other(err))?; + .map_err(Error::Other)?; //TODO need to have this error be an Error::Other from state_manager errs let actor = state.get_actor(addr).map_err(Error::Other)?; match actor { @@ -175,6 +176,8 @@ struct MessagePool { network_name: String, bls_sig_cache: LruCache, sig_val_cache: LruCache, + // this will be a hashmap where the key is msg.cid.bytes.to_string and the value is a byte vec + local_msgs: HashMap> } impl MessagePool @@ -190,6 +193,10 @@ where // LruCache sizes have been taken from the lotus implementation let bls_sig_cache = LruCache::new(40000); let sig_val_cache = LruCache::new(32000); + // TODO take in the local_msgs hashmap as a param and just apply a standard key + // prefix to it + let local_msgs = HashMap::new(); + MessagePool { local_addrs: HashMap::new(), pending: HashMap::new(), @@ -200,6 +207,7 @@ where network_name, bls_sig_cache, sig_val_cache, + local_msgs, } } @@ -208,7 +216,7 @@ where // TODO will be used to addlocal which still needs to be implemented let msg_serial = msg .marshal_cbor() - .map_err(|err| return Error::Other(err.to_string()))?; + .map_err(|err| Error::Other(err.to_string()))?; self.add(msg)?; // TODO do pubsub publish with mp.netName and msg_serial msg.cid().map_err(|err| Error::Other(err.to_string())) @@ -219,7 +227,7 @@ where fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { let size = msg .marshal_cbor() - .map_err(|err| return Error::Other(err.to_string()))? + .map_err(|err| Error::Other(err.to_string()))? .len(); if size > 32 * 1024 { return Err(Error::MessageTooBig); @@ -262,7 +270,7 @@ where let sck = self.sig_cache_key(msg)?; let is_verif = self.sig_val_cache.get(&sck); match is_verif { - Some(()) => return Ok(()), + Some(()) => Ok(()), None => { let verif = msg .signature() @@ -325,28 +333,163 @@ where Ok(()) } + /// TODO uncomment the code for this function when subscribe new head changes has been implemented + fn get_nonce(&self, addr: &Address) -> Result { + // self.get_nonce_locked(addr, self.cur_tipset) + unimplemented!() + } + + fn get_nonce_locked(&self, addr: &Address, cur_ts: &Tipset) -> Result { + let state_nonce = self.get_state_nonce(addr, cur_ts)?; + + let mset = self.pending.get(addr).unwrap(); + if state_nonce > mset.next_nonce { + // state nonce is larger than mset.next_nonce + return Ok(state_nonce); + } + Ok(mset.next_nonce) + } + /// Get the state of the base_nonce for a given address in cur_ts fn get_state_nonce(&self, addr: &Address, cur_ts: &Tipset) -> Result { let actor = self.api.state_get_actor(&addr, cur_ts)?; - let base_nonce = actor.sequence; - - // TODO will need to chang e this to set cur_ts to chain.head - // will implement this once we have subscribe to head change done - // let msgs = self.api.messages_for_tipset(cur_ts).unwrap(); - - // TODO will need to call messages_for_tipset after it is implemented - // and iterate over the messages, and check whether or not the from - // addr from each message equals addr, if it is not throw error, otherwise - // increase base_nonce by 1 and then after loop termpinates return base_nonce + let mut base_nonce = actor.sequence; + // TODO here lotus has a todo, so I guess we should eventually remove cur_ts from one + // of the params for this method and just use the chain head + let msgs = self.api.messages_for_tipset(cur_ts)?; + for m in msgs { + if m.from() == addr { + return Err(Error::Other("thipset has bad nonce ordering".to_string())) + } + base_nonce += 1; + } Ok(base_nonce) } /// Get the state balance for the actor that corresponds to the supplied address and tipset, /// if this actor does not exist, return an error - fn get_state_balance(&self, addr: &Address, ts: &Tipset) -> Result { + fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { let actor = self.api.state_get_actor(&addr, &ts)?; - return Ok(BigInt::from(actor.balance)); + Ok(BigInt::from(actor.balance)) + } + + /// TODO this will need to be completed when state_account_key is implemented + fn push_with_nonce(&self) { unimplemented!()} + + /// TODO need to add publish to the end of this once a way to publish has been figured out + fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { + let mset = self.pending.get_mut(from).unwrap(); + // TODO will use this to publish the removal of this message once implemented + // let m = mset.msgs.get(&sequence).unwrap(); + mset.msgs.remove(&sequence); + + if mset.msgs.len() == 0 { + self.pending.remove(from); + } else { + let mut max_sequence: u64 = 0; + for sequence_temp in mset.msgs.keys().cloned() { + if max_sequence < sequence_temp { + max_sequence = sequence_temp; + } + } + if max_sequence < sequence { + max_sequence = sequence; + } + mset.next_nonce = max_sequence + 1; + } + Ok(()) + } + + /// TODO need to see if after this function is run, clear out the pending field in self + fn pending(&self) -> Result, Tipset> { + let mut out: Vec = Vec::new(); + for (addr, _) in self.pending.clone() { + out.append(self.pending_for(&addr).unwrap().as_mut()) + } + Ok(out) + } + + fn pending_for(&self, a: &Address) -> Option> { + let mset = self.pending.get(a); + match mset { + Some(msgset) => { + if msgset.msgs.is_empty() { + return None; + } + + let mut msg_vec = Vec::new(); + + for (_, item) in msgset.msgs.clone() { + msg_vec.push(item); + } + + msg_vec.sort_by_key(|value| value.message().sequence().clone()); + + Some(msg_vec) + } + None => None + } + } + + fn messages_for_blocks(&mut self, blks: Vec) -> Result, Error> { + let mut msg_vec: Vec = Vec::new(); + for block in blks { + let (umsg, mut smsgs) = self.api.messages_for_block(&block)?; + msg_vec.append(smsgs.as_mut()); + for msg in umsg { + let smsg = self.recover_sig(msg)?; + msg_vec.push(smsg) + } + } + Ok(msg_vec) + } + + fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { + let val = self.bls_sig_cache.get(&msg.cid().map_err(|err| Error::Other(err.to_string()))?).unwrap(); + Ok(SignedMessage::new_from_fields(msg, val.clone())) + } + + fn add_local(&mut self) -> Result<(), Error> { + let mut msg_vec = Vec::new(); + for (_, bvec) in self.local_msgs.iter_mut() { + // TODO convert these errors into one larger error so that this loop doesnt terminate after finding + // one error + msg_vec.push(SignedMessage::unmarshal_cbor(&bvec).map_err(|err| Error::Other(err.to_string()))?); + } + for sm in msg_vec { + self.add(&sm).map_err(|err| Error::Other(err.to_string()))?; + // TODO if error is encountered, remove this message from cache + } + Ok(()) + } + + pub fn estimate_gas_price(&self, nblocksincl: u64) -> Result { + // TODO: something different, this is what lotus has and there is a TODO there too + let min_gas_price = 0; + match nblocksincl { + 0 => Ok(BigInt::from(min_gas_price + 2)), + 1 => Ok(BigInt::from(min_gas_price + 1)), + _ => Ok(BigInt::from(min_gas_price)) + } + } + + pub fn load_local(&mut self) -> Result<(), Error> { + for (key, value) in self.local_msgs.clone() { + let value = SignedMessage::unmarshal_cbor(&value).map_err(|err| Error::Other(err.to_string()))?; + self.add(&value).map_err(|err| { + if err == Error::NonceTooLow { + self.local_msgs.remove(&key); + } + }); + + } + Ok(()) } } + +struct StatBucket { + msgs: HashMap +} + diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index c368790090f1..01e1fe2ad05f 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -25,6 +25,9 @@ impl SignedMessage { Ok(SignedMessage { message, signature }) } + pub fn new_from_fields(message: UnsignedMessage, signature: Signature) -> Self { + SignedMessage { message, signature } + } /// Returns reference to the unsigned message. pub fn message(&self) -> &UnsignedMessage { &self.message From d738418a3465e0bfb939622eb04db32f21ebdbf8 Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 1 Jun 2020 00:16:30 -0400 Subject: [PATCH 10/30] fmt, clippy and documentation --- blockchain/message_pool/src/msgpool.rs | 82 ++++++++++++++------------ 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 1027d385d98a..0523eb8d11b5 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -18,6 +18,9 @@ use state_tree::StateTree; use std::{collections::HashMap, str::from_utf8}; use vm::ActorState; +/// TODO after subscribe head changes is done, re-go over logic to make sure that all functions are +/// connected where needed as well as implement all uses of subscribe head changes + /// Simple struct that contains a hashmap of messages where k: a message from address, v: a message /// which corresponds to that address #[derive(Clone)] @@ -49,8 +52,7 @@ impl MsgSet { { let gas_price = exms.message().gas_price(); let replace_by_fee_ratio: f32 = 1.25; - let rbf_num = - BigUint::from(((replace_by_fee_ratio - 1_f32) * 256_f32) as u64); + let rbf_num = BigUint::from(((replace_by_fee_ratio - 1_f32) * 256_f32) as u64); let rbf_denom = BigUint::from(256 as u64); let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom; if m.message().gas_price() <= &min_price { @@ -177,7 +179,7 @@ struct MessagePool { bls_sig_cache: LruCache, sig_val_cache: LruCache, // this will be a hashmap where the key is msg.cid.bytes.to_string and the value is a byte vec - local_msgs: HashMap> + local_msgs: HashMap>, } impl MessagePool @@ -185,7 +187,7 @@ where DB: BlockStore, { /// Create a new MessagePool. This is not yet functioning as per the outlined TODO above - pub fn new(api: MpoolProvider, network_name: String) -> Self + pub fn new(api: MpoolProvider, network_name: String) -> Result, Error> where DB: BlockStore, { @@ -197,7 +199,7 @@ where // prefix to it let local_msgs = HashMap::new(); - MessagePool { + let mut mp = MessagePool { local_addrs: HashMap::new(), pending: HashMap::new(), cur_tipset: "tmp".to_string(), // cannnot do this yet, need pubsub done @@ -208,7 +210,9 @@ where bls_sig_cache, sig_val_cache, local_msgs, - } + }; + mp.load_local()?; + Ok(mp) } /// Push a signed message to the MessagePool @@ -361,7 +365,7 @@ where let msgs = self.api.messages_for_tipset(cur_ts)?; for m in msgs { if m.from() == addr { - return Err(Error::Other("thipset has bad nonce ordering".to_string())) + return Err(Error::Other("thipset has bad nonce ordering".to_string())); } base_nonce += 1; } @@ -376,7 +380,9 @@ where } /// TODO this will need to be completed when state_account_key is implemented - fn push_with_nonce(&self) { unimplemented!()} + fn push_with_nonce(&self) { + unimplemented!() + } /// TODO need to add publish to the end of this once a way to publish has been figured out fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { @@ -385,7 +391,7 @@ where // let m = mset.msgs.get(&sequence).unwrap(); mset.msgs.remove(&sequence); - if mset.msgs.len() == 0 { + if mset.msgs.is_empty() { self.pending.remove(from); } else { let mut max_sequence: u64 = 0; @@ -402,15 +408,20 @@ where Ok(()) } + /// Return a tuple that contains a vector of all signed messages and the current tipset for + /// self. + /// TODO when subscribe head changes is completed, change the return type parameters and refactor /// TODO need to see if after this function is run, clear out the pending field in self - fn pending(&self) -> Result, Tipset> { + fn pending(&self) -> (Vec, String) { let mut out: Vec = Vec::new(); for (addr, _) in self.pending.clone() { out.append(self.pending_for(&addr).unwrap().as_mut()) } - Ok(out) + (out, self.cur_tipset.clone()) } + /// Return a Vector of signed messages for a given from address. This vector will be sorted by + /// each messsage's nonce (sequence). If no corresponding messages found, return None result type fn pending_for(&self, a: &Address) -> Option> { let mset = self.pending.get(a); match mset { @@ -425,14 +436,15 @@ where msg_vec.push(item); } - msg_vec.sort_by_key(|value| value.message().sequence().clone()); + msg_vec.sort_by_key(|value| value.message().sequence()); Some(msg_vec) } - None => None + None => None, } } + /// Return Vector of signed messages given a block header for self fn messages_for_blocks(&mut self, blks: Vec) -> Result, Error> { let mut msg_vec: Vec = Vec::new(); for block in blks { @@ -446,50 +458,46 @@ where Ok(msg_vec) } + /// Attempt to get the signed message given an unsigned message in message pool fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { - let val = self.bls_sig_cache.get(&msg.cid().map_err(|err| Error::Other(err.to_string()))?).unwrap(); + let val = self + .bls_sig_cache + .get(&msg.cid().map_err(|err| Error::Other(err.to_string()))?) + .unwrap(); Ok(SignedMessage::new_from_fields(msg, val.clone())) } - fn add_local(&mut self) -> Result<(), Error> { - let mut msg_vec = Vec::new(); - for (_, bvec) in self.local_msgs.iter_mut() { - // TODO convert these errors into one larger error so that this loop doesnt terminate after finding - // one error - msg_vec.push(SignedMessage::unmarshal_cbor(&bvec).map_err(|err| Error::Other(err.to_string()))?); - } - for sm in msg_vec { - self.add(&sm).map_err(|err| Error::Other(err.to_string()))?; - // TODO if error is encountered, remove this message from cache - } - Ok(()) - } - + /// Return gas price estimate this has been translated from lotus, a more smart implementation will + /// most likely need to be implemented pub fn estimate_gas_price(&self, nblocksincl: u64) -> Result { // TODO: something different, this is what lotus has and there is a TODO there too let min_gas_price = 0; match nblocksincl { 0 => Ok(BigInt::from(min_gas_price + 2)), 1 => Ok(BigInt::from(min_gas_price + 1)), - _ => Ok(BigInt::from(min_gas_price)) + _ => Ok(BigInt::from(min_gas_price)), } } + /// Load local messages into pending. As of right now messages are not deleted from self's + /// local_message field, possibly implement this in the future? pub fn load_local(&mut self) -> Result<(), Error> { for (key, value) in self.local_msgs.clone() { - let value = SignedMessage::unmarshal_cbor(&value).map_err(|err| Error::Other(err.to_string()))?; - self.add(&value).map_err(|err| { - if err == Error::NonceTooLow { - self.local_msgs.remove(&key); + let value = SignedMessage::unmarshal_cbor(&value) + .map_err(|err| Error::Other(err.to_string()))?; + match self.add(&value) { + Ok(()) => (), + Err(err) => { + if err == Error::NonceTooLow { + self.local_msgs.remove(&key); + } } - }); - + } } Ok(()) } } struct StatBucket { - msgs: HashMap + msgs: HashMap, } - From 48055ccff9f05325f86e8dfbea77d19bd4eb2082 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 16 Jun 2020 12:12:36 -0400 Subject: [PATCH 11/30] fixed merge conflict --- blockchain/state_manager/Cargo.toml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 blockchain/state_manager/Cargo.toml diff --git a/blockchain/state_manager/Cargo.toml b/blockchain/state_manager/Cargo.toml new file mode 100644 index 000000000000..8baa04876f3a --- /dev/null +++ b/blockchain/state_manager/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "state_manager" +version = "0.1.0" +authors = ["ChainSafe Systems "] +edition = "2018" + +[dependencies] +address = { package = "forest_address", path = "../../vm/address/" } +actor = { path = "../../vm/actor/" } +cid = { package = "forest_cid", path = "../../ipld/cid" } +db = { path = "../../node/db/" } +encoding = { package = "forest_encoding", path = "../../encoding/" } +num-bigint = { path = "../../utils/bigint", package = "forest_bigint" } +state_tree = { path = "../../vm/state_tree/" } +blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" } +forest_blocks = { path = "../../blockchain/blocks" } +thiserror = "1.0" +interpreter = { path = "../../vm/interpreter/" } +ipld_amt = { path = "../../ipld/amt/" } +clock = { path = "../../node/clock" } +chain = { path = "../chain" } +async-std = "1.5.0" +async-log = "2.0.0" +log = "0.4.8" +filecoin-proofs-api = { git = "https://github.com/filecoin-project/rust-filecoin-proofs-api", rev = "0e1c7cb464707c7a7ad82b0f8303f000f6f3fc8a" } +fil_types = { path = "../../types" } \ No newline at end of file From 7d693a88301d3a25520d927b3265e2dd1ae31b60 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 16 Jun 2020 14:54:12 -0400 Subject: [PATCH 12/30] first test --- blockchain/message_pool/src/msgpool.rs | 123 +++++-------------------- 1 file changed, 21 insertions(+), 102 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index daf2d94dba29..b5ce04172972 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -198,7 +198,7 @@ pub struct MessagePool { bls_sig_cache: LruCache, sig_val_cache: LruCache, // this will be a hashmap where the key is msg.cid.bytes.to_string and the value is a byte vec - local_msgs: HashMap>, + local_msgs: HashMap, SignedMessage>, } impl MessagePool @@ -216,7 +216,7 @@ where let sig_val_cache = LruCache::new(32000); // TODO take in the local_msgs hashmap as a param and just apply a standard key // prefix to it - let local_msgs = HashMap::new(); + // let local_msgs = HashMap::new(); let mut mp = MessagePool { local_addrs: Vec::new(), @@ -228,7 +228,7 @@ where network_name, bls_sig_cache, sig_val_cache, - local_msgs, + local_msgs: HashMap::new(), }; mp.load_local()?; Ok(mp) @@ -236,7 +236,7 @@ where fn add_local(&mut self, m: &SignedMessage, msgb: Vec) -> Result<(), Error> { self.local_addrs.push(m.from().clone()); - self.local_msgs.insert(from_utf8(msgb.as_slice()).map_err(|err| Error::Other(err.to_string()))?.to_string(), msgb); + self.local_msgs.insert(msgb, m.clone()); Ok(()) } @@ -269,7 +269,7 @@ where return Err(Error::MessageValueTooHigh); } - // self.verify_msg_sig(msg)?; + self.verify_msg_sig(msg)?; let tmp = msg.clone(); self.add_tipset(tmp, &self.cur_tipset.clone()) @@ -283,7 +283,7 @@ where /// Return the string representation of the message signature pub(crate) fn sig_cache_key(&mut self, msg: &SignedMessage) -> Result { match msg.signature().signature_type() { - SignatureType::Secp256 => Ok(msg.cid().unwrap().to_string()), + SignatureType::Secp256k1 => Ok(msg.cid().unwrap().to_string()), SignatureType::BLS => { if msg.signature().bytes().len() < 90 { return Err(Error::BLSSigTooShort); @@ -306,9 +306,10 @@ where match is_verif { Some(()) => Ok(()), None => { + let umsg = Cbor::marshal_cbor(msg.message()).map_err(|err| Error::Other(err.to_string()))?; let verif = msg .signature() - .verify(&msg.message().cid().unwrap().to_bytes(), msg.from()); + .verify(umsg.as_slice(), msg.from()); match verif { Ok(()) => { self.sig_val_cache.put(sck, ()); @@ -331,7 +332,7 @@ where let balance = self.get_state_balance(msg.from(), cur_ts)?; let msg_balance = BigInt::from(msg.message().required_funds()); - if balance > msg_balance { + if balance < msg_balance { return Err(Error::NotEnoughFunds); } self.add_locked(msg) @@ -367,7 +368,6 @@ where Ok(()) } - /// TODO uncomment the code for this function when subscribe new head changes has been implemented pub fn get_nonce(&self, addr: &Address) -> Result { self.get_nonce_locked(addr, &self.cur_tipset) } @@ -377,7 +377,7 @@ where match self.pending.get(addr) { Some(mset) => { - if state_nonce < mset.next_nonce { + if state_nonce > mset.next_nonce { return Ok(state_nonce) } Ok(mset.next_nonce) @@ -515,8 +515,8 @@ where /// local_message field, possibly implement this in the future? pub fn load_local(&mut self) -> Result<(), Error> { for (key, value) in self.local_msgs.clone() { - let value = SignedMessage::unmarshal_cbor(&value) - .map_err(|err| Error::Other(err.to_string()))?; + // let value = SignedMessage::unmarshal_cbor(msg.as_slice()) + // .map_err(|err| Error::Other(err.to_string()))?; match self.add(&value) { Ok(()) => (), Err(err) => { @@ -535,21 +535,15 @@ where mod tests { use super::*; use address::Address; - use crypto::{Signer}; use blocks::{BlockHeader, Tipset}; use cid::Cid; use crypto::SignatureType; use message::{SignedMessage, UnsignedMessage}; use num_bigint::BigUint; - use key_management::{MemKeyStore, Wallet, KeyStore}; + use key_management::{MemKeyStore, Wallet}; use crate::MessagePool; use std::borrow::BorrowMut; - use std::error::Error as Error; use super::Error as Errors; - use std::convert::{TryFrom, TryInto}; - use secp256k1::{Message as SecpMessage, PublicKey as SecpPublic, SecretKey as SecpPrivate}; - use encoding; - use blake2b_simd::Params; struct TestApi { bmsgs: HashMap>, @@ -624,15 +618,6 @@ mod tests { } } - const DUMMY_SIG: [u8; 65] = [0u8; 65]; - - struct DummySigner; - impl Signer for DummySigner { - fn sign_bytes(&self, _: Vec, _: &Address) -> Result> { - Ok(Signature::new_secp256k1(DUMMY_SIG.to_vec())) - } - } - fn create_header(weight: u64, parent_bz: &[u8], cached_bytes: &[u8]) -> BlockHeader { let header = BlockHeader::builder() .weight(BigUint::from(weight)) @@ -643,24 +628,10 @@ mod tests { .unwrap(); header } - - fn get_digest(message: &[u8]) -> [u8; 32] { - let message_hashed = Params::new().hash_length(32).to_state().update(message).finalize(); - let cid_hashed = Params::new().hash_length(32).to_state().update(&[0x01, 0x71, 0xa0, 0xe4, 0x02, 0x20]).update(message_hashed.as_bytes()).finalize(); - cid_hashed.as_bytes().try_into().unwrap() - } - fn create_smsg(to: &Address, from: &Address, wallet: &mut Wallet) -> SignedMessage { let umsg: UnsignedMessage = UnsignedMessage::builder().to(to.clone()).from(from.clone()).build().unwrap(); let message_cbor = Cbor::marshal_cbor(&umsg).unwrap(); - let cid_hashed = get_digest(message_cbor.as_ref()); - // let message_digest = SecpMessage::parse_slice(&cid_hashed).unwrap(); - let mut sig = wallet.sign(&from, &cid_hashed).unwrap(); - let bytes = sig.bytes(); - // let slice = msg.as_slice(); - // print!("{}", slice.len()); // prints 53, 32 is required for wallet sign - // let signer = wallet.sign(from, slice).unwrap(); - + let sig = wallet.sign(&from, message_cbor.as_slice()).unwrap(); SignedMessage::new_from_fields(umsg, sig) } @@ -668,8 +639,8 @@ mod tests { fn test_message_pool() { let keystore = MemKeyStore::new(); let mut wallet = Wallet::new(keystore); - let sender = wallet.generate_key(SignatureType::Secp256).unwrap(); - let target = wallet.generate_key(SignatureType::Secp256).unwrap(); + let sender = wallet.generate_addr(SignatureType::Secp256k1).unwrap(); + let target = wallet.generate_addr(SignatureType::Secp256k1).unwrap(); let mut tma = TestApi::new(); tma.set_state_nonce(&sender, 0); @@ -679,66 +650,14 @@ mod tests { let mut smsg_vec = Vec::new(); for _ in 1..4 { - let msg = create_smsg(&sender, &target, wallet.borrow_mut()); + let msg = create_smsg(&target, &sender, wallet.borrow_mut()); smsg_vec.push(msg); } - let nonce = mempool.get_nonce(&sender).unwrap(); + let mut nonce = mempool.get_nonce(&sender).unwrap(); assert_eq!(nonce, 0); - // right now error is being thrown when trying to add to mempool, logic should be right though - - // let thing = mempool.verify_msg_sig(&smsg_vec[0]).unwrap(); - // let sck = mempool.sig_cache_key(&smsg_vec[0]).unwrap(); - // let thing2 = mempool.sig_val_cache.get(&sck); - - // let test = mempool.add(&smsg_vec[0]).unwrap(); - // let temp = smsg_vec[0].signature().bytes().len(); - // println!("{}", temp); - // let temp = &smsg_vec[0].message().cid().unwrap(); - let verif = smsg_vec[0] - .signature() - .verify(&smsg_vec[0].message().cid().unwrap().to_bytes(), &smsg_vec[0].from()).unwrap(); - // mempool.push(&smsg_vec[0]).unwrap(); - - - - - // let api = MpoolProvider::new(cs); - // api.put_message(&smsg_vec[0]).unwrap(); - // assert!(api.messages_for_tipset(&cur_tipset).is_ok()); - // let mut mpool = MessagePool::new(api, "mptest".to_string()).unwrap(); - // assert!(mpool.api.messages_for_tipset(&mpool.cur_tipset).is_ok()); - - // mpool.sig_cache_key(&smsg_vec[0]).unwrap(); - - // mpool.add(&smsg_vec[0]).unwrap(); - // TODO need to set the state nonce of the address - - // assert!(mpool.get_state_nonce(&sender, &mpool.cur_tipset).is_ok()); - // assert!(mpool.pending.get(&sender).is_none()); - // assert!(mpool.api.state_get_actor(&sender, &mpool.cur_tipset).is_ok()); - // assert!(mpool.api.messages_for_tipset(&mpool.cur_tipset).is_ok()); - - // let test_api = MpoolProvider::new(ChainStore::new(Arc::new(db::MemoryDB::default()))); - // let nonce = mpool.get_nonce(&sender).unwrap(); - // println!("{}", nonce); - - // check to make sure that the current tipset has been set up correctly in messagepool - // assert_eq!(mpool.cur_tipset, cur_tipset); - - - // need to create a wallet and get a sender address - - // let a: BlockHeader = BlockHeader::builder().weight(1).build().unwrap(); // first mock block - // let tsk = a. - // let b = BlockHeader::builder().parents(); // second mock block - // - // let target = Address::new_id(1001); - // - // let mut msgs = Vec::new(); - // for i in 0..5 { - // let unsigned_temp = UnsignedMessage::builder().to(sender).from(target).sequence(i); - // msgs.push(mock_msg(sender.clone(), target.clone(), i, w)) - // } + mempool.push(&smsg_vec[0]).unwrap(); + nonce = mempool.get_nonce(&sender).unwrap(); + assert_eq!(nonce, 1); } } From 202f86b3e4b2294a35fd8966e3c207e7759ddcaf Mon Sep 17 00:00:00 2001 From: flodesi Date: Thu, 18 Jun 2020 18:09:57 -0400 Subject: [PATCH 13/30] finished tests --- blockchain/message_pool/src/msgpool.rs | 353 ++++++++++++++++++++----- 1 file changed, 285 insertions(+), 68 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index b5ce04172972..b542a1e6a205 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -4,6 +4,7 @@ use super::errors::Error; use crate::errors::Error::DuplicateNonce; use address::Address; +use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; use chain::ChainStore; @@ -11,14 +12,14 @@ use cid::multihash::Blake2b256; use cid::Cid; use crypto::{Signature, SignatureType}; use encoding::Cbor; +use futures::stream::StreamExt; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; use num_bigint::{BigInt, BigUint, ToBigInt, ToBigUint}; use state_tree::StateTree; -use std::{collections::HashMap, str::from_utf8}; +use std::borrow::BorrowMut; +use std::collections::HashMap; use vm::ActorState; -use async_std::task; -use futures::stream::StreamExt; /// TODO after subscribe head changes is done, re-go over logic to make sure that all functions are /// connected where needed as well as implement all uses of subscribe head changes @@ -43,6 +44,8 @@ impl MsgSet { /// Add a signed message to the MsgSet. Increase next_nonce if the message has a nonce greater /// than any existing message nonce. pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { + println!("this is the sequence {}", m.sequence()); + println!("this is the next nonce {}", self.next_nonce); if self.msgs.is_empty() || m.sequence() >= self.next_nonce { self.next_nonce = m.sequence() + 1; } @@ -104,8 +107,8 @@ impl Provider for MpoolProvider where DB: BlockStore, { - // TODO don't know if this needs to be done, but will need to add a call to subscribe to - // return a new current tipset when the head changes + // TODO find a way to get this function to periodically check for head changes in cs and update + // msgpool accordingly fn subscribe_head_changes(&mut self) -> Tipset { if self.cs.heaviest_tipset().is_none() { task::block_on(async { @@ -113,7 +116,12 @@ where loop { let sub = subscriber.next().await; if sub.is_some() { - if self.cs.set_heaviest_tipset(sub.unwrap().clone()).await.is_ok() { + if self + .cs + .set_heaviest_tipset(sub.unwrap().clone()) + .await + .is_ok() + { break; } } @@ -145,11 +153,6 @@ where } } - /// TODO implement this method when we can resolve to key address given a temp StateManager - // fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result { - // unimplemented!() - // } - /// Return the signed messages for given blockheader fn messages_for_block( &self, @@ -173,7 +176,6 @@ where umsg.push(msg.message().clone()); } Ok(umsg) - // unimplemented!() } /// Return a tipset given the tipset keys from the ChainStore @@ -190,8 +192,8 @@ pub struct MessagePool { // need to inquire about closer in golang and rust equivalent local_addrs: Vec
, pending: HashMap, - pub(crate) cur_tipset: Tipset, // need to wait until pubsub is done - api: T, // will need to replace with provider type + pub(crate) cur_tipset: Tipset, // need to wait until pubsub is done + api: T, // will need to replace with provider type pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, @@ -214,10 +216,6 @@ where // LruCache sizes have been taken from the lotus implementation let bls_sig_cache = LruCache::new(40000); let sig_val_cache = LruCache::new(32000); - // TODO take in the local_msgs hashmap as a param and just apply a standard key - // prefix to it - // let local_msgs = HashMap::new(); - let mut mp = MessagePool { local_addrs: Vec::new(), pending: HashMap::new(), @@ -242,13 +240,11 @@ where /// Push a signed message to the MessagePool pub fn push(&mut self, msg: &SignedMessage) -> Result { - // TODO will be used to addlocal which still needs to be implemented let msg_serial = msg .marshal_cbor() .map_err(|err| Error::Other(err.to_string()))?; self.add(msg)?; self.add_local(msg, msg_serial)?; - // TODO do pubsub publish with mp.netName and msg_serial msg.cid().map_err(|err| Error::Other(err.to_string())) } @@ -288,11 +284,20 @@ where if msg.signature().bytes().len() < 90 { return Err(Error::BLSSigTooShort); } - let slice = from_utf8(&msg.signature().bytes()[64..]).unwrap(); - let mut beginning = from_utf8(&msg.cid().unwrap().to_bytes()) + let mut beginning = String::new(); + beginning += &msg + .cid() .unwrap() - .to_string(); - beginning.push_str(slice); + .to_bytes() + .into_iter() + .map(|b| char::from(b)) + .collect::(); + beginning += &msg + .signature() + .bytes() + .into_iter() + .map(|b| char::from(b.clone())) + .collect::(); Ok(beginning) } } @@ -306,10 +311,9 @@ where match is_verif { Some(()) => Ok(()), None => { - let umsg = Cbor::marshal_cbor(msg.message()).map_err(|err| Error::Other(err.to_string()))?; - let verif = msg - .signature() - .verify(umsg.as_slice(), msg.from()); + let umsg = Cbor::marshal_cbor(msg.message()) + .map_err(|err| Error::Other(err.to_string()))?; + let verif = msg.signature().verify(umsg.as_slice(), msg.from()); match verif { Ok(()) => { self.sig_val_cache.put(sck, ()); @@ -357,14 +361,16 @@ where let msett = self.pending.get_mut(msg.message().from()); match msett { - Some(mset) => mset.add(&msg).map_err(|err| Error::Other(err.to_string()))?, + Some(mset) => mset + .add(&msg) + .map_err(|err| Error::Other(err.to_string()))?, None => { let mut mset = MsgSet::new(); - mset.add(&msg).map_err(|err| Error::Other(err.to_string()))?; + mset.add(&msg) + .map_err(|err| Error::Other(err.to_string()))?; self.pending.insert(msg.message().from().clone(), mset); } } - // TODO pubsub msg Ok(()) } @@ -378,11 +384,11 @@ where match self.pending.get(addr) { Some(mset) => { if state_nonce > mset.next_nonce { - return Ok(state_nonce) + return Ok(state_nonce); } Ok(mset.next_nonce) - }, - None => Ok(state_nonce) + } + None => Ok(state_nonce), } } @@ -396,10 +402,18 @@ where // of the params for this method and just use the chain head let msgs = self.api.messages_for_tipset(cur_ts)?; for m in msgs { + println!("this the base_nonce: {}", base_nonce); if m.from() == addr { - return Err(Error::Other("thipset has bad nonce ordering".to_string())); + if m.sequence() != base_nonce { + println!( + "this is the nonce: {} and this is the base_nonce: {}", + m.sequence(), + base_nonce + ); + return Err(Error::Other("tipset has bad nonce ordering".to_string())); + } + base_nonce += 1; } - base_nonce += 1; } Ok(base_nonce) } @@ -411,16 +425,8 @@ where Ok(BigInt::from(actor.balance)) } - /// TODO this will need to be completed when state_account_key is implemented - // fn push_with_nonce(&self) { - // unimplemented!() - // } - - /// TODO need to add publish to the end of this once a way to publish has been figured out pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { let mset = self.pending.get_mut(from).unwrap(); - // TODO will use this to publish the removal of this message once implemented - // let m = mset.msgs.get(&sequence).unwrap(); mset.msgs.remove(&sequence); if mset.msgs.is_empty() { @@ -477,7 +483,10 @@ where } /// Return Vector of signed messages given a block header for self - pub fn messages_for_blocks(&mut self, blks: Vec) -> Result, Error> { + pub fn messages_for_blocks( + &mut self, + blks: Vec, + ) -> Result, Error> { let mut msg_vec: Vec = Vec::new(); for block in blks { let (umsg, mut smsgs) = self.api.messages_for_block(&block)?; @@ -515,8 +524,6 @@ where /// local_message field, possibly implement this in the future? pub fn load_local(&mut self) -> Result<(), Error> { for (key, value) in self.local_msgs.clone() { - // let value = SignedMessage::unmarshal_cbor(msg.as_slice()) - // .map_err(|err| Error::Other(err.to_string()))?; match self.add(&value) { Ok(()) => (), Err(err) => { @@ -529,26 +536,93 @@ where Ok(()) } + fn rm( + &mut self, + from: &Address, + nonce: u64, + rmsgs: &mut HashMap>, + ) { + let s = rmsgs.get_mut(from); + if s.is_none() { + self.remove(from, nonce).map_err(|_| ()); + return; + } + let temp = s.unwrap(); + if temp.get_mut(&nonce).is_some() { + temp.remove(&nonce); + return; + } + self.remove(from, nonce); + } + + /// this function will revert and/or apply tipsets to the message pool. This function should be + /// called every time that there is a head change in the message pool + pub fn head_change(&mut self, revert: Vec, apply: Vec) -> Result<(), Error> { + let mut rmsgs: HashMap> = HashMap::new(); + + for ts in revert { + let pts = self.api.load_tipset(ts.parents())?; + let msgs = self.messages_for_blocks(ts.blocks().to_vec())?; + self.cur_tipset = pts; + for msg in msgs { + add(msg, rmsgs.borrow_mut()); + } + } + + for ts in apply { + for b in ts.blocks() { + let (msgs, smsgs) = self.api.messages_for_block(b).unwrap(); + for msg in smsgs { + self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); + } + + for msg in msgs { + self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); + } + } + self.cur_tipset = ts; + } + + for (_, hm) in rmsgs { + for (_, msg) in hm { + self.add_skip_checks(msg).map_err(|_| ()); + } + } + Ok(()) + } +} + +fn add(m: SignedMessage, rmsgs: &mut HashMap>) { + let s = rmsgs.get_mut(m.from()); + if s.is_none() { + let mut temp = HashMap::new(); + temp.insert(m.sequence(), m.clone()); + rmsgs.insert(m.from().clone(), temp); + return; + } + let temp = s.unwrap(); + temp.insert(m.sequence(), m.clone()); } #[cfg(test)] mod tests { + use super::Error as Errors; use super::*; + use crate::MessagePool; use address::Address; - use blocks::{BlockHeader, Tipset}; + use blocks::{BlockHeader, Ticket, Tipset}; use cid::Cid; - use crypto::SignatureType; + use crypto::{election_proof::ElectionProof, SignatureType, VRFProof}; + use key_management::{MemKeyStore, Wallet}; use message::{SignedMessage, UnsignedMessage}; use num_bigint::BigUint; - use key_management::{MemKeyStore, Wallet}; - use crate::MessagePool; use std::borrow::BorrowMut; - use super::Error as Errors; + use std::convert::TryFrom; struct TestApi { bmsgs: HashMap>, state_nonce: HashMap, - tipsets: Vec + tipsets: Vec, } impl TestApi { @@ -556,7 +630,7 @@ mod tests { TestApi { bmsgs: HashMap::new(), state_nonce: HashMap::new(), - tipsets: Vec::new() + tipsets: Vec::new(), } } @@ -564,15 +638,15 @@ mod tests { self.state_nonce.insert(addr.clone(), nonce); } - pub fn set_block_messages(&mut self, h: BlockHeader, msgs: Vec) { + pub fn set_block_messages(&mut self, h: &BlockHeader, msgs: Vec) { self.bmsgs.insert(h.cid().clone(), msgs.clone()); + self.tipsets.push(Tipset::new(vec![h.clone()]).unwrap()) } } impl Provider for TestApi { fn subscribe_head_changes(&mut self) -> Tipset { Tipset::new(vec![create_header(1, b"", b"")]).unwrap() - // return Tipset::new(vec![BlockHeader::builder().weight(BigUint::from(1 as u64)).build().unwrap()]).unwrap() } fn put_message(&self, msg: &SignedMessage) -> Result { @@ -580,11 +654,24 @@ mod tests { } fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { - let actor = ActorState::new(Cid::default(), Cid::default(), BigUint::from(9_000_000 as u64), self.state_nonce.get(addr).unwrap().clone()); + let s = self.state_nonce.get(addr); + let mut sequence = 0; + if s.is_some() { + sequence = s.unwrap().clone(); + } + let actor = ActorState::new( + Cid::default(), + Cid::default(), + BigUint::from(9_000_000 as u64), + sequence, + ); Ok(actor) } - fn messages_for_block(&self, h: &BlockHeader) -> Result<(Vec, Vec), Errors> { + fn messages_for_block( + &self, + h: &BlockHeader, + ) -> Result<(Vec, Vec), Errors> { let v: Vec = Vec::new(); let thing = self.bmsgs.get(h.cid()); match thing { @@ -610,8 +697,9 @@ mod tests { fn load_tipset(&self, tsk: &TipsetKeys) -> Result { for ts in &self.tipsets { + println!("{:?} ------- {:?}", tsk.cids, ts.cids()); if tsk.cids == ts.cids() { - return Ok(ts.clone()) + return Ok(ts.clone()); } } Err(Errors::InvalidToAddr) @@ -628,13 +716,76 @@ mod tests { .unwrap(); header } - fn create_smsg(to: &Address, from: &Address, wallet: &mut Wallet) -> SignedMessage { - let umsg: UnsignedMessage = UnsignedMessage::builder().to(to.clone()).from(from.clone()).build().unwrap(); + + fn create_smsg( + to: &Address, + from: &Address, + wallet: &mut Wallet, + nonce: u64, + ) -> SignedMessage { + let umsg: UnsignedMessage = UnsignedMessage::builder() + .to(to.clone()) + .from(from.clone()) + .sequence(nonce) + .build() + .unwrap(); let message_cbor = Cbor::marshal_cbor(&umsg).unwrap(); let sig = wallet.sign(&from, message_cbor.as_slice()).unwrap(); SignedMessage::new_from_fields(umsg, sig) } + fn mock_block(weight: u64, ticket_nonce: u64) -> BlockHeader { + let addr = Address::new_id(1234561); + let c = + Cid::try_from("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i").unwrap(); + + let fmt_str = format!("===={}=====", ticket_nonce); + let ticket = Ticket::new(VRFProof::new(fmt_str.clone().into_bytes())); + let election_proof = ElectionProof { + vrfproof: VRFProof::new(fmt_str.into_bytes()), + }; + let weight_inc = BigUint::from(weight); + BlockHeader::builder() + .miner_address(addr) + .election_proof(Some(election_proof)) + .ticket(ticket) + .message_receipts(c.clone()) + .messages(c.clone()) + .state_root(c) + .weight(weight_inc) + .build_and_validate() + .unwrap() + } + + fn mock_block_with_parents(parents: Tipset, weight: u64, ticket_nonce: u64) -> BlockHeader { + let addr = Address::new_id(1234561); + let c = + Cid::try_from("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i").unwrap(); + + // let parents_of = parents.parents().clone(); + let height = parents.epoch() + 1; + + let mut weight_inc = BigUint::from(weight); + weight_inc = parents.blocks()[0].weight() + weight_inc; + let fmt_str = format!("===={}=====", ticket_nonce); + let ticket = Ticket::new(VRFProof::new(fmt_str.clone().into_bytes())); + let election_proof = ElectionProof { + vrfproof: VRFProof::new(fmt_str.into_bytes()), + }; + BlockHeader::builder() + .miner_address(addr) + .election_proof(Some(election_proof)) + .ticket(ticket) + .parents(parents.key().clone()) + .message_receipts(c.clone()) + .messages(c.clone()) + .state_root(c) + .weight(weight_inc) + .epoch(height) + .build_and_validate() + .unwrap() + } + #[test] fn test_message_pool() { let keystore = MemKeyStore::new(); @@ -647,17 +798,83 @@ mod tests { let mut mempool = MessagePool::new(tma, "mptest".to_string()).unwrap(); - let mut smsg_vec = Vec::new(); - for _ in 1..4 { - let msg = create_smsg(&target, &sender, wallet.borrow_mut()); + for i in 0..4 { + let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); smsg_vec.push(msg); } - let mut nonce = mempool.get_nonce(&sender).unwrap(); - assert_eq!(nonce, 0); + assert_eq!(mempool.get_nonce(&sender).unwrap(), 0); mempool.push(&smsg_vec[0]).unwrap(); - nonce = mempool.get_nonce(&sender).unwrap(); - assert_eq!(nonce, 1); + assert_eq!(mempool.get_nonce(&sender).unwrap(), 1); + mempool.push(&smsg_vec[1]).unwrap(); + assert_eq!(mempool.get_nonce(&sender).unwrap(), 2); + + let a = mock_block(1, 1); + + mempool + .api + .set_block_messages(&a, vec![smsg_vec[0].clone(), smsg_vec[1].clone()]); + mempool + .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) + .unwrap(); + + assert_eq!(mempool.get_nonce(&sender).unwrap(), 2); + } + + #[test] + fn test_revert_messages() { + let tma = TestApi::new(); + let mut wallet = Wallet::new(MemKeyStore::new()); + let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + + let a = mock_block(1, 1); + let tipset = Tipset::new(vec![a.clone()]).unwrap(); + println!("this is the cid {:?}", tipset.cids()); + let b = mock_block_with_parents(tipset, 1, 1); + + println!("{:?}", b.parents().cids); + let sender = wallet.generate_addr(SignatureType::BLS).unwrap(); + let target = Address::new_id(1001); + + let mut smsg_vec = Vec::new(); + + for i in 0..4 { + let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); + smsg_vec.push(msg); + } + + mpool.api.set_block_messages(&a, vec![smsg_vec[0].clone()]); + mpool + .api + .set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); + + mpool.api.set_state_nonce(&sender, 0); + + mpool.add(&smsg_vec[0]).unwrap(); + mpool.add(&smsg_vec[1]).unwrap(); + mpool.add(&smsg_vec[2]).unwrap(); + mpool.add(&smsg_vec[3]).unwrap(); + + mpool.api.set_state_nonce(&sender, 0); + mpool + .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) + .unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + + mpool.api.set_state_nonce(&sender, 1); + mpool + .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) + .unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + + mpool.api.set_state_nonce(&sender, 0); + mpool + .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) + .unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + + let (p, _) = mpool.pending(); + assert_eq!(p.len(), 3); } } From dffff0bfeaa4ead5022bcc64c77275e9489ef424 Mon Sep 17 00:00:00 2001 From: flodesi Date: Fri, 19 Jun 2020 15:40:04 -0400 Subject: [PATCH 14/30] fmt and clippy --- blockchain/message_pool/src/msgpool.rs | 133 +++++++++++-------------- 1 file changed, 59 insertions(+), 74 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index b542a1e6a205..fa77f470906e 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -21,12 +21,9 @@ use std::borrow::BorrowMut; use std::collections::HashMap; use vm::ActorState; -/// TODO after subscribe head changes is done, re-go over logic to make sure that all functions are -/// connected where needed as well as implement all uses of subscribe head changes - /// Simple struct that contains a hashmap of messages where k: a message from address, v: a message /// which corresponds to that address -#[derive(Clone)] +#[derive(Clone, Default)] pub struct MsgSet { msgs: HashMap, next_nonce: u64, @@ -44,13 +41,10 @@ impl MsgSet { /// Add a signed message to the MsgSet. Increase next_nonce if the message has a nonce greater /// than any existing message nonce. pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { - println!("this is the sequence {}", m.sequence()); - println!("this is the next nonce {}", self.next_nonce); if self.msgs.is_empty() || m.sequence() >= self.next_nonce { self.next_nonce = m.sequence() + 1; } if self.msgs.contains_key(&m.sequence()) { - // need to fix in the event that there's an err raised from calling this next line let exms = self.msgs.get(&m.sequence()).unwrap(); if m.cid().map_err(|err| Error::Other(err.to_string()))? != exms.cid().map_err(|err| Error::Other(err.to_string()))? @@ -71,22 +65,29 @@ impl MsgSet { } } +/// Trait documentation for pub trait Provider { + /// Return the tipset that will be used for the Messagepool cur_tipset during the creation of a + /// Messagepool fn subscribe_head_changes(&mut self) -> Tipset; + /// Add a message to the MpoolProvider, return either Cid or Error depending on successful put fn put_message(&self, msg: &SignedMessage) -> Result; + /// Return state actor for given address given the tipset that the a temp StateTree will be rooted + /// at. Return ActorState or Error depending on whether or not ActorState is found fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result; - // fn state_account_key(&self, addr: &Address, ts: Tipset) -> Result; // TODO dunno how to do this + /// Return the signed messages for given blockheader fn messages_for_block( &self, h: &BlockHeader, ) -> Result<(Vec, Vec), Error>; + /// Return all messages for a tipset fn messages_for_tipset(&self, h: &Tipset) -> Result, Error>; - fn load_tipset(&self, tsk: &TipsetKeys) -> Result; // TODO dunno how to do this + /// Return a tipset given the tipset keys from the ChainStore + fn load_tipset(&self, tsk: &TipsetKeys) -> Result; } /// This is the mpool provider struct that will let us access and add messages to messagepool. -/// future TODO is to add a pubsub field to allow for publishing updates. Future TODO is also to -/// add a subscribe_head_change function in order to actually get a functioning messagepool +/// future TODO is to add a pubsub field to allow for publishing updates. pub struct MpoolProvider { cs: ChainStore, } @@ -107,23 +108,23 @@ impl Provider for MpoolProvider where DB: BlockStore, { - // TODO find a way to get this function to periodically check for head changes in cs and update - // msgpool accordingly + /// TODO find a way to get this function to periodically check for head changes in cs and update + /// msgpool accordingly or find a way to have an async function always running in the background + /// checking for newly published tipset and add it to msgpool fn subscribe_head_changes(&mut self) -> Tipset { if self.cs.heaviest_tipset().is_none() { task::block_on(async { let mut subscriber = self.cs.subscribe(); loop { let sub = subscriber.next().await; - if sub.is_some() { - if self + if sub.is_some() + && self .cs .set_heaviest_tipset(sub.unwrap().clone()) .await .is_ok() - { - break; - } + { + break; } } }); @@ -131,7 +132,6 @@ where self.cs.heaviest_tipset().unwrap().as_ref().clone() } - /// Add a message to the MpoolProvider, return either Cid or Error depending on successful put fn put_message(&self, msg: &SignedMessage) -> Result { let cid = self .cs @@ -141,8 +141,6 @@ where Ok(cid) } - /// Return state actor for given address given the tipset that the a temp StateTree will be rooted - /// at. Return ActorState or Error depending on whether or not ActorState is found fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { let state = StateTree::new_from_root(self.cs.db.as_ref(), ts.parent_state()) .map_err(Error::Other)?; @@ -153,7 +151,6 @@ where } } - /// Return the signed messages for given blockheader fn messages_for_block( &self, h: &BlockHeader, @@ -161,7 +158,6 @@ where chain::block_messages(self.cs.blockstore(), h).map_err(|err| Error::Other(err.to_string())) } - /// Return all messages for a tipset fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { let mut umsg: Vec = Vec::new(); let mut msg: Vec = Vec::new(); @@ -178,7 +174,6 @@ where Ok(umsg) } - /// Return a tipset given the tipset keys from the ChainStore fn load_tipset(&self, tsk: &TipsetKeys) -> Result { self.cs .tipset_from_keys(tsk) @@ -186,20 +181,17 @@ where } } -/// This is the main MessagePool struct TODO async safety as well as get a tipset for the cur_tipset -/// field. This can only be done when subscribe to new heads has been completed +/// This is the main MessagePool struct TODO async safety pub struct MessagePool { - // need to inquire about closer in golang and rust equivalent local_addrs: Vec
, pending: HashMap, - pub(crate) cur_tipset: Tipset, // need to wait until pubsub is done - api: T, // will need to replace with provider type + pub(crate) cur_tipset: Tipset, + api: T, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, bls_sig_cache: LruCache, sig_val_cache: LruCache, - // this will be a hashmap where the key is msg.cid.bytes.to_string and the value is a byte vec local_msgs: HashMap, SignedMessage>, } @@ -207,7 +199,7 @@ impl MessagePool where T: Provider, { - /// Create a new MessagePool. This is not yet functioning as per the outlined TODO above + /// Create a new message pool pub fn new(mut api: T, network_name: String) -> Result, Error> where T: Provider, @@ -232,8 +224,9 @@ where Ok(mp) } + /// Add a signed message to local_addrs and local_msgs fn add_local(&mut self, m: &SignedMessage, msgb: Vec) -> Result<(), Error> { - self.local_addrs.push(m.from().clone()); + self.local_addrs.push(*m.from()); self.local_msgs.insert(msgb, m.clone()); Ok(()) } @@ -273,7 +266,7 @@ where /// Add a SignedMessage without doing any of the checks pub fn add_skip_checks(&mut self, m: SignedMessage) -> Result<(), Error> { - self.add_locked(m) + self.add_helper(m) } /// Return the string representation of the message signature @@ -290,13 +283,13 @@ where .unwrap() .to_bytes() .into_iter() - .map(|b| char::from(b)) + .map(char::from) .collect::(); beginning += &msg .signature() .bytes() - .into_iter() - .map(|b| char::from(b.clone())) + .iter() + .map(|b| char::from(*b)) .collect::(); Ok(beginning) } @@ -339,13 +332,13 @@ where if balance < msg_balance { return Err(Error::NotEnoughFunds); } - self.add_locked(msg) + self.add_helper(msg) } /// Finish verifying signed message before adding it to the pending mset hashmap. If an entry /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending hashmap - fn add_locked(&mut self, msg: SignedMessage) -> Result<(), Error> { + fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { if msg.signature().signature_type() == SignatureType::BLS { self.bls_sig_cache.put( msg.cid().map_err(|err| Error::Other(err.to_string()))?, @@ -368,17 +361,15 @@ where let mut mset = MsgSet::new(); mset.add(&msg) .map_err(|err| Error::Other(err.to_string()))?; - self.pending.insert(msg.message().from().clone(), mset); + self.pending.insert(*msg.message().from(), mset); } } Ok(()) } + /// Get the nonce for a given address, return Error if there is a failure to retrieve nonce pub fn get_nonce(&self, addr: &Address) -> Result { - self.get_nonce_locked(addr, &self.cur_tipset) - } - - fn get_nonce_locked(&self, addr: &Address, cur_ts: &Tipset) -> Result { + let cur_ts = &self.cur_tipset; let state_nonce = self.get_state_nonce(addr, cur_ts)?; match self.pending.get(addr) { @@ -402,14 +393,8 @@ where // of the params for this method and just use the chain head let msgs = self.api.messages_for_tipset(cur_ts)?; for m in msgs { - println!("this the base_nonce: {}", base_nonce); if m.from() == addr { if m.sequence() != base_nonce { - println!( - "this is the nonce: {} and this is the base_nonce: {}", - m.sequence(), - base_nonce - ); return Err(Error::Other("tipset has bad nonce ordering".to_string())); } base_nonce += 1; @@ -425,6 +410,7 @@ where Ok(BigInt::from(actor.balance)) } + /// Remove a message given a nonce and address from the messagepool pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { let mset = self.pending.get_mut(from).unwrap(); mset.msgs.remove(&sequence); @@ -448,8 +434,6 @@ where /// Return a tuple that contains a vector of all signed messages and the current tipset for /// self. - /// TODO when subscribe head changes is completed, change the return type parameters and refactor - /// TODO need to see if after this function is run, clear out the pending field in self pub fn pending(&self) -> (Vec, Tipset) { let mut out: Vec = Vec::new(); for (addr, _) in self.pending.clone() { @@ -511,7 +495,7 @@ where /// Return gas price estimate this has been translated from lotus, a more smart implementation will /// most likely need to be implemented pub fn estimate_gas_price(&self, nblocksincl: u64) -> Result { - // TODO: something different, this is what lotus has and there is a TODO there too + // TODO possibly come up with a smarter way to estimate the gas price let min_gas_price = 0; match nblocksincl { 0 => Ok(BigInt::from(min_gas_price + 2)), @@ -536,6 +520,8 @@ where Ok(()) } + /// This is a helper method for head_change. This method will remove a nonce for a from address + /// from rmsgs fn rm( &mut self, from: &Address, @@ -544,7 +530,7 @@ where ) { let s = rmsgs.get_mut(from); if s.is_none() { - self.remove(from, nonce).map_err(|_| ()); + self.remove(from, nonce).ok(); return; } let temp = s.unwrap(); @@ -552,10 +538,10 @@ where temp.remove(&nonce); return; } - self.remove(from, nonce); + self.remove(from, nonce).ok(); } - /// this function will revert and/or apply tipsets to the message pool. This function should be + /// This function will revert and/or apply tipsets to the message pool. This function should be /// called every time that there is a head change in the message pool pub fn head_change(&mut self, revert: Vec, apply: Vec) -> Result<(), Error> { let mut rmsgs: HashMap> = HashMap::new(); @@ -585,19 +571,20 @@ where for (_, hm) in rmsgs { for (_, msg) in hm { - self.add_skip_checks(msg).map_err(|_| ()); + self.add_skip_checks(msg).ok(); } } Ok(()) } } +/// This function is a helper method for head_change. This method will add a signed message to rmsgs fn add(m: SignedMessage, rmsgs: &mut HashMap>) { let s = rmsgs.get_mut(m.from()); if s.is_none() { let mut temp = HashMap::new(); temp.insert(m.sequence(), m.clone()); - rmsgs.insert(m.from().clone(), temp); + rmsgs.insert(*m.from(), temp); return; } let temp = s.unwrap(); @@ -649,11 +636,11 @@ mod tests { Tipset::new(vec![create_header(1, b"", b"")]).unwrap() } - fn put_message(&self, msg: &SignedMessage) -> Result { + fn put_message(&self, _msg: &SignedMessage) -> Result { Ok(Cid::default()) } - fn state_get_actor(&self, addr: &Address, ts: &Tipset) -> Result { + fn state_get_actor(&self, addr: &Address, _ts: &Tipset) -> Result { let s = self.state_nonce.get(addr); let mut sequence = 0; if s.is_some() { @@ -697,7 +684,6 @@ mod tests { fn load_tipset(&self, tsk: &TipsetKeys) -> Result { for ts in &self.tipsets { - println!("{:?} ------- {:?}", tsk.cids, ts.cids()); if tsk.cids == ts.cids() { return Ok(ts.clone()); } @@ -762,7 +748,6 @@ mod tests { let c = Cid::try_from("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i").unwrap(); - // let parents_of = parents.parents().clone(); let height = parents.epoch() + 1; let mut weight_inc = BigUint::from(weight); @@ -796,7 +781,7 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_nonce(&sender, 0); - let mut mempool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); let mut smsg_vec = Vec::new(); for i in 0..4 { @@ -804,22 +789,24 @@ mod tests { smsg_vec.push(msg); } - assert_eq!(mempool.get_nonce(&sender).unwrap(), 0); - mempool.push(&smsg_vec[0]).unwrap(); - assert_eq!(mempool.get_nonce(&sender).unwrap(), 1); - mempool.push(&smsg_vec[1]).unwrap(); - assert_eq!(mempool.get_nonce(&sender).unwrap(), 2); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 0); + mpool.push(&smsg_vec[0]).unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 1); + mpool.push(&smsg_vec[1]).unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 2); + mpool.push(&smsg_vec[2]).unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 3); + mpool.push(&smsg_vec[3]).unwrap(); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); let a = mock_block(1, 1); - mempool - .api - .set_block_messages(&a, vec![smsg_vec[0].clone(), smsg_vec[1].clone()]); - mempool + mpool.api.set_block_messages(&a, smsg_vec); + mpool .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) .unwrap(); - assert_eq!(mempool.get_nonce(&sender).unwrap(), 2); + assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); } #[test] @@ -830,10 +817,8 @@ mod tests { let a = mock_block(1, 1); let tipset = Tipset::new(vec![a.clone()]).unwrap(); - println!("this is the cid {:?}", tipset.cids()); let b = mock_block_with_parents(tipset, 1, 1); - println!("{:?}", b.parents().cids); let sender = wallet.generate_addr(SignatureType::BLS).unwrap(); let target = Address::new_id(1001); From 7275d9bf1197efc7195835c02ac9a955c9c1cc98 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 23 Jun 2020 10:48:55 -0400 Subject: [PATCH 15/30] added changes --- blockchain/message_pool/src/errors.rs | 8 +- blockchain/message_pool/src/msgpool.rs | 133 +++++++++++++------------ 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index 86e8a2f56e19..877eeda97117 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -11,14 +11,14 @@ pub enum Error { MessageTooBig, #[error("Cannot send more Filecoin than will ever exist")] MessageValueTooHigh, - #[error("Message nonce too low")] - NonceTooLow, + #[error("Message sequence too low")] + SequenceTooLow, #[error("not enough funds to execute transaction")] NotEnoughFunds, #[error("invalid to address for message")] InvalidToAddr, - #[error("message with nonce already in mempool")] - DuplicateNonce, + #[error("message with sequence already in mempool")] + DuplicateSequence, #[error("signature validation failed")] SigVerification, #[error("Unknown signature type")] diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index fa77f470906e..2b6999d8e75b 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::errors::Error; -use crate::errors::Error::DuplicateNonce; use address::Address; use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; @@ -26,23 +25,23 @@ use vm::ActorState; #[derive(Clone, Default)] pub struct MsgSet { msgs: HashMap, - next_nonce: u64, + next_sequence: u64, } impl MsgSet { - /// Generate a new MsgSet with an empty hashmap and a default next_nonce of 0 + /// Generate a new MsgSet with an empty hashmap and a default next_sequence of 0 pub fn new() -> Self { MsgSet { msgs: HashMap::new(), - next_nonce: 0, + next_sequence: 0, } } - /// Add a signed message to the MsgSet. Increase next_nonce if the message has a nonce greater - /// than any existing message nonce. + /// Add a signed message to the MsgSet. Increase next_sequence if the message has a sequence greater + /// than any existing message sequence. pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { - if self.msgs.is_empty() || m.sequence() >= self.next_nonce { - self.next_nonce = m.sequence() + 1; + if self.msgs.is_empty() || m.sequence() >= self.next_sequence { + self.next_sequence = m.sequence() + 1; } if self.msgs.contains_key(&m.sequence()) { let exms = self.msgs.get(&m.sequence()).unwrap(); @@ -53,10 +52,11 @@ impl MsgSet { let replace_by_fee_ratio: f32 = 1.25; let rbf_num = BigUint::from(((replace_by_fee_ratio - 1_f32) * 256_f32) as u64); let rbf_denom = BigUint::from(256 as u64); - let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom; + let min_price = + gas_price.clone() + (gas_price / &rbf_num) + rbf_denom + BigUint::from(1_u64); if m.message().gas_price() <= &min_price { - // message with duplicate nonce is already in mpool - return Err(DuplicateNonce); + // message with duplicate sequence is already in mpool + return Err(Error::DuplicateSequence); } } } @@ -65,7 +65,8 @@ impl MsgSet { } } -/// Trait documentation for +/// Provider Trait. This trait will be used by the messagepool to interact with some medium in order to do +/// the operations that are listed below that are required for the messagepool. pub trait Provider { /// Return the tipset that will be used for the Messagepool cur_tipset during the creation of a /// Messagepool @@ -318,13 +319,13 @@ where } } - /// Verify the state_nonce and balance for the sender of the message given then call add_locked + /// Verify the state_sequence and balance for the sender of the message given then call add_locked /// to finish adding the signed_message to pending fn add_tipset(&mut self, msg: SignedMessage, cur_ts: &Tipset) -> Result<(), Error> { - let snonce = self.get_state_nonce(msg.from(), cur_ts)?; + let sequence = self.get_state_sequence(msg.from(), cur_ts)?; - if snonce > msg.message().sequence() { - return Err(Error::NonceTooLow); + if sequence > msg.message().sequence() { + return Err(Error::SequenceTooLow); } let balance = self.get_state_balance(msg.from(), cur_ts)?; @@ -367,40 +368,40 @@ where Ok(()) } - /// Get the nonce for a given address, return Error if there is a failure to retrieve nonce - pub fn get_nonce(&self, addr: &Address) -> Result { + /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence + pub fn get_sequence(&self, addr: &Address) -> Result { let cur_ts = &self.cur_tipset; - let state_nonce = self.get_state_nonce(addr, cur_ts)?; + let sequence = self.get_state_sequence(addr, cur_ts)?; match self.pending.get(addr) { Some(mset) => { - if state_nonce > mset.next_nonce { - return Ok(state_nonce); + if sequence > mset.next_sequence { + return Ok(sequence); } - Ok(mset.next_nonce) + Ok(mset.next_sequence) } - None => Ok(state_nonce), + None => Ok(sequence), } } - /// Get the state of the base_nonce for a given address in cur_ts - fn get_state_nonce(&self, addr: &Address, cur_ts: &Tipset) -> Result { + /// Get the state of the base_sequence for a given address in cur_ts + fn get_state_sequence(&self, addr: &Address, cur_ts: &Tipset) -> Result { let actor = self.api.state_get_actor(&addr, cur_ts)?; - let mut base_nonce = actor.sequence; + let mut base_sequence = actor.sequence; // TODO here lotus has a todo, so I guess we should eventually remove cur_ts from one // of the params for this method and just use the chain head let msgs = self.api.messages_for_tipset(cur_ts)?; for m in msgs { if m.from() == addr { - if m.sequence() != base_nonce { - return Err(Error::Other("tipset has bad nonce ordering".to_string())); + if m.sequence() != base_sequence { + return Err(Error::Other("tipset has bad sequence ordering".to_string())); } - base_nonce += 1; + base_sequence += 1; } } - Ok(base_nonce) + Ok(base_sequence) } /// Get the state balance for the actor that corresponds to the supplied address and tipset, @@ -410,7 +411,7 @@ where Ok(BigInt::from(actor.balance)) } - /// Remove a message given a nonce and address from the messagepool + /// Remove a message given a sequence and address from the messagepool pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { let mset = self.pending.get_mut(from).unwrap(); mset.msgs.remove(&sequence); @@ -427,7 +428,7 @@ where if max_sequence < sequence { max_sequence = sequence; } - mset.next_nonce = max_sequence + 1; + mset.next_sequence = max_sequence + 1; } Ok(()) } @@ -443,7 +444,7 @@ where } /// Return a Vector of signed messages for a given from address. This vector will be sorted by - /// each messsage's nonce (sequence). If no corresponding messages found, return None result type + /// each messsage's sequence. If no corresponding messages found, return None result type fn pending_for(&self, a: &Address) -> Option> { let mset = self.pending.get(a); match mset { @@ -511,7 +512,7 @@ where match self.add(&value) { Ok(()) => (), Err(err) => { - if err == Error::NonceTooLow { + if err == Error::SequenceTooLow { self.local_msgs.remove(&key); } } @@ -520,25 +521,25 @@ where Ok(()) } - /// This is a helper method for head_change. This method will remove a nonce for a from address + /// This is a helper method for head_change. This method will remove a sequence for a from address /// from rmsgs fn rm( &mut self, from: &Address, - nonce: u64, + sequence: u64, rmsgs: &mut HashMap>, ) { let s = rmsgs.get_mut(from); if s.is_none() { - self.remove(from, nonce).ok(); + self.remove(from, sequence).ok(); return; } let temp = s.unwrap(); - if temp.get_mut(&nonce).is_some() { - temp.remove(&nonce); + if temp.get_mut(&sequence).is_some() { + temp.remove(&sequence); return; } - self.remove(from, nonce).ok(); + self.remove(from, sequence).ok(); } /// This function will revert and/or apply tipsets to the message pool. This function should be @@ -588,7 +589,7 @@ fn add(m: SignedMessage, rmsgs: &mut HashMap>, - state_nonce: HashMap, + state_sequence: HashMap, tipsets: Vec, } @@ -616,13 +617,13 @@ mod tests { pub fn new() -> Self { TestApi { bmsgs: HashMap::new(), - state_nonce: HashMap::new(), + state_sequence: HashMap::new(), tipsets: Vec::new(), } } - pub fn set_state_nonce(&mut self, addr: &Address, nonce: u64) { - self.state_nonce.insert(addr.clone(), nonce); + pub fn set_state_sequence(&mut self, addr: &Address, sequence: u64) { + self.state_sequence.insert(addr.clone(), sequence); } pub fn set_block_messages(&mut self, h: &BlockHeader, msgs: Vec) { @@ -641,7 +642,7 @@ mod tests { } fn state_get_actor(&self, addr: &Address, _ts: &Tipset) -> Result { - let s = self.state_nonce.get(addr); + let s = self.state_sequence.get(addr); let mut sequence = 0; if s.is_some() { sequence = s.unwrap().clone(); @@ -707,12 +708,12 @@ mod tests { to: &Address, from: &Address, wallet: &mut Wallet, - nonce: u64, + sequence: u64, ) -> SignedMessage { let umsg: UnsignedMessage = UnsignedMessage::builder() .to(to.clone()) .from(from.clone()) - .sequence(nonce) + .sequence(sequence) .build() .unwrap(); let message_cbor = Cbor::marshal_cbor(&umsg).unwrap(); @@ -720,12 +721,12 @@ mod tests { SignedMessage::new_from_fields(umsg, sig) } - fn mock_block(weight: u64, ticket_nonce: u64) -> BlockHeader { + fn mock_block(weight: u64, ticket_sequence: u64) -> BlockHeader { let addr = Address::new_id(1234561); let c = Cid::try_from("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i").unwrap(); - let fmt_str = format!("===={}=====", ticket_nonce); + let fmt_str = format!("===={}=====", ticket_sequence); let ticket = Ticket::new(VRFProof::new(fmt_str.clone().into_bytes())); let election_proof = ElectionProof { vrfproof: VRFProof::new(fmt_str.into_bytes()), @@ -743,7 +744,7 @@ mod tests { .unwrap() } - fn mock_block_with_parents(parents: Tipset, weight: u64, ticket_nonce: u64) -> BlockHeader { + fn mock_block_with_parents(parents: Tipset, weight: u64, ticket_sequence: u64) -> BlockHeader { let addr = Address::new_id(1234561); let c = Cid::try_from("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i").unwrap(); @@ -752,7 +753,7 @@ mod tests { let mut weight_inc = BigUint::from(weight); weight_inc = parents.blocks()[0].weight() + weight_inc; - let fmt_str = format!("===={}=====", ticket_nonce); + let fmt_str = format!("===={}=====", ticket_sequence); let ticket = Ticket::new(VRFProof::new(fmt_str.clone().into_bytes())); let election_proof = ElectionProof { vrfproof: VRFProof::new(fmt_str.into_bytes()), @@ -779,7 +780,7 @@ mod tests { let target = wallet.generate_addr(SignatureType::Secp256k1).unwrap(); let mut tma = TestApi::new(); - tma.set_state_nonce(&sender, 0); + tma.set_state_sequence(&sender, 0); let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); @@ -789,15 +790,15 @@ mod tests { smsg_vec.push(msg); } - assert_eq!(mpool.get_nonce(&sender).unwrap(), 0); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); mpool.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 1); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); mpool.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 2); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); mpool.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 3); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); mpool.push(&smsg_vec[3]).unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); let a = mock_block(1, 1); @@ -806,7 +807,7 @@ mod tests { .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); } #[test] @@ -834,30 +835,30 @@ mod tests { .api .set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); - mpool.api.set_state_nonce(&sender, 0); + mpool.api.set_state_sequence(&sender, 0); mpool.add(&smsg_vec[0]).unwrap(); mpool.add(&smsg_vec[1]).unwrap(); mpool.add(&smsg_vec[2]).unwrap(); mpool.add(&smsg_vec[3]).unwrap(); - mpool.api.set_state_nonce(&sender, 0); + mpool.api.set_state_sequence(&sender, 0); mpool .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); - mpool.api.set_state_nonce(&sender, 1); + mpool.api.set_state_sequence(&sender, 1); mpool .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); - mpool.api.set_state_nonce(&sender, 0); + mpool.api.set_state_sequence(&sender, 0); mpool .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) .unwrap(); - assert_eq!(mpool.get_nonce(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); let (p, _) = mpool.pending(); assert_eq!(p.len(), 3); From c2d139ceff3a37d1f351b1f24db06ea3ed2ee615 Mon Sep 17 00:00:00 2001 From: flodesi Date: Wed, 24 Jun 2020 10:36:05 -0400 Subject: [PATCH 16/30] added changes --- blockchain/chain/src/store/chain_store.rs | 18 ++ blockchain/message_pool/Cargo.toml | 7 +- blockchain/message_pool/src/errors.rs | 14 ++ blockchain/message_pool/src/msgpool.rs | 192 +++++++--------------- vm/message/src/signed_message.rs | 8 +- 5 files changed, 99 insertions(+), 140 deletions(-) diff --git a/blockchain/chain/src/store/chain_store.rs b/blockchain/chain/src/store/chain_store.rs index 7b2e893886f7..451f3ab44a6b 100644 --- a/blockchain/chain/src/store/chain_store.rs +++ b/blockchain/chain/src/store/chain_store.rs @@ -180,6 +180,7 @@ where // the given tipset has already been verified, so this cannot fail Ok(FullTipset::new(blocks).unwrap()) } + /// Determines if provided tipset is heavier than existing known heaviest tipset async fn update_heaviest(&mut self, ts: &Tipset) -> Result<(), Error> { match &self.heaviest { @@ -199,6 +200,23 @@ where } Ok(()) } + + /// Return the messages in the chainstore for a given tipset + pub fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { + let mut umsg: Vec = Vec::new(); + let mut msgs: Vec = Vec::new(); + for bh in h.blocks().iter() { + let (mut bh_umsg_tmp, mut bh_msg_tmp) = block_messages(self.blockstore(), bh)?; + let bh_umsg = &mut bh_umsg_tmp; + let bh_msg = &mut bh_msg_tmp; + umsg.append(bh_umsg); + msgs.append(bh_msg); + } + for msg in msgs { + umsg.push(msg.into_message()); + } + Ok(umsg) + } } /// Returns a Tuple of bls messages of type UnsignedMessage and secp messages diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index 7e2303fb75c1..4b9cc8ab447b 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -18,12 +18,15 @@ lru = "0.5.1" crypto = { package = "forest_crypto", path = "../../crypto", version = "0.2" } chain = { path = "../chain"} state_tree = { path = "../../vm/state_tree/" } -interpreter = { path = "../../vm/interpreter/" } serde = { version = "1.0", features = ["derive"] } db = { path = "../../node/db" } flo_stream = "0.4.0" async-std = "1.6.0" futures = "0.3.5" -key_management = { path = "../../key_management"} libsecp256k1 = "0.3.4" blake2b_simd = "0.5.10" +log = "0.4.8" + +[dev-dependencies] +interpreter = { path = "../../vm/interpreter/" } +key_management = { path = "../../key_management"} \ No newline at end of file diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index 877eeda97117..a2ee99e3f46a 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -1,6 +1,8 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use chain::Error as ChainError; +use encoding::Error as EncodeError; use thiserror::Error; // /// MessagePool error @@ -28,3 +30,15 @@ pub enum Error { #[error("{0}")] Other(String), } + +impl From for Error { + fn from(ce: ChainError) -> Self { + Error::Other(ce.to_string()) + } +} + +impl From for Error { + fn from(ee: EncodeError) -> Self { + Error::Other(ee.to_string()) + } +} diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 2b6999d8e75b..7d181c3f4eef 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,7 +3,7 @@ use super::errors::Error; use address::Address; -use async_std::task; +// use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; use chain::ChainStore; @@ -11,15 +11,20 @@ use cid::multihash::Blake2b256; use cid::Cid; use crypto::{Signature, SignatureType}; use encoding::Cbor; -use futures::stream::StreamExt; +// use futures::stream::StreamExt; +use log::warn; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; -use num_bigint::{BigInt, BigUint, ToBigInt, ToBigUint}; +use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; use std::collections::HashMap; use vm::ActorState; +const REPLACE_BY_FEE_RATIO: f32 = 1.25; +const RBF_NUM: u64 = ((REPLACE_BY_FEE_RATIO - 1f32) * 256f32) as u64; +const RBF_DENOM: u64 = 256; + /// Simple struct that contains a hashmap of messages where k: a message from address, v: a message /// which corresponds to that address #[derive(Clone, Default)] @@ -43,15 +48,11 @@ impl MsgSet { if self.msgs.is_empty() || m.sequence() >= self.next_sequence { self.next_sequence = m.sequence() + 1; } - if self.msgs.contains_key(&m.sequence()) { - let exms = self.msgs.get(&m.sequence()).unwrap(); - if m.cid().map_err(|err| Error::Other(err.to_string()))? - != exms.cid().map_err(|err| Error::Other(err.to_string()))? - { + if let Some(exms) = self.msgs.get(&m.sequence()) { + if m.cid()? != exms.cid()? { let gas_price = exms.message().gas_price(); - let replace_by_fee_ratio: f32 = 1.25; - let rbf_num = BigUint::from(((replace_by_fee_ratio - 1_f32) * 256_f32) as u64); - let rbf_denom = BigUint::from(256 as u64); + let rbf_num = BigUint::from(RBF_NUM); + let rbf_denom = BigUint::from(RBF_DENOM); let min_price = gas_price.clone() + (gas_price / &rbf_num) + rbf_denom + BigUint::from(1_u64); if m.message().gas_price() <= &min_price { @@ -70,7 +71,7 @@ impl MsgSet { pub trait Provider { /// Return the tipset that will be used for the Messagepool cur_tipset during the creation of a /// Messagepool - fn subscribe_head_changes(&mut self) -> Tipset; + fn subscribe_head_changes(&mut self) -> Option; /// Add a message to the MpoolProvider, return either Cid or Error depending on successful put fn put_message(&self, msg: &SignedMessage) -> Result; /// Return state actor for given address given the tipset that the a temp StateTree will be rooted @@ -112,25 +113,9 @@ where /// TODO find a way to get this function to periodically check for head changes in cs and update /// msgpool accordingly or find a way to have an async function always running in the background /// checking for newly published tipset and add it to msgpool - fn subscribe_head_changes(&mut self) -> Tipset { - if self.cs.heaviest_tipset().is_none() { - task::block_on(async { - let mut subscriber = self.cs.subscribe(); - loop { - let sub = subscriber.next().await; - if sub.is_some() - && self - .cs - .set_heaviest_tipset(sub.unwrap().clone()) - .await - .is_ok() - { - break; - } - } - }); - } - self.cs.heaviest_tipset().unwrap().as_ref().clone() + fn subscribe_head_changes(&mut self) -> Option { + let ts = self.cs.heaviest_tipset()?; + Some(ts.as_ref().clone()) } fn put_message(&self, msg: &SignedMessage) -> Result { @@ -146,39 +131,22 @@ where let state = StateTree::new_from_root(self.cs.db.as_ref(), ts.parent_state()) .map_err(Error::Other)?; let actor = state.get_actor(addr).map_err(Error::Other)?; - match actor { - Some(actor_state) => Ok(actor_state), - None => Err(Error::Other("No actor state".to_string())), - } + actor.ok_or_else(|| Error::Other("No actor state".to_owned())) } fn messages_for_block( &self, h: &BlockHeader, ) -> Result<(Vec, Vec), Error> { - chain::block_messages(self.cs.blockstore(), h).map_err(|err| Error::Other(err.to_string())) + chain::block_messages(self.cs.blockstore(), h).map_err(|err| err.into()) } fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { - let mut umsg: Vec = Vec::new(); - let mut msg: Vec = Vec::new(); - for bh in h.blocks().iter() { - let (mut bh_umsg_tmp, mut bh_msg_tmp) = self.messages_for_block(bh)?; - let bh_umsg = bh_umsg_tmp.as_mut(); - let bh_msg = bh_msg_tmp.as_mut(); - umsg.append(bh_umsg); - msg.append(bh_msg); - } - for msg in &msg { - umsg.push(msg.message().clone()); - } - Ok(umsg) + self.cs.messages_for_tipset(h).map_err(|err| err.into()) } fn load_tipset(&self, tsk: &TipsetKeys) -> Result { - self.cs - .tipset_from_keys(tsk) - .map_err(|err| Error::Other(err.to_string())) + self.cs.tipset_from_keys(tsk).map_err(|err| err.into()) } } @@ -186,13 +154,13 @@ where pub struct MessagePool { local_addrs: Vec
, pending: HashMap, - pub(crate) cur_tipset: Tipset, + pub cur_tipset: Tipset, api: T, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, bls_sig_cache: LruCache, - sig_val_cache: LruCache, + sig_val_cache: LruCache, local_msgs: HashMap, SignedMessage>, } @@ -205,8 +173,11 @@ where where T: Provider, { - let tipset = api.subscribe_head_changes(); + // let tipset = api.subscribe_head_changes(); // LruCache sizes have been taken from the lotus implementation + let tipset = api + .subscribe_head_changes() + .ok_or_else(|| Error::Other("No ts in api to set as cur_tipset".to_owned()))?; let bls_sig_cache = LruCache::new(40000); let sig_val_cache = LruCache::new(32000); let mut mp = MessagePool { @@ -214,7 +185,7 @@ where pending: HashMap::new(), cur_tipset: tipset, api, - min_gas_price: ToBigInt::to_bigint(&0).unwrap(), + min_gas_price: Default::default(), max_tx_pool_size: 5000, network_name, bls_sig_cache, @@ -234,28 +205,20 @@ where /// Push a signed message to the MessagePool pub fn push(&mut self, msg: &SignedMessage) -> Result { - let msg_serial = msg - .marshal_cbor() - .map_err(|err| Error::Other(err.to_string()))?; + let msg_serial = msg.marshal_cbor()?; self.add(msg)?; self.add_local(msg, msg_serial)?; - msg.cid().map_err(|err| Error::Other(err.to_string())) + msg.cid().map_err(|err| err.into()) } /// This is a helper to push that will help to make sure that the message fits the parameters /// to be pushed to the MessagePool pub fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { - let size = msg - .marshal_cbor() - .map_err(|err| Error::Other(err.to_string()))? - .len(); + let size = msg.marshal_cbor()?.len(); if size > 32 * 1024 { return Err(Error::MessageTooBig); } - if msg - .value() - .gt(&ToBigUint::to_biguint(&2_000_000_000).unwrap()) - { + if msg.value() > &BigUint::from(2_000_000_000u64) { return Err(Error::MessageValueTooHigh); } @@ -270,53 +233,19 @@ where self.add_helper(m) } - /// Return the string representation of the message signature - pub(crate) fn sig_cache_key(&mut self, msg: &SignedMessage) -> Result { - match msg.signature().signature_type() { - SignatureType::Secp256k1 => Ok(msg.cid().unwrap().to_string()), - SignatureType::BLS => { - if msg.signature().bytes().len() < 90 { - return Err(Error::BLSSigTooShort); - } - let mut beginning = String::new(); - beginning += &msg - .cid() - .unwrap() - .to_bytes() - .into_iter() - .map(char::from) - .collect::(); - beginning += &msg - .signature() - .bytes() - .iter() - .map(|b| char::from(*b)) - .collect::(); - Ok(beginning) - } - } - } - /// Verify the message signature. first check if it has already been verified and put into /// cache. If it has not, then manually verify it then put it into cache for future use fn verify_msg_sig(&mut self, msg: &SignedMessage) -> Result<(), Error> { - let sck = self.sig_cache_key(msg)?; - let is_verif = self.sig_val_cache.get(&sck); - match is_verif { - Some(()) => Ok(()), - None => { - let umsg = Cbor::marshal_cbor(msg.message()) - .map_err(|err| Error::Other(err.to_string()))?; - let verif = msg.signature().verify(umsg.as_slice(), msg.from()); - match verif { - Ok(()) => { - self.sig_val_cache.put(sck, ()); - Ok(()) - } - Err(value) => Err(Error::Other(value)), - } - } + let cid = msg.cid()?; + if let Some(()) = self.sig_val_cache.get(&cid) { + return Ok(()); } + let umsg = msg.message().marshal_cbor()?; + msg.signature() + .verify(umsg.as_slice(), msg.from()) + .map_err(Error::Other)?; + self.sig_val_cache.put(cid, ()); + Ok(()) } /// Verify the state_sequence and balance for the sender of the message given then call add_locked @@ -341,10 +270,7 @@ where /// and push it to the pending hashmap fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { if msg.signature().signature_type() == SignatureType::BLS { - self.bls_sig_cache.put( - msg.cid().map_err(|err| Error::Other(err.to_string()))?, - msg.signature().clone(), - ); + self.bls_sig_cache.put(msg.cid()?, msg.signature().clone()); } if msg.message().gas_limit() > 100_000_000 { return Err(Error::Other( @@ -355,13 +281,10 @@ where let msett = self.pending.get_mut(msg.message().from()); match msett { - Some(mset) => mset - .add(&msg) - .map_err(|err| Error::Other(err.to_string()))?, + Some(mset) => mset.add(&msg)?, None => { let mut mset = MsgSet::new(); - mset.add(&msg) - .map_err(|err| Error::Other(err.to_string()))?; + mset.add(&msg)?; self.pending.insert(*msg.message().from(), mset); } } @@ -486,11 +409,8 @@ where /// Attempt to get the signed message given an unsigned message in message pool pub fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { - let val = self - .bls_sig_cache - .get(&msg.cid().map_err(|err| Error::Other(err.to_string()))?) - .unwrap(); - Ok(SignedMessage::new_from_fields(msg, val.clone())) + let val = self.bls_sig_cache.get(&msg.cid()?).unwrap(); + Ok(SignedMessage::new_from_parts(msg, val.clone())) } /// Return gas price estimate this has been translated from lotus, a more smart implementation will @@ -509,20 +429,18 @@ where /// local_message field, possibly implement this in the future? pub fn load_local(&mut self) -> Result<(), Error> { for (key, value) in self.local_msgs.clone() { - match self.add(&value) { - Ok(()) => (), - Err(err) => { - if err == Error::SequenceTooLow { - self.local_msgs.remove(&key); - } + self.add(&value).unwrap_or_else(|err| { + if err == Error::SequenceTooLow { + warn!("error adding message: {:?}", err); + self.local_msgs.remove(&key); } - } + }); } Ok(()) } /// This is a helper method for head_change. This method will remove a sequence for a from address - /// from rmsgs + /// from the rmsgs hashmap. Also remove the from address and sequence from the mmessagepool. fn rm( &mut self, from: &Address, @@ -579,7 +497,7 @@ where } } -/// This function is a helper method for head_change. This method will add a signed message to rmsgs +/// This function is a helper method for head_change. This method will add a signed message to the given rmsgs HashMap fn add(m: SignedMessage, rmsgs: &mut HashMap>) { let s = rmsgs.get_mut(m.from()); if s.is_none() { @@ -633,8 +551,8 @@ mod tests { } impl Provider for TestApi { - fn subscribe_head_changes(&mut self) -> Tipset { - Tipset::new(vec![create_header(1, b"", b"")]).unwrap() + fn subscribe_head_changes(&mut self) -> Option { + Tipset::new(vec![create_header(1, b"", b"")]).ok() } fn put_message(&self, _msg: &SignedMessage) -> Result { @@ -718,7 +636,7 @@ mod tests { .unwrap(); let message_cbor = Cbor::marshal_cbor(&umsg).unwrap(); let sig = wallet.sign(&from, message_cbor.as_slice()).unwrap(); - SignedMessage::new_from_fields(umsg, sig) + SignedMessage::new_from_parts(umsg, sig) } fn mock_block(weight: u64, ticket_sequence: u64) -> BlockHeader { diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index 210c6009a722..1e2963c2d001 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -25,7 +25,8 @@ impl SignedMessage { Ok(SignedMessage { message, signature }) } - pub fn new_from_fields(message: UnsignedMessage, signature: Signature) -> Self { + /// Construct a new SignedMessage given an UnsignedMessage and Signature + pub fn new_from_parts(message: UnsignedMessage, signature: Signature) -> Self { SignedMessage { message, signature } } /// Returns reference to the unsigned message. @@ -37,6 +38,11 @@ impl SignedMessage { pub fn signature(&self) -> &Signature { &self.signature } + + /// Consumes self and returns it's unsigned message + pub fn into_message(self) -> UnsignedMessage { + self.message + } } impl Message for SignedMessage { From 8c15afbc2785b81d1a28c67cf09193fe9b092439 Mon Sep 17 00:00:00 2001 From: flodesi Date: Sat, 27 Jun 2020 18:51:39 -0400 Subject: [PATCH 17/30] added async subscribe new heads --- blockchain/message_pool/src/errors.rs | 2 + blockchain/message_pool/src/msgpool.rs | 275 ++++++++++++++++++------- 2 files changed, 206 insertions(+), 71 deletions(-) diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index a2ee99e3f46a..658f0d731b9c 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -29,6 +29,8 @@ pub enum Error { BLSSigTooShort, #[error("{0}")] Other(String), + #[error("{0}")] + MutexPoisonError(String), } impl From for Error { diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 7d181c3f4eef..a548c8b4ef76 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,7 +3,7 @@ use super::errors::Error; use address::Address; -// use async_std::task; +use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; use chain::ChainStore; @@ -11,7 +11,8 @@ use cid::multihash::Blake2b256; use cid::Cid; use crypto::{Signature, SignatureType}; use encoding::Cbor; -// use futures::stream::StreamExt; +use flo_stream::Subscriber; +use futures::StreamExt; use log::warn; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; @@ -19,6 +20,9 @@ use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::thread::sleep; +use std::time::Duration; use vm::ActorState; const REPLACE_BY_FEE_RATIO: f32 = 1.25; @@ -69,9 +73,10 @@ impl MsgSet { /// Provider Trait. This trait will be used by the messagepool to interact with some medium in order to do /// the operations that are listed below that are required for the messagepool. pub trait Provider { - /// Return the tipset that will be used for the Messagepool cur_tipset during the creation of a - /// Messagepool - fn subscribe_head_changes(&mut self) -> Option; + /// Update Mpool's cur_tipset whenever there is a chnge to the provider + fn subscribe_head_changes(&mut self) -> Subscriber>; + /// Get the heaviest Tipset in the provider + fn get_heaviest_tipset(&mut self) -> Option; /// Add a message to the MpoolProvider, return either Cid or Error depending on successful put fn put_message(&self, msg: &SignedMessage) -> Result; /// Return state actor for given address given the tipset that the a temp StateTree will be rooted @@ -89,7 +94,7 @@ pub trait Provider { } /// This is the mpool provider struct that will let us access and add messages to messagepool. -/// future TODO is to add a pubsub field to allow for publishing updates. +/// future pub struct MpoolProvider { cs: ChainStore, } @@ -110,10 +115,11 @@ impl Provider for MpoolProvider where DB: BlockStore, { - /// TODO find a way to get this function to periodically check for head changes in cs and update - /// msgpool accordingly or find a way to have an async function always running in the background - /// checking for newly published tipset and add it to msgpool - fn subscribe_head_changes(&mut self) -> Option { + fn subscribe_head_changes(&mut self) -> Subscriber> { + self.cs.subscribe() + } + + fn get_heaviest_tipset(&mut self) -> Option { let ts = self.cs.heaviest_tipset()?; Some(ts.as_ref().clone()) } @@ -150,12 +156,12 @@ where } } -/// This is the main MessagePool struct TODO async safety -pub struct MessagePool { +/// This is the main MessagePool struct +pub struct MessagePool { local_addrs: Vec
, pending: HashMap, - pub cur_tipset: Tipset, - api: T, + pub cur_tipset: Mutex, + api: Arc>, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, @@ -166,33 +172,53 @@ pub struct MessagePool { impl MessagePool where - T: Provider, + T: Provider + std::marker::Send, { /// Create a new message pool - pub fn new(mut api: T, network_name: String) -> Result, Error> + pub fn new(mut api: T, network_name: String) -> Result>>, Error> where T: Provider, { - // let tipset = api.subscribe_head_changes(); // LruCache sizes have been taken from the lotus implementation - let tipset = api - .subscribe_head_changes() - .ok_or_else(|| Error::Other("No ts in api to set as cur_tipset".to_owned()))?; + let tipset = Mutex::new( + api.get_heaviest_tipset() + .ok_or_else(|| Error::Other("No ts in api to set as cur_tipset".to_owned()))?, + ); let bls_sig_cache = LruCache::new(40000); let sig_val_cache = LruCache::new(32000); - let mut mp = MessagePool { + let api_mutex = Arc::new(Mutex::new(api)); + let mp = Arc::new(Mutex::new(MessagePool { local_addrs: Vec::new(), pending: HashMap::new(), cur_tipset: tipset, - api, + api: api_mutex, min_gas_price: Default::default(), max_tx_pool_size: 5000, network_name, bls_sig_cache, sig_val_cache, local_msgs: HashMap::new(), - }; - mp.load_local()?; + })); + + let mut mp_lock = mp.lock().unwrap(); + mp_lock + .load_local()?; + let mut api = mp_lock.api.lock().unwrap(); + let mut subscriber = api.subscribe_head_changes(); + drop(api); + drop(mp_lock); + + let mpool = mp.clone(); + task::spawn(async move { + loop { + if let Some(ts) = subscriber.next().await { + let mut lock = mpool.lock().unwrap(); + lock.head_change(Vec::new(), vec![ts.as_ref().clone()]) + .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); + } + sleep(Duration::new(1, 0)); + } + }); Ok(mp) } @@ -225,7 +251,8 @@ where self.verify_msg_sig(msg)?; let tmp = msg.clone(); - self.add_tipset(tmp, &self.cur_tipset.clone()) + let tip = self.cur_tipset.lock().unwrap().clone(); + self.add_tipset(tmp, &tip) } /// Add a SignedMessage without doing any of the checks @@ -269,6 +296,10 @@ where /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending hashmap fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { + let api = self + .api + .lock() + .map_err(|err| Error::Other(err.to_string()))?; if msg.signature().signature_type() == SignatureType::BLS { self.bls_sig_cache.put(msg.cid()?, msg.signature().clone()); } @@ -277,7 +308,7 @@ where "given message has too high of a gas limit".to_string(), )); } - self.api.put_message(&msg)?; + api.put_message(&msg)?; let msett = self.pending.get_mut(msg.message().from()); match msett { @@ -293,7 +324,7 @@ where /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence pub fn get_sequence(&self, addr: &Address) -> Result { - let cur_ts = &self.cur_tipset; + let cur_ts = &self.cur_tipset.lock().unwrap(); let sequence = self.get_state_sequence(addr, cur_ts)?; match self.pending.get(addr) { @@ -309,13 +340,17 @@ where /// Get the state of the base_sequence for a given address in cur_ts fn get_state_sequence(&self, addr: &Address, cur_ts: &Tipset) -> Result { - let actor = self.api.state_get_actor(&addr, cur_ts)?; + let api = self + .api + .lock() + .map_err(|err| Error::Other(err.to_string()))?; + let actor = api.state_get_actor(&addr, cur_ts)?; let mut base_sequence = actor.sequence; // TODO here lotus has a todo, so I guess we should eventually remove cur_ts from one // of the params for this method and just use the chain head - let msgs = self.api.messages_for_tipset(cur_ts)?; + let msgs = api.messages_for_tipset(cur_ts)?; for m in msgs { if m.from() == addr { if m.sequence() != base_sequence { @@ -330,7 +365,11 @@ where /// Get the state balance for the actor that corresponds to the supplied address and tipset, /// if this actor does not exist, return an error fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { - let actor = self.api.state_get_actor(&addr, &ts)?; + let api = self + .api + .lock() + .map_err(|err| Error::Other(err.to_string()))?; + let actor = api.state_get_actor(&addr, &ts)?; Ok(BigInt::from(actor.balance)) } @@ -363,7 +402,7 @@ where for (addr, _) in self.pending.clone() { out.append(self.pending_for(&addr).unwrap().as_mut()) } - (out, self.cur_tipset.clone()) + (out, self.cur_tipset.lock().unwrap().clone()) } /// Return a Vector of signed messages for a given from address. This vector will be sorted by @@ -393,11 +432,11 @@ where /// Return Vector of signed messages given a block header for self pub fn messages_for_blocks( &mut self, - blks: Vec, + blks: &[BlockHeader], ) -> Result, Error> { let mut msg_vec: Vec = Vec::new(); for block in blks { - let (umsg, mut smsgs) = self.api.messages_for_block(&block)?; + let (umsg, mut smsgs) = self.api.lock().unwrap().messages_for_block(&block)?; msg_vec.append(smsgs.as_mut()); for msg in umsg { let smsg = self.recover_sig(msg)?; @@ -464,11 +503,11 @@ where /// called every time that there is a head change in the message pool pub fn head_change(&mut self, revert: Vec, apply: Vec) -> Result<(), Error> { let mut rmsgs: HashMap> = HashMap::new(); - for ts in revert { - let pts = self.api.load_tipset(ts.parents())?; - let msgs = self.messages_for_blocks(ts.blocks().to_vec())?; - self.cur_tipset = pts; + let pts = self.api.lock().unwrap().load_tipset(ts.parents())?; + let msgs = self.messages_for_blocks(ts.blocks())?; + let parent = pts.clone(); + *self.cur_tipset.lock().unwrap() = parent; for msg in msgs { add(msg, rmsgs.borrow_mut()); } @@ -476,7 +515,7 @@ where for ts in apply { for b in ts.blocks() { - let (msgs, smsgs) = self.api.messages_for_block(b).unwrap(); + let (msgs, smsgs) = self.api.lock().unwrap().messages_for_block(b).unwrap(); for msg in smsgs { self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); } @@ -485,7 +524,7 @@ where self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); } } - self.cur_tipset = ts; + *self.cur_tipset.lock().unwrap() = ts; } for (_, hm) in rmsgs { @@ -516,9 +555,11 @@ mod tests { use super::*; use crate::MessagePool; use address::Address; + use async_std::task; use blocks::{BlockHeader, Ticket, Tipset}; use cid::Cid; use crypto::{election_proof::ElectionProof, SignatureType, VRFProof}; + use flo_stream::{MessagePublisher, Publisher, Subscriber}; use key_management::{MemKeyStore, Wallet}; use message::{SignedMessage, UnsignedMessage}; use num_bigint::BigUint; @@ -529,6 +570,7 @@ mod tests { bmsgs: HashMap>, state_sequence: HashMap, tipsets: Vec, + publisher: Publisher>, } impl TestApi { @@ -537,6 +579,7 @@ mod tests { bmsgs: HashMap::new(), state_sequence: HashMap::new(), tipsets: Vec::new(), + publisher: Publisher::new(1), } } @@ -548,10 +591,18 @@ mod tests { self.bmsgs.insert(h.cid().clone(), msgs.clone()); self.tipsets.push(Tipset::new(vec![h.clone()]).unwrap()) } + + pub async fn set_heaviest_tipset(&mut self, ts: Arc) -> () { + self.publisher.publish(ts).await + } } impl Provider for TestApi { - fn subscribe_head_changes(&mut self) -> Option { + fn subscribe_head_changes(&mut self) -> Subscriber> { + self.publisher.subscribe() + } + + fn get_heaviest_tipset(&mut self) -> Option { Tipset::new(vec![create_header(1, b"", b"")]).ok() } @@ -700,7 +751,8 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_sequence(&sender, 0); - let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mut mpool_locked = mpool.lock().unwrap(); let mut smsg_vec = Vec::new(); for i in 0..4 { @@ -708,31 +760,39 @@ mod tests { smsg_vec.push(msg); } - assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); - mpool.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); - mpool.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); - mpool.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); - mpool.push(&smsg_vec[3]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 0); + mpool_locked.push(&smsg_vec[0]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 1); + mpool_locked.push(&smsg_vec[1]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 2); + mpool_locked.push(&smsg_vec[2]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 3); + mpool_locked.push(&smsg_vec[3]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); let a = mock_block(1, 1); - mpool.api.set_block_messages(&a, smsg_vec); - mpool + mpool_locked + .api + .lock() + .unwrap() + .set_block_messages(&a, smsg_vec); + mpool_locked .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); + + drop(mpool_locked); + assert_eq!(mpool.lock().unwrap().get_sequence(&sender).unwrap(), 4); } #[test] fn test_revert_messages() { let tma = TestApi::new(); let mut wallet = Wallet::new(MemKeyStore::new()); - let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mut mpool_locked = mpool.lock().unwrap(); let a = mock_block(1, 1); let tipset = Tipset::new(vec![a.clone()]).unwrap(); @@ -748,37 +808,110 @@ mod tests { smsg_vec.push(msg); } - mpool.api.set_block_messages(&a, vec![smsg_vec[0].clone()]); - mpool + mpool_locked + .api + .lock() + .unwrap() + .set_block_messages(&a, vec![smsg_vec[0].clone()]); + mpool_locked .api + .lock() + .unwrap() .set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); - mpool.api.set_state_sequence(&sender, 0); + mpool_locked + .api + .lock() + .unwrap() + .set_state_sequence(&sender, 0); - mpool.add(&smsg_vec[0]).unwrap(); - mpool.add(&smsg_vec[1]).unwrap(); - mpool.add(&smsg_vec[2]).unwrap(); - mpool.add(&smsg_vec[3]).unwrap(); + mpool_locked.add(&smsg_vec[0]).unwrap(); + mpool_locked.add(&smsg_vec[1]).unwrap(); + mpool_locked.add(&smsg_vec[2]).unwrap(); + mpool_locked.add(&smsg_vec[3]).unwrap(); - mpool.api.set_state_sequence(&sender, 0); - mpool + mpool_locked + .api + .lock() + .unwrap() + .set_state_sequence(&sender, 0); + mpool_locked .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - mpool.api.set_state_sequence(&sender, 1); - mpool + mpool_locked + .api + .lock() + .unwrap() + .set_state_sequence(&sender, 1); + mpool_locked .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - mpool.api.set_state_sequence(&sender, 0); - mpool + mpool_locked + .api + .lock() + .unwrap() + .set_state_sequence(&sender, 0); + mpool_locked .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - let (p, _) = mpool.pending(); + let (p, _) = mpool_locked.pending(); assert_eq!(p.len(), 3); } + + #[test] + fn test_async_message_pool() { + let keystore = MemKeyStore::new(); + let mut wallet = Wallet::new(keystore); + let sender = wallet.generate_addr(SignatureType::Secp256k1).unwrap(); + let target = wallet.generate_addr(SignatureType::Secp256k1).unwrap(); + + let mut tma = TestApi::new(); + tma.set_state_sequence(&sender, 0); + + let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + let mut mpool_locked = mpool.lock().unwrap(); + + let mut smsg_vec = Vec::new(); + for i in 0..3 { + let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); + smsg_vec.push(msg); + } + + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 0); + mpool_locked.push(&smsg_vec[0]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 1); + mpool_locked.push(&smsg_vec[1]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 2); + mpool_locked.push(&smsg_vec[2]).unwrap(); + assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 3); + + drop(mpool_locked); + + let header = mock_block(1, 1); + let tipset = Tipset::new(vec![header.clone()]).unwrap(); + + let updater = mpool.lock().unwrap(); + let temp = updater.api.clone(); + let mut api = temp.lock().unwrap(); + drop(updater); + + let ts = tipset.clone(); + task::block_on(async move { + // updater.api.lock().unwrap().set_block_messages(&header, vec![message]); + api.set_heaviest_tipset(Arc::new(ts)).await; + }); + + // sleep allows for async block to update mpool's cur_tipset + sleep(Duration::new(2, 0)); + + let locked = mpool.lock().unwrap(); + let cur_ts = locked.cur_tipset.lock().unwrap().clone(); + assert_eq!(cur_ts, tipset); + } } From 0f41e5b92db68e8ca78210636b58d3e05863b2a5 Mon Sep 17 00:00:00 2001 From: flodesi Date: Sat, 27 Jun 2020 19:13:06 -0400 Subject: [PATCH 18/30] fmt --- Cargo.lock | 30 ++++++++++++++++++++++++++ blockchain/message_pool/src/msgpool.rs | 3 +-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b768634ad3a..e520e88c137d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2585,6 +2585,7 @@ dependencies = [ "futures 0.3.5", "futures-util", "futures_codec", + "ipld_blockstore", "libp2p", "log", "multihash 0.10.1", @@ -3779,6 +3780,35 @@ dependencies = [ "typenum", ] +[[package]] +name = "message_pool" +version = "0.1.0" +dependencies = [ + "async-std", + "blake2b_simd", + "chain", + "db", + "flo_stream", + "forest_address", + "forest_bigint", + "forest_blocks", + "forest_cid", + "forest_crypto", + "forest_encoding", + "forest_message", + "forest_vm", + "futures 0.3.5", + "interpreter", + "ipld_blockstore", + "key_management", + "libsecp256k1", + "log", + "lru 0.5.2", + "serde", + "state_tree", + "thiserror", +] + [[package]] name = "mime" version = "0.3.16" diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index a548c8b4ef76..88e62200e94a 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -201,8 +201,7 @@ where })); let mut mp_lock = mp.lock().unwrap(); - mp_lock - .load_local()?; + mp_lock.load_local()?; let mut api = mp_lock.api.lock().unwrap(); let mut subscriber = api.subscribe_head_changes(); drop(api); From f244be5111bdb3d8ef8f46df4130fa1a5f820357 Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 29 Jun 2020 12:21:14 -0400 Subject: [PATCH 19/30] added better error handling --- blockchain/message_pool/src/errors.rs | 14 ++-- blockchain/message_pool/src/msgpool.rs | 89 ++++++++++++++++++-------- 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index 658f0d731b9c..eb5d31d4e1ba 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -15,13 +15,15 @@ pub enum Error { MessageValueTooHigh, #[error("Message sequence too low")] SequenceTooLow, - #[error("not enough funds to execute transaction")] + #[error("Not enough funds to execute transaction")] NotEnoughFunds, - #[error("invalid to address for message")] + #[error("Invalid to address for message")] InvalidToAddr, - #[error("message with sequence already in mempool")] + #[error("Invalid from address")] + InvalidFromAddr, + #[error("Message with sequence already in mempool")] DuplicateSequence, - #[error("signature validation failed")] + #[error("Signature validation failed")] SigVerification, #[error("Unknown signature type")] UnknownSigType, @@ -29,8 +31,8 @@ pub enum Error { BLSSigTooShort, #[error("{0}")] Other(String), - #[error("{0}")] - MutexPoisonError(String), + #[error("Mutex is either poisoned or data inside could not be accessed at this current time")] + MutexPoisonError, } impl From for Error { diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 88e62200e94a..71d044362d6a 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -200,9 +200,9 @@ where local_msgs: HashMap::new(), })); - let mut mp_lock = mp.lock().unwrap(); + let mut mp_lock = mp.lock().map_err(|_| Error::MutexPoisonError)?; mp_lock.load_local()?; - let mut api = mp_lock.api.lock().unwrap(); + let mut api = mp_lock.api.lock().map_err(|_| Error::MutexPoisonError)?; let mut subscriber = api.subscribe_head_changes(); drop(api); drop(mp_lock); @@ -211,9 +211,10 @@ where task::spawn(async move { loop { if let Some(ts) = subscriber.next().await { - let mut lock = mpool.lock().unwrap(); - lock.head_change(Vec::new(), vec![ts.as_ref().clone()]) - .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); + if let Ok(mut lock) = mpool.lock() { + lock.head_change(Vec::new(), vec![ts.as_ref().clone()]) + .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); + } } sleep(Duration::new(1, 0)); } @@ -250,7 +251,11 @@ where self.verify_msg_sig(msg)?; let tmp = msg.clone(); - let tip = self.cur_tipset.lock().unwrap().clone(); + let tip = self + .cur_tipset + .lock() + .map_err(|_| Error::MutexPoisonError)? + .clone(); self.add_tipset(tmp, &tip) } @@ -323,7 +328,10 @@ where /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence pub fn get_sequence(&self, addr: &Address) -> Result { - let cur_ts = &self.cur_tipset.lock().unwrap(); + let cur_ts = &self + .cur_tipset + .lock() + .map_err(|_| Error::MutexPoisonError)?; let sequence = self.get_state_sequence(addr, cur_ts)?; match self.pending.get(addr) { @@ -339,10 +347,7 @@ where /// Get the state of the base_sequence for a given address in cur_ts fn get_state_sequence(&self, addr: &Address, cur_ts: &Tipset) -> Result { - let api = self - .api - .lock() - .map_err(|err| Error::Other(err.to_string()))?; + let api = self.api.lock().map_err(|_| Error::MutexPoisonError)?; let actor = api.state_get_actor(&addr, cur_ts)?; let mut base_sequence = actor.sequence; @@ -364,17 +369,17 @@ where /// Get the state balance for the actor that corresponds to the supplied address and tipset, /// if this actor does not exist, return an error fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { - let api = self - .api - .lock() - .map_err(|err| Error::Other(err.to_string()))?; + let api = self.api.lock().map_err(|_| Error::MutexPoisonError)?; let actor = api.state_get_actor(&addr, &ts)?; Ok(BigInt::from(actor.balance)) } /// Remove a message given a sequence and address from the messagepool pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { - let mset = self.pending.get_mut(from).unwrap(); + let mset = self + .pending + .get_mut(from) + .ok_or_else(|| Error::InvalidFromAddr)?; mset.msgs.remove(&sequence); if mset.msgs.is_empty() { @@ -396,12 +401,21 @@ where /// Return a tuple that contains a vector of all signed messages and the current tipset for /// self. - pub fn pending(&self) -> (Vec, Tipset) { + pub fn pending(&self) -> Result<(Vec, Tipset), Error> { let mut out: Vec = Vec::new(); for (addr, _) in self.pending.clone() { - out.append(self.pending_for(&addr).unwrap().as_mut()) - } - (out, self.cur_tipset.lock().unwrap().clone()) + out.append( + self.pending_for(&addr) + .ok_or_else(|| Error::InvalidFromAddr)? + .as_mut(), + ) + } + let cur_ts = self + .cur_tipset + .lock() + .map_err(|_| Error::MutexPoisonError)? + .clone(); + Ok((out, cur_ts)) } /// Return a Vector of signed messages for a given from address. This vector will be sorted by @@ -435,7 +449,11 @@ where ) -> Result, Error> { let mut msg_vec: Vec = Vec::new(); for block in blks { - let (umsg, mut smsgs) = self.api.lock().unwrap().messages_for_block(&block)?; + let (umsg, mut smsgs) = self + .api + .lock() + .map_err(|_| Error::MutexPoisonError)? + .messages_for_block(&block)?; msg_vec.append(smsgs.as_mut()); for msg in umsg { let smsg = self.recover_sig(msg)?; @@ -447,7 +465,10 @@ where /// Attempt to get the signed message given an unsigned message in message pool pub fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { - let val = self.bls_sig_cache.get(&msg.cid()?).unwrap(); + let val = self + .bls_sig_cache + .get(&msg.cid()?) + .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; Ok(SignedMessage::new_from_parts(msg, val.clone())) } @@ -503,10 +524,17 @@ where pub fn head_change(&mut self, revert: Vec, apply: Vec) -> Result<(), Error> { let mut rmsgs: HashMap> = HashMap::new(); for ts in revert { - let pts = self.api.lock().unwrap().load_tipset(ts.parents())?; + let pts = self + .api + .lock() + .map_err(|_| Error::MutexPoisonError)? + .load_tipset(ts.parents())?; let msgs = self.messages_for_blocks(ts.blocks())?; let parent = pts.clone(); - *self.cur_tipset.lock().unwrap() = parent; + *self + .cur_tipset + .lock() + .map_err(|_| Error::MutexPoisonError)? = parent; for msg in msgs { add(msg, rmsgs.borrow_mut()); } @@ -514,7 +542,11 @@ where for ts in apply { for b in ts.blocks() { - let (msgs, smsgs) = self.api.lock().unwrap().messages_for_block(b).unwrap(); + let (msgs, smsgs) = self + .api + .lock() + .map_err(|_| Error::MutexPoisonError)? + .messages_for_block(b)?; for msg in smsgs { self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); } @@ -523,7 +555,10 @@ where self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); } } - *self.cur_tipset.lock().unwrap() = ts; + *self + .cur_tipset + .lock() + .map_err(|_| Error::MutexPoisonError)? = ts; } for (_, hm) in rmsgs { @@ -859,7 +894,7 @@ mod tests { .unwrap(); assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - let (p, _) = mpool_locked.pending(); + let (p, _) = mpool_locked.pending().unwrap(); assert_eq!(p.len(), 3); } From 4d64d97cb5dcea572fb10c4ab6d8b38ab2cdc5df Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 30 Jun 2020 19:59:25 -0400 Subject: [PATCH 20/30] removed mutex around messagepool, and cleaned up code --- blockchain/message_pool/Cargo.toml | 2 +- blockchain/message_pool/src/msgpool.rs | 554 ++++++++++++++++--------- 2 files changed, 349 insertions(+), 207 deletions(-) diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index 4b9cc8ab447b..aadda1a7140c 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -21,11 +21,11 @@ state_tree = { path = "../../vm/state_tree/" } serde = { version = "1.0", features = ["derive"] } db = { path = "../../node/db" } flo_stream = "0.4.0" -async-std = "1.6.0" futures = "0.3.5" libsecp256k1 = "0.3.4" blake2b_simd = "0.5.10" log = "0.4.8" +async-std = { version = "1.6.0", features = ["unstable"] } [dev-dependencies] interpreter = { path = "../../vm/interpreter/" } diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 71d044362d6a..4836a309ee9c 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -4,6 +4,7 @@ use super::errors::Error; use address::Address; use async_std::task; +use async_std::sync::RwLock; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; use chain::ChainStore; @@ -20,11 +21,13 @@ use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, MutexGuard}; use std::thread::sleep; use std::time::Duration; use vm::ActorState; +// use blake2b_simd::Hash; + const REPLACE_BY_FEE_RATIO: f32 = 1.25; const RBF_NUM: u64 = ((REPLACE_BY_FEE_RATIO - 1f32) * 256f32) as u64; const RBF_DENOM: u64 = 256; @@ -159,13 +162,13 @@ where /// This is the main MessagePool struct pub struct MessagePool { local_addrs: Vec
, - pending: HashMap, - pub cur_tipset: Mutex, + pending: Arc>>, // mutex this + pub cur_tipset: Arc>, api: Arc>, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, - bls_sig_cache: LruCache, + bls_sig_cache: Arc>>, // mutex this sig_val_cache: LruCache, local_msgs: HashMap, SignedMessage>, } @@ -175,21 +178,21 @@ where T: Provider + std::marker::Send, { /// Create a new message pool - pub fn new(mut api: T, network_name: String) -> Result>>, Error> + pub fn new(mut api: T, network_name: String) -> Result, Error> where T: Provider, { // LruCache sizes have been taken from the lotus implementation - let tipset = Mutex::new( - api.get_heaviest_tipset() - .ok_or_else(|| Error::Other("No ts in api to set as cur_tipset".to_owned()))?, - ); - let bls_sig_cache = LruCache::new(40000); + let pending = Arc::new(Mutex::new(HashMap::new())); + let tipset = Arc::new(Mutex::new(api.get_heaviest_tipset().ok_or_else(|| { + Error::Other("No ts in api to set as cur_tipset".to_owned()) + })?)); + let bls_sig_cache = Arc::new(Mutex::new(LruCache::new(40000))); let sig_val_cache = LruCache::new(32000); let api_mutex = Arc::new(Mutex::new(api)); - let mp = Arc::new(Mutex::new(MessagePool { + let mut mp = MessagePool { local_addrs: Vec::new(), - pending: HashMap::new(), + pending, cur_tipset: tipset, api: api_mutex, min_gas_price: Default::default(), @@ -198,23 +201,32 @@ where bls_sig_cache, sig_val_cache, local_msgs: HashMap::new(), - })); + }; - let mut mp_lock = mp.lock().map_err(|_| Error::MutexPoisonError)?; - mp_lock.load_local()?; - let mut api = mp_lock.api.lock().map_err(|_| Error::MutexPoisonError)?; + mp.load_local()?; + // let mut mp_lock = mp.lock().map_err(|_| Error::MutexPoisonError)?; + // mp_lock.load_local()?; + let mut api = mp.api.lock().map_err(|_| Error::MutexPoisonError)?; let mut subscriber = api.subscribe_head_changes(); drop(api); - drop(mp_lock); - let mpool = mp.clone(); + let api = mp.api.clone(); + let bls_sig_cache = mp.bls_sig_cache.clone(); + let pending = mp.pending.clone(); + let cur_tipset = mp.cur_tipset.clone(); + task::spawn(async move { loop { if let Some(ts) = subscriber.next().await { - if let Ok(mut lock) = mpool.lock() { - lock.head_change(Vec::new(), vec![ts.as_ref().clone()]) - .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); - } + head_change( + api.clone(), + bls_sig_cache.clone(), + pending.clone(), + cur_tipset.clone(), + Vec::new(), + vec![ts.as_ref().clone()], + ) + .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); } sleep(Duration::new(1, 0)); } @@ -249,7 +261,6 @@ where } self.verify_msg_sig(msg)?; - let tmp = msg.clone(); let tip = self .cur_tipset @@ -289,6 +300,7 @@ where } let balance = self.get_state_balance(msg.from(), cur_ts)?; + let msg_balance = BigInt::from(msg.message().required_funds()); if balance < msg_balance { return Err(Error::NotEnoughFunds); @@ -298,14 +310,17 @@ where /// Finish verifying signed message before adding it to the pending mset hashmap. If an entry /// in the hashmap does not yet exist, create a new mset that will correspond to the from message - /// and push it to the pending hashmap + /// and push it to the pending phashmap fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { let api = self .api .lock() .map_err(|err| Error::Other(err.to_string()))?; if msg.signature().signature_type() == SignatureType::BLS { - self.bls_sig_cache.put(msg.cid()?, msg.signature().clone()); + self.bls_sig_cache + .lock() + .map_err(|_| Error::MutexPoisonError)? + .put(msg.cid()?, msg.signature().clone()); } if msg.message().gas_limit() > 100_000_000 { return Err(Error::Other( @@ -314,13 +329,15 @@ where } api.put_message(&msg)?; - let msett = self.pending.get_mut(msg.message().from()); + let mut pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; + let msett = pending.get_mut(msg.message().from()); + // let msett = self.pending.get_mut(msg.message().from()); match msett { Some(mset) => mset.add(&msg)?, None => { let mut mset = MsgSet::new(); mset.add(&msg)?; - self.pending.insert(*msg.message().from(), mset); + pending.insert(*msg.message().from(), mset); } } Ok(()) @@ -334,7 +351,10 @@ where .map_err(|_| Error::MutexPoisonError)?; let sequence = self.get_state_sequence(addr, cur_ts)?; - match self.pending.get(addr) { + let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; + let msgset = pending.get(addr); + + match msgset { Some(mset) => { if sequence > mset.next_sequence { return Ok(sequence); @@ -376,34 +396,18 @@ where /// Remove a message given a sequence and address from the messagepool pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { - let mset = self - .pending - .get_mut(from) - .ok_or_else(|| Error::InvalidFromAddr)?; - mset.msgs.remove(&sequence); - - if mset.msgs.is_empty() { - self.pending.remove(from); - } else { - let mut max_sequence: u64 = 0; - for sequence_temp in mset.msgs.keys().cloned() { - if max_sequence < sequence_temp { - max_sequence = sequence_temp; - } - } - if max_sequence < sequence { - max_sequence = sequence; - } - mset.next_sequence = max_sequence + 1; - } - Ok(()) + let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; + remove(from, pending, sequence) } /// Return a tuple that contains a vector of all signed messages and the current tipset for /// self. pub fn pending(&self) -> Result<(Vec, Tipset), Error> { let mut out: Vec = Vec::new(); - for (addr, _) in self.pending.clone() { + let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; + let pending_hm = pending.clone(); + drop(pending); + for (addr, _) in pending_hm { out.append( self.pending_for(&addr) .ok_or_else(|| Error::InvalidFromAddr)? @@ -421,7 +425,12 @@ where /// Return a Vector of signed messages for a given from address. This vector will be sorted by /// each messsage's sequence. If no corresponding messages found, return None result type fn pending_for(&self, a: &Address) -> Option> { - let mset = self.pending.get(a); + let pending = self + .pending + .lock() + .map_err(|_| Error::MutexPoisonError) + .ok()?; + let mset = pending.get(a); match mset { Some(msgset) => { if msgset.msgs.is_empty() { @@ -465,8 +474,11 @@ where /// Attempt to get the signed message given an unsigned message in message pool pub fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { - let val = self + let mut bls_sig_cache = self .bls_sig_cache + .lock() + .map_err(|_| Error::MutexPoisonError)?; + let val = bls_sig_cache .get(&msg.cid()?) .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; Ok(SignedMessage::new_from_parts(msg, val.clone())) @@ -497,77 +509,183 @@ where } Ok(()) } +} - /// This is a helper method for head_change. This method will remove a sequence for a from address - /// from the rmsgs hashmap. Also remove the from address and sequence from the mmessagepool. - fn rm( - &mut self, - from: &Address, - sequence: u64, - rmsgs: &mut HashMap>, - ) { - let s = rmsgs.get_mut(from); - if s.is_none() { - self.remove(from, sequence).ok(); - return; - } - let temp = s.unwrap(); - if temp.get_mut(&sequence).is_some() { - temp.remove(&sequence); - return; - } - self.remove(from, sequence).ok(); - } - - /// This function will revert and/or apply tipsets to the message pool. This function should be - /// called every time that there is a head change in the message pool - pub fn head_change(&mut self, revert: Vec, apply: Vec) -> Result<(), Error> { - let mut rmsgs: HashMap> = HashMap::new(); - for ts in revert { - let pts = self - .api - .lock() - .map_err(|_| Error::MutexPoisonError)? - .load_tipset(ts.parents())?; - let msgs = self.messages_for_blocks(ts.blocks())?; - let parent = pts.clone(); - *self - .cur_tipset - .lock() - .map_err(|_| Error::MutexPoisonError)? = parent; - for msg in msgs { - add(msg, rmsgs.borrow_mut()); +pub fn remove( + from: &Address, + mut pending: MutexGuard>, + sequence: u64, +) -> Result<(), Error> { + let mset = pending + .get_mut(from) + .ok_or_else(|| Error::InvalidFromAddr)?; + mset.msgs.remove(&sequence); + + if mset.msgs.is_empty() { + pending.remove(from); + } else { + let mut max_sequence: u64 = 0; + for sequence_temp in mset.msgs.keys().cloned() { + if max_sequence < sequence_temp { + max_sequence = sequence_temp; } } + if max_sequence < sequence { + max_sequence = sequence; + } + mset.next_sequence = max_sequence + 1; + } + Ok(()) +} - for ts in apply { - for b in ts.blocks() { - let (msgs, smsgs) = self - .api - .lock() - .map_err(|_| Error::MutexPoisonError)? - .messages_for_block(b)?; - for msg in smsgs { - self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); - } +fn recover_sig( + mut bls_sig_cache: MutexGuard>, + msg: UnsignedMessage, +) -> Result { + let val = bls_sig_cache + .get(&msg.cid()?) + .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; + Ok(SignedMessage::new_from_parts(msg, val.clone())) +} - for msg in msgs { - self.rm(msg.from(), msg.sequence(), rmsgs.borrow_mut()); - } +/// Finish verifying signed message before adding it to the pending mset hashmap. If an entry +/// in the hashmap does not yet exist, create a new mset that will correspond to the from message +/// and push it to the pending hashmap +fn add_helper( + api: MutexGuard, + mut bls_sig_cache: MutexGuard>, + mut pending: MutexGuard>, + msg: SignedMessage, +) -> Result<(), Error> +where + T: Provider, +{ + if msg.signature().signature_type() == SignatureType::BLS { + bls_sig_cache.put(msg.cid()?, msg.signature().clone()); + } + if msg.message().gas_limit() > 100_000_000 { + return Err(Error::Other( + "given message has too high of a gas limit".to_string(), + )); + } + api.put_message(&msg)?; + let msett = pending.get_mut(msg.message().from()); + match msett { + Some(mset) => mset.add(&msg)?, + None => { + let mut mset = MsgSet::new(); + mset.add(&msg)?; + pending.insert(*msg.message().from(), mset); + } + } + Ok(()) +} + +/// This function will revert and/or apply tipsets to the message pool. This function should be +/// called every time that there is a head change in the message pool +pub fn head_change( + api: Arc>, + bls_sig_cache: Arc>>, + pending: Arc>>, + cur_tipset: Arc>, + revert: Vec, + apply: Vec, +) -> Result<(), Error> +where + T: Provider, +{ + let mut rmsgs: HashMap> = HashMap::new(); + for ts in revert { + let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let pts = api_locked.load_tipset(ts.parents())?; + drop(api_locked); + + let mut msgs: Vec = Vec::new(); + for block in ts.blocks() { + let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let (umsg, mut smsgs) = api_locked.messages_for_block(&block)?; + drop(api_locked); + msgs.append(smsgs.as_mut()); + for msg in umsg { + let smsg = recover_sig( + bls_sig_cache.lock().map_err(|_| Error::MutexPoisonError)?, + msg, + )?; + msgs.push(smsg) } - *self - .cur_tipset - .lock() - .map_err(|_| Error::MutexPoisonError)? = ts; } + // let msgs = self.messages_for_blocks(ts.blocks())?; + let parent = pts.clone(); + let mut cur_ts_locked = cur_tipset.lock().map_err(|_| Error::MutexPoisonError)?; + *cur_ts_locked = parent; + drop(cur_ts_locked); + + for msg in msgs { + add(msg, rmsgs.borrow_mut()); + } + } - for (_, hm) in rmsgs { - for (_, msg) in hm { - self.add_skip_checks(msg).ok(); + for ts in apply { + for b in ts.blocks() { + let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let (msgs, smsgs) = api_locked.messages_for_block(b)?; + drop(api_locked); + for msg in smsgs { + rm( + msg.from(), + pending.lock().map_err(|_| Error::MutexPoisonError)?, + msg.sequence(), + rmsgs.borrow_mut(), + ); + } + for msg in msgs { + rm( + msg.from(), + pending.lock().map_err(|_| Error::MutexPoisonError)?, + msg.sequence(), + rmsgs.borrow_mut(), + ); } } - Ok(()) + let mut cur_ts_locked = cur_tipset.lock().map_err(|_| Error::MutexPoisonError)?; + *cur_ts_locked = ts; + drop(cur_ts_locked); + } + for (_, hm) in rmsgs { + for (_, msg) in hm { + let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let pending = pending.lock().map_err(|_| Error::MutexPoisonError)?; + add_helper( + api_locked, + bls_sig_cache.lock().map_err(|_| Error::MutexPoisonError)?, + pending, + msg.clone(), + ) + .ok(); + } + } + Ok(()) +} + +/// This is a helper method for head_change. This method will remove a sequence for a from address +/// from the rmsgs hashmap. Also remove the from address and sequence from the mmessagepool. +fn rm( + from: &Address, + pending: MutexGuard>, + sequence: u64, + rmsgs: &mut HashMap>, +) { + let s = rmsgs.get_mut(from); + if s.is_none() { + remove(from, pending, sequence).ok(); + return; + } + let temp = s.unwrap(); + if temp.get_mut(&sequence).is_some() { + temp.remove(&sequence); + return; } + remove(from, pending, sequence).ok(); } /// This function is a helper method for head_change. This method will add a signed message to the given rmsgs HashMap @@ -785,8 +903,7 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_sequence(&sender, 0); - let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); - let mut mpool_locked = mpool.lock().unwrap(); + let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); let mut smsg_vec = Vec::new(); for i in 0..4 { @@ -794,39 +911,47 @@ mod tests { smsg_vec.push(msg); } - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 0); - mpool_locked.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 1); - mpool_locked.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 2); - mpool_locked.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 3); - mpool_locked.push(&smsg_vec[3]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); + mpool.push(&smsg_vec[0]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); + mpool.push(&smsg_vec[1]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); + mpool.push(&smsg_vec[2]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); + mpool.push(&smsg_vec[3]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); let a = mock_block(1, 1); - mpool_locked - .api - .lock() - .unwrap() - .set_block_messages(&a, smsg_vec); - mpool_locked - .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) - .unwrap(); - - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); + mpool.api.lock().unwrap().set_block_messages(&a, smsg_vec); + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); - drop(mpool_locked); - assert_eq!(mpool.lock().unwrap().get_sequence(&sender).unwrap(), 4); + head_change( + api, + bls_sig_cache, + pending, + cur_tipset, + Vec::new(), + vec![Tipset::new(vec![a]).unwrap()], + ) + .unwrap(); + // mpool + // .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) + // .unwrap(); + + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); } #[test] fn test_revert_messages() { let tma = TestApi::new(); let mut wallet = Wallet::new(MemKeyStore::new()); - let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); - let mut mpool_locked = mpool.lock().unwrap(); + let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); let a = mock_block(1, 1); let tipset = Tipset::new(vec![a.clone()]).unwrap(); @@ -842,59 +967,82 @@ mod tests { smsg_vec.push(msg); } - mpool_locked - .api - .lock() - .unwrap() - .set_block_messages(&a, vec![smsg_vec[0].clone()]); - mpool_locked - .api - .lock() - .unwrap() - .set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); - - mpool_locked - .api - .lock() - .unwrap() - .set_state_sequence(&sender, 0); - - mpool_locked.add(&smsg_vec[0]).unwrap(); - mpool_locked.add(&smsg_vec[1]).unwrap(); - mpool_locked.add(&smsg_vec[2]).unwrap(); - mpool_locked.add(&smsg_vec[3]).unwrap(); - - mpool_locked - .api - .lock() - .unwrap() - .set_state_sequence(&sender, 0); - mpool_locked - .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) - .unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - - mpool_locked - .api - .lock() - .unwrap() - .set_state_sequence(&sender, 1); - mpool_locked - .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) - .unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - - mpool_locked - .api - .lock() - .unwrap() - .set_state_sequence(&sender, 0); - mpool_locked - .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) - .unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 4); - - let (p, _) = mpool_locked.pending().unwrap(); + let mut api_temp = mpool.api.lock().unwrap(); + api_temp.set_block_messages(&a, vec![smsg_vec[0].clone()]); + api_temp.set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); + api_temp.set_state_sequence(&sender, 0); + + drop(api_temp); + + mpool.add(&smsg_vec[0]).unwrap(); + mpool.add(&smsg_vec[1]).unwrap(); + mpool.add(&smsg_vec[2]).unwrap(); + mpool.add(&smsg_vec[3]).unwrap(); + + mpool.api.lock().unwrap().set_state_sequence(&sender, 0); + + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); + + head_change( + api.clone(), + bls_sig_cache.clone(), + pending.clone(), + cur_tipset.clone(), + Vec::new(), + vec![Tipset::new(vec![a]).unwrap()], + ) + .unwrap(); + + // mpool + // .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) + // .unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + + mpool.api.lock().unwrap().set_state_sequence(&sender, 1); + + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); + + head_change( + api.clone(), + bls_sig_cache.clone(), + pending.clone(), + cur_tipset.clone(), + Vec::new(), + vec![Tipset::new(vec![b.clone()]).unwrap()], + ) + .unwrap(); + + // mpool + // .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) + // .unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + + let mut api_temp = mpool.api.lock().unwrap(); + api_temp.set_state_sequence(&sender, 0); + drop(api_temp); + + head_change( + api, + bls_sig_cache, + pending, + cur_tipset, + vec![Tipset::new(vec![b]).unwrap()], + Vec::new(), + ) + .unwrap(); + + // mpool + // .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) + // .unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + + let (p, _) = mpool.pending().unwrap(); assert_eq!(p.len(), 3); } @@ -908,8 +1056,7 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_sequence(&sender, 0); - let mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); - let mut mpool_locked = mpool.lock().unwrap(); + let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); let mut smsg_vec = Vec::new(); for i in 0..3 { @@ -917,23 +1064,19 @@ mod tests { smsg_vec.push(msg); } - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 0); - mpool_locked.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 1); - mpool_locked.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 2); - mpool_locked.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool_locked.get_sequence(&sender).unwrap(), 3); - - drop(mpool_locked); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); + mpool.push(&smsg_vec[0]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); + mpool.push(&smsg_vec[1]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); + mpool.push(&smsg_vec[2]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); let header = mock_block(1, 1); let tipset = Tipset::new(vec![header.clone()]).unwrap(); - let updater = mpool.lock().unwrap(); - let temp = updater.api.clone(); + let temp = mpool.api.clone(); let mut api = temp.lock().unwrap(); - drop(updater); let ts = tipset.clone(); task::block_on(async move { @@ -944,8 +1087,7 @@ mod tests { // sleep allows for async block to update mpool's cur_tipset sleep(Duration::new(2, 0)); - let locked = mpool.lock().unwrap(); - let cur_ts = locked.cur_tipset.lock().unwrap().clone(); + let cur_ts = mpool.cur_tipset.lock().unwrap().clone(); assert_eq!(cur_ts, tipset); } } From ca23f23eb32a5ddf80e3f643b2bd838d592e703c Mon Sep 17 00:00:00 2001 From: flodesi Date: Wed, 1 Jul 2020 15:44:26 -0400 Subject: [PATCH 21/30] rm unneeded mutex and cleaned up msgs for ts --- blockchain/chain/src/store/chain_store.rs | 33 ++++++++++++----------- blockchain/message_pool/src/msgpool.rs | 6 ++--- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/blockchain/chain/src/store/chain_store.rs b/blockchain/chain/src/store/chain_store.rs index 451f3ab44a6b..49bf0febc575 100644 --- a/blockchain/chain/src/store/chain_store.rs +++ b/blockchain/chain/src/store/chain_store.rs @@ -200,23 +200,26 @@ where } Ok(()) } +} - /// Return the messages in the chainstore for a given tipset - pub fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { - let mut umsg: Vec = Vec::new(); - let mut msgs: Vec = Vec::new(); - for bh in h.blocks().iter() { - let (mut bh_umsg_tmp, mut bh_msg_tmp) = block_messages(self.blockstore(), bh)?; - let bh_umsg = &mut bh_umsg_tmp; - let bh_msg = &mut bh_msg_tmp; - umsg.append(bh_umsg); - msgs.append(bh_msg); - } - for msg in msgs { - umsg.push(msg.into_message()); - } - Ok(umsg) +/// Returns messages for a given tipset from db +pub fn messages_for_tipset(db: &DB, h: &Tipset) -> Result, Error> +where + DB: BlockStore, +{ + let mut umsg: Vec = Vec::new(); + let mut msgs: Vec = Vec::new(); + for bh in h.blocks().iter() { + let (mut bh_umsg_tmp, mut bh_msg_tmp) = block_messages(db, bh)?; + let bh_umsg = &mut bh_umsg_tmp; + let bh_msg = &mut bh_msg_tmp; + umsg.append(bh_umsg); + msgs.append(bh_msg); + } + for msg in msgs { + umsg.push(msg.into_message()); } + Ok(umsg) } /// Returns a Tuple of bls messages of type UnsignedMessage and secp messages diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 4836a309ee9c..8a7d072da093 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -4,10 +4,10 @@ use super::errors::Error; use address::Address; use async_std::task; -use async_std::sync::RwLock; +// use async_std::sync::RwLock; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; -use chain::ChainStore; +use chain::{messages_for_tipset, ChainStore}; use cid::multihash::Blake2b256; use cid::Cid; use crypto::{Signature, SignatureType}; @@ -151,7 +151,7 @@ where } fn messages_for_tipset(&self, h: &Tipset) -> Result, Error> { - self.cs.messages_for_tipset(h).map_err(|err| err.into()) + messages_for_tipset(self.cs.blockstore(), h).map_err(|err| err.into()) } fn load_tipset(&self, tsk: &TipsetKeys) -> Result { From 9801e44fd27535ad9b4efea30a4ea69195fd8688 Mon Sep 17 00:00:00 2001 From: flodesi Date: Wed, 1 Jul 2020 19:09:17 -0400 Subject: [PATCH 22/30] replaced mutex with rwlock in mpool --- blockchain/message_pool/Cargo.toml | 2 +- blockchain/message_pool/src/errors.rs | 2 - blockchain/message_pool/src/msgpool.rs | 546 ++++++++++++------------- 3 files changed, 260 insertions(+), 290 deletions(-) diff --git a/blockchain/message_pool/Cargo.toml b/blockchain/message_pool/Cargo.toml index aadda1a7140c..23bfad93bdf2 100644 --- a/blockchain/message_pool/Cargo.toml +++ b/blockchain/message_pool/Cargo.toml @@ -25,7 +25,7 @@ futures = "0.3.5" libsecp256k1 = "0.3.4" blake2b_simd = "0.5.10" log = "0.4.8" -async-std = { version = "1.6.0", features = ["unstable"] } +async-std = "1.6.0" [dev-dependencies] interpreter = { path = "../../vm/interpreter/" } diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index eb5d31d4e1ba..156d68a12db9 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -31,8 +31,6 @@ pub enum Error { BLSSigTooShort, #[error("{0}")] Other(String), - #[error("Mutex is either poisoned or data inside could not be accessed at this current time")] - MutexPoisonError, } impl From for Error { diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 8a7d072da093..1620f4321e1a 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,8 +3,8 @@ use super::errors::Error; use address::Address; +use async_std::sync::{Arc, RwLock}; use async_std::task; -// use async_std::sync::RwLock; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; use chain::{messages_for_tipset, ChainStore}; @@ -21,13 +21,10 @@ use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; use std::collections::HashMap; -use std::sync::{Arc, Mutex, MutexGuard}; use std::thread::sleep; use std::time::Duration; use vm::ActorState; -// use blake2b_simd::Hash; - const REPLACE_BY_FEE_RATIO: f32 = 1.25; const RBF_NUM: u64 = ((REPLACE_BY_FEE_RATIO - 1f32) * 256f32) as u64; const RBF_DENOM: u64 = 256; @@ -162,34 +159,35 @@ where /// This is the main MessagePool struct pub struct MessagePool { local_addrs: Vec
, - pending: Arc>>, // mutex this - pub cur_tipset: Arc>, - api: Arc>, + pending: Arc>>, // mutex this + pub cur_tipset: Arc>, + api: Arc>, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, - bls_sig_cache: Arc>>, // mutex this + bls_sig_cache: Arc>>, // mutex this sig_val_cache: LruCache, local_msgs: HashMap, SignedMessage>, } impl MessagePool where - T: Provider + std::marker::Send, + T: Provider + std::marker::Send + std::marker::Sync + 'static, { /// Create a new message pool - pub fn new(mut api: T, network_name: String) -> Result, Error> + pub async fn new(mut api: T, network_name: String) -> Result, Error> where T: Provider, { // LruCache sizes have been taken from the lotus implementation - let pending = Arc::new(Mutex::new(HashMap::new())); - let tipset = Arc::new(Mutex::new(api.get_heaviest_tipset().ok_or_else(|| { + let pending = Arc::new(RwLock::new(HashMap::new())); + let tipset = Arc::new(RwLock::new(api.get_heaviest_tipset().ok_or_else(|| { Error::Other("No ts in api to set as cur_tipset".to_owned()) })?)); - let bls_sig_cache = Arc::new(Mutex::new(LruCache::new(40000))); + let bls_sig_cache = Arc::new(RwLock::new(LruCache::new(40000))); let sig_val_cache = LruCache::new(32000); - let api_mutex = Arc::new(Mutex::new(api)); + let api_mutex = Arc::new(RwLock::new(api)); + let mut mp = MessagePool { local_addrs: Vec::new(), pending, @@ -203,10 +201,9 @@ where local_msgs: HashMap::new(), }; - mp.load_local()?; - // let mut mp_lock = mp.lock().map_err(|_| Error::MutexPoisonError)?; - // mp_lock.load_local()?; - let mut api = mp.api.lock().map_err(|_| Error::MutexPoisonError)?; + mp.load_local().await?; + + let mut api = mp.api.write().await; let mut subscriber = api.subscribe_head_changes(); drop(api); @@ -226,6 +223,7 @@ where Vec::new(), vec![ts.as_ref().clone()], ) + .await .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); } sleep(Duration::new(1, 0)); @@ -242,16 +240,16 @@ where } /// Push a signed message to the MessagePool - pub fn push(&mut self, msg: &SignedMessage) -> Result { + pub async fn push(&mut self, msg: &SignedMessage) -> Result { let msg_serial = msg.marshal_cbor()?; - self.add(msg)?; + self.add(msg).await?; self.add_local(msg, msg_serial)?; msg.cid().map_err(|err| err.into()) } /// This is a helper to push that will help to make sure that the message fits the parameters /// to be pushed to the MessagePool - pub fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { + pub async fn add(&mut self, msg: &SignedMessage) -> Result<(), Error> { let size = msg.marshal_cbor()?.len(); if size > 32 * 1024 { return Err(Error::MessageTooBig); @@ -262,98 +260,80 @@ where self.verify_msg_sig(msg)?; let tmp = msg.clone(); - let tip = self - .cur_tipset - .lock() - .map_err(|_| Error::MutexPoisonError)? - .clone(); - self.add_tipset(tmp, &tip) + + let tip_locked = self.cur_tipset.read().await; + let tip = tip_locked.clone(); + drop(tip_locked); + + self.add_tipset(tmp, &tip).await } /// Add a SignedMessage without doing any of the checks - pub fn add_skip_checks(&mut self, m: SignedMessage) -> Result<(), Error> { - self.add_helper(m) + pub async fn add_skip_checks(&mut self, m: SignedMessage) -> Result<(), Error> { + self.add_helper(m).await } /// Verify the message signature. first check if it has already been verified and put into /// cache. If it has not, then manually verify it then put it into cache for future use fn verify_msg_sig(&mut self, msg: &SignedMessage) -> Result<(), Error> { let cid = msg.cid()?; + if let Some(()) = self.sig_val_cache.get(&cid) { return Ok(()); } + let umsg = msg.message().marshal_cbor()?; msg.signature() .verify(umsg.as_slice(), msg.from()) .map_err(Error::Other)?; + self.sig_val_cache.put(cid, ()); + Ok(()) } /// Verify the state_sequence and balance for the sender of the message given then call add_locked /// to finish adding the signed_message to pending - fn add_tipset(&mut self, msg: SignedMessage, cur_ts: &Tipset) -> Result<(), Error> { - let sequence = self.get_state_sequence(msg.from(), cur_ts)?; + async fn add_tipset(&mut self, msg: SignedMessage, cur_ts: &Tipset) -> Result<(), Error> { + let sequence = self.get_state_sequence(msg.from(), cur_ts).await?; if sequence > msg.message().sequence() { return Err(Error::SequenceTooLow); } - let balance = self.get_state_balance(msg.from(), cur_ts)?; + let balance = self.get_state_balance(msg.from(), cur_ts).await?; let msg_balance = BigInt::from(msg.message().required_funds()); if balance < msg_balance { return Err(Error::NotEnoughFunds); } - self.add_helper(msg) + self.add_helper(msg).await } /// Finish verifying signed message before adding it to the pending mset hashmap. If an entry /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending phashmap - fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { - let api = self - .api - .lock() - .map_err(|err| Error::Other(err.to_string()))?; - if msg.signature().signature_type() == SignatureType::BLS { - self.bls_sig_cache - .lock() - .map_err(|_| Error::MutexPoisonError)? - .put(msg.cid()?, msg.signature().clone()); - } - if msg.message().gas_limit() > 100_000_000 { - return Err(Error::Other( - "given message has too high of a gas limit".to_string(), - )); - } - api.put_message(&msg)?; - - let mut pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; - let msett = pending.get_mut(msg.message().from()); - // let msett = self.pending.get_mut(msg.message().from()); - match msett { - Some(mset) => mset.add(&msg)?, - None => { - let mut mset = MsgSet::new(); - mset.add(&msg)?; - pending.insert(*msg.message().from(), mset); - } - } - Ok(()) + async fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { + add_helper( + self.api.clone(), + self.bls_sig_cache.clone(), + self.pending.clone(), + msg, + ) + .await } /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence - pub fn get_sequence(&self, addr: &Address) -> Result { - let cur_ts = &self - .cur_tipset - .lock() - .map_err(|_| Error::MutexPoisonError)?; - let sequence = self.get_state_sequence(addr, cur_ts)?; - - let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; - let msgset = pending.get(addr); + pub async fn get_sequence(&self, addr: &Address) -> Result { + let cur_t = self.cur_tipset.read().await; + let cur_ts = cur_t.clone(); + drop(cur_t); + + let sequence = self.get_state_sequence(addr, &cur_ts).await?; + + let pending = self.pending.read().await; + let msgset = pending.get(addr); match msgset { Some(mset) => { if sequence > mset.next_sequence { @@ -366,15 +346,15 @@ where } /// Get the state of the base_sequence for a given address in cur_ts - fn get_state_sequence(&self, addr: &Address, cur_ts: &Tipset) -> Result { - let api = self.api.lock().map_err(|_| Error::MutexPoisonError)?; + async fn get_state_sequence(&self, addr: &Address, cur_ts: &Tipset) -> Result { + let api = self.api.read().await; let actor = api.state_get_actor(&addr, cur_ts)?; - let mut base_sequence = actor.sequence; - // TODO here lotus has a todo, so I guess we should eventually remove cur_ts from one // of the params for this method and just use the chain head let msgs = api.messages_for_tipset(cur_ts)?; + drop(api); + for m in msgs { if m.from() == addr { if m.sequence() != base_sequence { @@ -383,53 +363,53 @@ where base_sequence += 1; } } + Ok(base_sequence) } /// Get the state balance for the actor that corresponds to the supplied address and tipset, /// if this actor does not exist, return an error - fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { - let api = self.api.lock().map_err(|_| Error::MutexPoisonError)?; + async fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { + let api = self.api.read().await; let actor = api.state_get_actor(&addr, &ts)?; Ok(BigInt::from(actor.balance)) } /// Remove a message given a sequence and address from the messagepool - pub fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { - let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; - remove(from, pending, sequence) + pub async fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { + remove(from, self.pending.clone(), sequence).await } /// Return a tuple that contains a vector of all signed messages and the current tipset for /// self. - pub fn pending(&self) -> Result<(Vec, Tipset), Error> { + pub async fn pending(&self) -> Result<(Vec, Tipset), Error> { let mut out: Vec = Vec::new(); - let pending = self.pending.lock().map_err(|_| Error::MutexPoisonError)?; + + let pending = self.pending.read().await; let pending_hm = pending.clone(); drop(pending); + for (addr, _) in pending_hm { out.append( self.pending_for(&addr) + .await .ok_or_else(|| Error::InvalidFromAddr)? .as_mut(), ) } - let cur_ts = self - .cur_tipset - .lock() - .map_err(|_| Error::MutexPoisonError)? - .clone(); + + let cur_t = self.cur_tipset.read().await; + let cur_ts = cur_t.clone(); + drop(cur_t); + Ok((out, cur_ts)) } /// Return a Vector of signed messages for a given from address. This vector will be sorted by /// each messsage's sequence. If no corresponding messages found, return None result type - fn pending_for(&self, a: &Address) -> Option> { - let pending = self - .pending - .lock() - .map_err(|_| Error::MutexPoisonError) - .ok()?; + async fn pending_for(&self, a: &Address) -> Option> { + let pending = self.pending.read().await; + let mset = pending.get(a); match mset { Some(msgset) => { @@ -452,38 +432,26 @@ where } /// Return Vector of signed messages given a block header for self - pub fn messages_for_blocks( + pub async fn messages_for_blocks( &mut self, blks: &[BlockHeader], ) -> Result, Error> { let mut msg_vec: Vec = Vec::new(); + for block in blks { - let (umsg, mut smsgs) = self - .api - .lock() - .map_err(|_| Error::MutexPoisonError)? - .messages_for_block(&block)?; + let api = self.api.read().await; + let (umsg, mut smsgs) = api.messages_for_block(&block)?; + drop(api); + msg_vec.append(smsgs.as_mut()); for msg in umsg { - let smsg = self.recover_sig(msg)?; + let smsg = recover_sig(self.bls_sig_cache.clone(), msg).await?; msg_vec.push(smsg) } } Ok(msg_vec) } - /// Attempt to get the signed message given an unsigned message in message pool - pub fn recover_sig(&mut self, msg: UnsignedMessage) -> Result { - let mut bls_sig_cache = self - .bls_sig_cache - .lock() - .map_err(|_| Error::MutexPoisonError)?; - let val = bls_sig_cache - .get(&msg.cid()?) - .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; - Ok(SignedMessage::new_from_parts(msg, val.clone())) - } - /// Return gas price estimate this has been translated from lotus, a more smart implementation will /// most likely need to be implemented pub fn estimate_gas_price(&self, nblocksincl: u64) -> Result { @@ -498,9 +466,9 @@ where /// Load local messages into pending. As of right now messages are not deleted from self's /// local_message field, possibly implement this in the future? - pub fn load_local(&mut self) -> Result<(), Error> { + pub async fn load_local(&mut self) -> Result<(), Error> { for (key, value) in self.local_msgs.clone() { - self.add(&value).unwrap_or_else(|err| { + self.add(&value).await.unwrap_or_else(|err| { if err == Error::SequenceTooLow { warn!("error adding message: {:?}", err); self.local_msgs.remove(&key); @@ -511,11 +479,13 @@ where } } -pub fn remove( +pub async fn remove( from: &Address, - mut pending: MutexGuard>, + pending: Arc>>, sequence: u64, ) -> Result<(), Error> { + let mut pending = pending.write().await; + let mset = pending .get_mut(from) .ok_or_else(|| Error::InvalidFromAddr)?; @@ -538,10 +508,12 @@ pub fn remove( Ok(()) } -fn recover_sig( - mut bls_sig_cache: MutexGuard>, +/// Attempt to get a signed message that corresponds to an unsigned message in bls_sig_cache +async fn recover_sig( + bls_sig_cache: Arc>>, msg: UnsignedMessage, ) -> Result { + let mut bls_sig_cache = bls_sig_cache.write().await; let val = bls_sig_cache .get(&msg.cid()?) .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; @@ -551,23 +523,28 @@ fn recover_sig( /// Finish verifying signed message before adding it to the pending mset hashmap. If an entry /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending hashmap -fn add_helper( - api: MutexGuard, - mut bls_sig_cache: MutexGuard>, - mut pending: MutexGuard>, +async fn add_helper( + api: Arc>, + bls_sig_cache: Arc>>, + pending: Arc>>, msg: SignedMessage, ) -> Result<(), Error> where T: Provider, { + let mut pending = pending.write().await; + let api = api.read().await; + let mut bls_sig_cache = bls_sig_cache.write().await; if msg.signature().signature_type() == SignatureType::BLS { bls_sig_cache.put(msg.cid()?, msg.signature().clone()); } + if msg.message().gas_limit() > 100_000_000 { return Err(Error::Other( "given message has too high of a gas limit".to_string(), )); } + api.put_message(&msg)?; let msett = pending.get_mut(msg.message().from()); match msett { @@ -583,11 +560,11 @@ where /// This function will revert and/or apply tipsets to the message pool. This function should be /// called every time that there is a head change in the message pool -pub fn head_change( - api: Arc>, - bls_sig_cache: Arc>>, - pending: Arc>>, - cur_tipset: Arc>, +pub async fn head_change( + api: Arc>, + bls_sig_cache: Arc>>, + pending: Arc>>, + cur_tipset: Arc>, revert: Vec, apply: Vec, ) -> Result<(), Error> @@ -596,27 +573,24 @@ where { let mut rmsgs: HashMap> = HashMap::new(); for ts in revert { - let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let api_locked = api.write().await; let pts = api_locked.load_tipset(ts.parents())?; drop(api_locked); let mut msgs: Vec = Vec::new(); for block in ts.blocks() { - let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let api_locked = api.read().await; let (umsg, mut smsgs) = api_locked.messages_for_block(&block)?; drop(api_locked); msgs.append(smsgs.as_mut()); for msg in umsg { - let smsg = recover_sig( - bls_sig_cache.lock().map_err(|_| Error::MutexPoisonError)?, - msg, - )?; + let bls_sig_cache = bls_sig_cache.clone(); + let smsg = recover_sig(bls_sig_cache, msg).await?; msgs.push(smsg) } } - // let msgs = self.messages_for_blocks(ts.blocks())?; let parent = pts.clone(); - let mut cur_ts_locked = cur_tipset.lock().map_err(|_| Error::MutexPoisonError)?; + let mut cur_ts_locked = cur_tipset.write().await; *cur_ts_locked = parent; drop(cur_ts_locked); @@ -627,41 +601,41 @@ where for ts in apply { for b in ts.blocks() { - let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; + let api_locked = api.read().await; let (msgs, smsgs) = api_locked.messages_for_block(b)?; drop(api_locked); + for msg in smsgs { rm( msg.from(), - pending.lock().map_err(|_| Error::MutexPoisonError)?, + pending.clone(), msg.sequence(), rmsgs.borrow_mut(), - ); + ) + .await; } for msg in msgs { rm( msg.from(), - pending.lock().map_err(|_| Error::MutexPoisonError)?, + pending.clone(), msg.sequence(), rmsgs.borrow_mut(), - ); + ) + .await; } } - let mut cur_ts_locked = cur_tipset.lock().map_err(|_| Error::MutexPoisonError)?; + let mut cur_ts_locked = cur_tipset.write().await; *cur_ts_locked = ts; drop(cur_ts_locked); } for (_, hm) in rmsgs { for (_, msg) in hm { - let api_locked = api.lock().map_err(|_| Error::MutexPoisonError)?; - let pending = pending.lock().map_err(|_| Error::MutexPoisonError)?; - add_helper( - api_locked, - bls_sig_cache.lock().map_err(|_| Error::MutexPoisonError)?, - pending, - msg.clone(), - ) - .ok(); + let api_locked = api.clone(); + let pending = pending.clone(); + let bls_sig_cache = bls_sig_cache.clone(); + add_helper(api_locked, bls_sig_cache, pending, msg.clone()) + .await + .ok(); } } Ok(()) @@ -669,15 +643,15 @@ where /// This is a helper method for head_change. This method will remove a sequence for a from address /// from the rmsgs hashmap. Also remove the from address and sequence from the mmessagepool. -fn rm( +async fn rm( from: &Address, - pending: MutexGuard>, + pending: Arc>>, sequence: u64, rmsgs: &mut HashMap>, ) { let s = rmsgs.get_mut(from); if s.is_none() { - remove(from, pending, sequence).ok(); + remove(from, pending.clone(), sequence).await.ok(); return; } let temp = s.unwrap(); @@ -685,7 +659,7 @@ fn rm( temp.remove(&sequence); return; } - remove(from, pending, sequence).ok(); + remove(from, pending, sequence).await.ok(); } /// This function is a helper method for head_change. This method will add a signed message to the given rmsgs HashMap @@ -783,6 +757,7 @@ mod tests { ) -> Result<(Vec, Vec), Errors> { let v: Vec = Vec::new(); let thing = self.bmsgs.get(h.cid()); + match thing { Some(s) => Ok((v, s.clone())), None => { @@ -795,6 +770,7 @@ mod tests { fn messages_for_tipset(&self, h: &Tipset) -> Result, Errors> { let (us, s) = self.messages_for_block(&h.blocks()[0]).unwrap(); let mut msgs = Vec::new(); + for msg in us { msgs.push(msg); } @@ -903,55 +879,54 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_sequence(&sender, 0); - let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); - - let mut smsg_vec = Vec::new(); - for i in 0..4 { - let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); - smsg_vec.push(msg); - } - - assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); - mpool.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); - mpool.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); - mpool.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); - mpool.push(&smsg_vec[3]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); - - let a = mock_block(1, 1); + task::block_on(async move { + let mut mpool = MessagePool::new(tma, "mptest".to_string()).await.unwrap(); - mpool.api.lock().unwrap().set_block_messages(&a, smsg_vec); - let api = mpool.api.clone(); - let bls_sig_cache = mpool.bls_sig_cache.clone(); - let pending = mpool.pending.clone(); - let cur_tipset = mpool.cur_tipset.clone(); + let mut smsg_vec = Vec::new(); + for i in 0..4 { + let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); + smsg_vec.push(msg); + } - head_change( - api, - bls_sig_cache, - pending, - cur_tipset, - Vec::new(), - vec![Tipset::new(vec![a]).unwrap()], - ) - .unwrap(); - // mpool - // .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) - // .unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 0); + mpool.push(&smsg_vec[0]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 1); + mpool.push(&smsg_vec[1]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 2); + mpool.push(&smsg_vec[2]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 3); + mpool.push(&smsg_vec[3]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); + + let a = mock_block(1, 1); + + mpool.api.write().await.set_block_messages(&a, smsg_vec); + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); + + head_change( + api, + bls_sig_cache, + pending, + cur_tipset, + Vec::new(), + vec![Tipset::new(vec![a]).unwrap()], + ) + .await + .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); + }) } #[test] fn test_revert_messages() { let tma = TestApi::new(); let mut wallet = Wallet::new(MemKeyStore::new()); - let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); let a = mock_block(1, 1); let tipset = Tipset::new(vec![a.clone()]).unwrap(); @@ -967,83 +942,80 @@ mod tests { smsg_vec.push(msg); } - let mut api_temp = mpool.api.lock().unwrap(); - api_temp.set_block_messages(&a, vec![smsg_vec[0].clone()]); - api_temp.set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); - api_temp.set_state_sequence(&sender, 0); - - drop(api_temp); + task::block_on(async move { + let mut mpool = MessagePool::new(tma, "mptest".to_string()).await.unwrap(); + + let mut api_temp = mpool.api.write().await; + api_temp.set_block_messages(&a, vec![smsg_vec[0].clone()]); + api_temp.set_block_messages(&b.clone(), smsg_vec[1..4].to_vec()); + api_temp.set_state_sequence(&sender, 0); + drop(api_temp); + + mpool.add(&smsg_vec[0]).await.unwrap(); + mpool.add(&smsg_vec[1]).await.unwrap(); + mpool.add(&smsg_vec[2]).await.unwrap(); + mpool.add(&smsg_vec[3]).await.unwrap(); + + mpool.api.write().await.set_state_sequence(&sender, 0); + + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); + + head_change( + api.clone(), + bls_sig_cache.clone(), + pending.clone(), + cur_tipset.clone(), + Vec::new(), + vec![Tipset::new(vec![a]).unwrap()], + ) + .await + .unwrap(); - mpool.add(&smsg_vec[0]).unwrap(); - mpool.add(&smsg_vec[1]).unwrap(); - mpool.add(&smsg_vec[2]).unwrap(); - mpool.add(&smsg_vec[3]).unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); - mpool.api.lock().unwrap().set_state_sequence(&sender, 0); + mpool.api.write().await.set_state_sequence(&sender, 1); - let api = mpool.api.clone(); - let bls_sig_cache = mpool.bls_sig_cache.clone(); - let pending = mpool.pending.clone(); - let cur_tipset = mpool.cur_tipset.clone(); + let api = mpool.api.clone(); + let bls_sig_cache = mpool.bls_sig_cache.clone(); + let pending = mpool.pending.clone(); + let cur_tipset = mpool.cur_tipset.clone(); - head_change( - api.clone(), - bls_sig_cache.clone(), - pending.clone(), - cur_tipset.clone(), - Vec::new(), - vec![Tipset::new(vec![a]).unwrap()], - ) - .unwrap(); - - // mpool - // .head_change(Vec::new(), vec![Tipset::new(vec![a]).unwrap()]) - // .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); - - mpool.api.lock().unwrap().set_state_sequence(&sender, 1); - - let api = mpool.api.clone(); - let bls_sig_cache = mpool.bls_sig_cache.clone(); - let pending = mpool.pending.clone(); - let cur_tipset = mpool.cur_tipset.clone(); - - head_change( - api.clone(), - bls_sig_cache.clone(), - pending.clone(), - cur_tipset.clone(), - Vec::new(), - vec![Tipset::new(vec![b.clone()]).unwrap()], - ) - .unwrap(); + head_change( + api.clone(), + bls_sig_cache.clone(), + pending.clone(), + cur_tipset.clone(), + Vec::new(), + vec![Tipset::new(vec![b.clone()]).unwrap()], + ) + .await + .unwrap(); - // mpool - // .head_change(Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()]) - // .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); - let mut api_temp = mpool.api.lock().unwrap(); - api_temp.set_state_sequence(&sender, 0); - drop(api_temp); + let mut api_temp = mpool.api.write().await; + api_temp.set_state_sequence(&sender, 0); + drop(api_temp); - head_change( - api, - bls_sig_cache, - pending, - cur_tipset, - vec![Tipset::new(vec![b]).unwrap()], - Vec::new(), - ) - .unwrap(); + head_change( + api, + bls_sig_cache, + pending, + cur_tipset, + vec![Tipset::new(vec![b]).unwrap()], + Vec::new(), + ) + .await + .unwrap(); - // mpool - // .head_change(vec![Tipset::new(vec![b]).unwrap()], Vec::new()) - // .unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 4); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); - let (p, _) = mpool.pending().unwrap(); - assert_eq!(p.len(), 3); + let (p, _) = mpool.pending().await.unwrap(); + assert_eq!(p.len(), 3); + }) } #[test] @@ -1056,38 +1028,38 @@ mod tests { let mut tma = TestApi::new(); tma.set_state_sequence(&sender, 0); - let mut mpool = MessagePool::new(tma, "mptest".to_string()).unwrap(); + task::block_on(async move { + let mut mpool = MessagePool::new(tma, "mptest".to_string()).await.unwrap(); - let mut smsg_vec = Vec::new(); - for i in 0..3 { - let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); - smsg_vec.push(msg); - } + let mut smsg_vec = Vec::new(); + for i in 0..3 { + let msg = create_smsg(&target, &sender, wallet.borrow_mut(), i); + smsg_vec.push(msg); + } - assert_eq!(mpool.get_sequence(&sender).unwrap(), 0); - mpool.push(&smsg_vec[0]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 1); - mpool.push(&smsg_vec[1]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 2); - mpool.push(&smsg_vec[2]).unwrap(); - assert_eq!(mpool.get_sequence(&sender).unwrap(), 3); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 0); + mpool.push(&smsg_vec[0]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 1); + mpool.push(&smsg_vec[1]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 2); + mpool.push(&smsg_vec[2]).await.unwrap(); + assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 3); - let header = mock_block(1, 1); - let tipset = Tipset::new(vec![header.clone()]).unwrap(); + let header = mock_block(1, 1); + let tipset = Tipset::new(vec![header.clone()]).unwrap(); - let temp = mpool.api.clone(); - let mut api = temp.lock().unwrap(); + let temp = mpool.api.clone(); - let ts = tipset.clone(); - task::block_on(async move { - // updater.api.lock().unwrap().set_block_messages(&header, vec![message]); + let ts = tipset.clone(); + let mut api = temp.write().await; api.set_heaviest_tipset(Arc::new(ts)).await; - }); + drop(api); - // sleep allows for async block to update mpool's cur_tipset - sleep(Duration::new(2, 0)); + // sleep allows for async block to update mpool's cur_tipset + sleep(Duration::new(2, 0)); - let cur_ts = mpool.cur_tipset.lock().unwrap().clone(); - assert_eq!(cur_ts, tipset); + let cur_ts = mpool.cur_tipset.read().await.clone(); + assert_eq!(cur_ts, tipset); + }) } } From 5c67b8119b4317c87a4d10cc518f92012a0e8f4d Mon Sep 17 00:00:00 2001 From: flodesi Date: Thu, 2 Jul 2020 15:30:23 -0400 Subject: [PATCH 23/30] add comment --- blockchain/message_pool/src/msgpool.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 1620f4321e1a..291670fd894f 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -479,6 +479,7 @@ where } } +/// Remove a message from pending given the from address and sequence pub async fn remove( from: &Address, pending: Arc>>, From fb4b2e53c90bcf2f733fa70f0ff43047ed8a007f Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 6 Jul 2020 00:07:38 -0400 Subject: [PATCH 24/30] applied changes --- Makefile | 1 + blockchain/chain/src/store/chain_store.rs | 12 +- blockchain/message_pool/src/msgpool.rs | 225 +++++++++------------- 3 files changed, 100 insertions(+), 138 deletions(-) diff --git a/Makefile b/Makefile index 4dd23ba99609..9154084871e4 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ clean: @cargo clean -p key_management @cargo clean -p forest_json_utils @cargo clean -p test_utils + @cargo clean -p message_pool @echo "Done cleaning." lint: license clean diff --git a/blockchain/chain/src/store/chain_store.rs b/blockchain/chain/src/store/chain_store.rs index 49bf0febc575..3f43af433a3c 100644 --- a/blockchain/chain/src/store/chain_store.rs +++ b/blockchain/chain/src/store/chain_store.rs @@ -208,16 +208,10 @@ where DB: BlockStore, { let mut umsg: Vec = Vec::new(); - let mut msgs: Vec = Vec::new(); for bh in h.blocks().iter() { - let (mut bh_umsg_tmp, mut bh_msg_tmp) = block_messages(db, bh)?; - let bh_umsg = &mut bh_umsg_tmp; - let bh_msg = &mut bh_msg_tmp; - umsg.append(bh_umsg); - msgs.append(bh_msg); - } - for msg in msgs { - umsg.push(msg.into_message()); + let (mut bh_umsg, bh_msg) = block_messages(db, bh)?; + umsg.append(&mut bh_umsg); + umsg.append(&mut bh_msg.into_iter().map(|msg| msg.into_message()).collect()); } Ok(umsg) } diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 291670fd894f..d1691f52cb80 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -14,15 +14,13 @@ use crypto::{Signature, SignatureType}; use encoding::Cbor; use flo_stream::Subscriber; use futures::StreamExt; -use log::warn; +use log::{error, warn}; use lru::LruCache; use message::{Message, SignedMessage, UnsignedMessage}; use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; use std::collections::HashMap; -use std::thread::sleep; -use std::time::Duration; use vm::ActorState; const REPLACE_BY_FEE_RATIO: f32 = 1.25; @@ -48,7 +46,7 @@ impl MsgSet { /// Add a signed message to the MsgSet. Increase next_sequence if the message has a sequence greater /// than any existing message sequence. - pub fn add(&mut self, m: &SignedMessage) -> Result<(), Error> { + pub fn add(&mut self, m: SignedMessage) -> Result<(), Error> { if self.msgs.is_empty() || m.sequence() >= self.next_sequence { self.next_sequence = m.sequence() + 1; } @@ -57,15 +55,18 @@ impl MsgSet { let gas_price = exms.message().gas_price(); let rbf_num = BigUint::from(RBF_NUM); let rbf_denom = BigUint::from(RBF_DENOM); - let min_price = - gas_price.clone() + (gas_price / &rbf_num) + rbf_denom + BigUint::from(1_u64); + let min_price = gas_price.clone() + ((gas_price * &rbf_num) / rbf_denom) + 1u8; if m.message().gas_price() <= &min_price { // message with duplicate sequence is already in mpool + warn!("try to add message with duplicate sequence"); return Err(Error::DuplicateSequence); } + } else { + warn!("try to add message with duplicate sequence"); + return Err(Error::DuplicateSequence); } } - self.msgs.insert(m.sequence(), m.clone()); + self.msgs.insert(m.sequence(), m); Ok(()) } } @@ -167,7 +168,7 @@ pub struct MessagePool { pub network_name: String, bls_sig_cache: Arc>>, // mutex this sig_val_cache: LruCache, - local_msgs: HashMap, SignedMessage>, + local_msgs: Vec, } impl MessagePool @@ -198,14 +199,12 @@ where network_name, bls_sig_cache, sig_val_cache, - local_msgs: HashMap::new(), + local_msgs: Vec::new(), }; mp.load_local().await?; - let mut api = mp.api.write().await; - let mut subscriber = api.subscribe_head_changes(); - drop(api); + let mut subscriber = mp.api.write().await.subscribe_head_changes(); let api = mp.api.clone(); let bls_sig_cache = mp.bls_sig_cache.clone(); @@ -226,25 +225,24 @@ where .await .unwrap_or_else(|err| warn!("Error changing head: {:?}", err)); } - sleep(Duration::new(1, 0)); } }); Ok(mp) } /// Add a signed message to local_addrs and local_msgs - fn add_local(&mut self, m: &SignedMessage, msgb: Vec) -> Result<(), Error> { + fn add_local(&mut self, m: SignedMessage) -> Result<(), Error> { self.local_addrs.push(*m.from()); - self.local_msgs.insert(msgb, m.clone()); + self.local_msgs.push(m); Ok(()) } /// Push a signed message to the MessagePool - pub async fn push(&mut self, msg: &SignedMessage) -> Result { - let msg_serial = msg.marshal_cbor()?; - self.add(msg).await?; - self.add_local(msg, msg_serial)?; - msg.cid().map_err(|err| err.into()) + pub async fn push(&mut self, msg: SignedMessage) -> Result { + let cid = msg.cid().map_err(|err| Error::Other(err.to_string()))?; + self.add(&msg).await?; + self.add_local(msg)?; + Ok(cid) } /// This is a helper to push that will help to make sure that the message fits the parameters @@ -261,9 +259,7 @@ where self.verify_msg_sig(msg)?; let tmp = msg.clone(); - let tip_locked = self.cur_tipset.read().await; - let tip = tip_locked.clone(); - drop(tip_locked); + let tip = self.cur_tipset.read().await.clone(); self.add_tipset(tmp, &tip).await } @@ -325,9 +321,7 @@ where /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence pub async fn get_sequence(&self, addr: &Address) -> Result { - let cur_t = self.cur_tipset.read().await; - let cur_ts = cur_t.clone(); - drop(cur_t); + let cur_ts = self.cur_tipset.read().await.clone(); let sequence = self.get_state_sequence(addr, &cur_ts).await?; @@ -370,8 +364,7 @@ where /// Get the state balance for the actor that corresponds to the supplied address and tipset, /// if this actor does not exist, return an error async fn get_state_balance(&mut self, addr: &Address, ts: &Tipset) -> Result { - let api = self.api.read().await; - let actor = api.state_get_actor(&addr, &ts)?; + let actor = self.api.read().await.state_get_actor(&addr, &ts)?; Ok(BigInt::from(actor.balance)) } @@ -384,10 +377,8 @@ where /// self. pub async fn pending(&self) -> Result<(Vec, Tipset), Error> { let mut out: Vec = Vec::new(); - let pending = self.pending.read().await; let pending_hm = pending.clone(); - drop(pending); for (addr, _) in pending_hm { out.append( @@ -398,9 +389,7 @@ where ) } - let cur_t = self.cur_tipset.read().await; - let cur_ts = cur_t.clone(); - drop(cur_t); + let cur_ts = self.cur_tipset.read().await.clone(); Ok((out, cur_ts)) } @@ -409,26 +398,16 @@ where /// each messsage's sequence. If no corresponding messages found, return None result type async fn pending_for(&self, a: &Address) -> Option> { let pending = self.pending.read().await; - - let mset = pending.get(a); - match mset { - Some(msgset) => { - if msgset.msgs.is_empty() { - return None; - } - - let mut msg_vec = Vec::new(); - - for (_, item) in msgset.msgs.clone() { - msg_vec.push(item); - } - - msg_vec.sort_by_key(|value| value.message().sequence()); - - Some(msg_vec) - } - None => None, + let mset = pending.get(a)?; + if mset.msgs.is_empty() { + return None; } + let mut msg_vec = Vec::new(); + for (_, item) in mset.msgs.iter() { + msg_vec.push(item.clone()); + } + msg_vec.sort_by_key(|value| value.message().sequence()); + Some(msg_vec) } /// Return Vector of signed messages given a block header for self @@ -439,13 +418,12 @@ where let mut msg_vec: Vec = Vec::new(); for block in blks { - let api = self.api.read().await; - let (umsg, mut smsgs) = api.messages_for_block(&block)?; - drop(api); + let (umsg, mut smsgs) = self.api.read().await.messages_for_block(&block)?; msg_vec.append(smsgs.as_mut()); for msg in umsg { - let smsg = recover_sig(self.bls_sig_cache.clone(), msg).await?; + let mut bls_sig_cache = self.bls_sig_cache.write().await; + let smsg = recover_sig(&mut bls_sig_cache, msg).await?; msg_vec.push(smsg) } } @@ -467,13 +445,14 @@ where /// Load local messages into pending. As of right now messages are not deleted from self's /// local_message field, possibly implement this in the future? pub async fn load_local(&mut self) -> Result<(), Error> { - for (key, value) in self.local_msgs.clone() { - self.add(&value).await.unwrap_or_else(|err| { + let msg_vec = self.local_msgs.clone(); + for msg in msg_vec { + self.add(&msg).await.unwrap_or_else(|err| { if err == Error::SequenceTooLow { warn!("error adding message: {:?}", err); - self.local_msgs.remove(&key); + self.local_msgs.retain(|smsg| *smsg != msg); } - }); + }) } Ok(()) } @@ -511,10 +490,9 @@ pub async fn remove( /// Attempt to get a signed message that corresponds to an unsigned message in bls_sig_cache async fn recover_sig( - bls_sig_cache: Arc>>, + bls_sig_cache: &mut LruCache, msg: UnsignedMessage, ) -> Result { - let mut bls_sig_cache = bls_sig_cache.write().await; let val = bls_sig_cache .get(&msg.cid()?) .ok_or_else(|| Error::Other("Could not recover sig".to_owned()))?; @@ -533,11 +511,11 @@ async fn add_helper( where T: Provider, { - let mut pending = pending.write().await; - let api = api.read().await; - let mut bls_sig_cache = bls_sig_cache.write().await; if msg.signature().signature_type() == SignatureType::BLS { - bls_sig_cache.put(msg.cid()?, msg.signature().clone()); + bls_sig_cache + .write() + .await + .put(msg.cid()?, msg.signature().clone()); } if msg.message().gas_limit() > 100_000_000 { @@ -546,16 +524,20 @@ where )); } - api.put_message(&msg)?; + api.read().await.put_message(&msg)?; + + let mut pending = pending.write().await; let msett = pending.get_mut(msg.message().from()); match msett { - Some(mset) => mset.add(&msg)?, + Some(mset) => mset.add(msg)?, None => { let mut mset = MsgSet::new(); - mset.add(&msg)?; - pending.insert(*msg.message().from(), mset); + let from = *msg.message().from(); + mset.add(msg)?; + pending.insert(from, mset); } } + Ok(()) } @@ -574,26 +556,19 @@ where { let mut rmsgs: HashMap> = HashMap::new(); for ts in revert { - let api_locked = api.write().await; - let pts = api_locked.load_tipset(ts.parents())?; - drop(api_locked); + let pts = api.write().await.load_tipset(ts.parents())?; let mut msgs: Vec = Vec::new(); for block in ts.blocks() { - let api_locked = api.read().await; - let (umsg, mut smsgs) = api_locked.messages_for_block(&block)?; - drop(api_locked); + let (umsg, mut smsgs) = api.read().await.messages_for_block(&block)?; msgs.append(smsgs.as_mut()); for msg in umsg { - let bls_sig_cache = bls_sig_cache.clone(); - let smsg = recover_sig(bls_sig_cache, msg).await?; + let mut bls_sig_cache = bls_sig_cache.write().await; + let smsg = recover_sig(&mut bls_sig_cache, msg).await?; msgs.push(smsg) } } - let parent = pts.clone(); - let mut cur_ts_locked = cur_tipset.write().await; - *cur_ts_locked = parent; - drop(cur_ts_locked); + *cur_tipset.write().await = pts; for msg in msgs { add(msg, rmsgs.borrow_mut()); @@ -602,9 +577,7 @@ where for ts in apply { for b in ts.blocks() { - let api_locked = api.read().await; - let (msgs, smsgs) = api_locked.messages_for_block(b)?; - drop(api_locked); + let (msgs, smsgs) = api.read().await.messages_for_block(b)?; for msg in smsgs { rm( @@ -613,7 +586,7 @@ where msg.sequence(), rmsgs.borrow_mut(), ) - .await; + .await?; } for msg in msgs { rm( @@ -622,21 +595,18 @@ where msg.sequence(), rmsgs.borrow_mut(), ) - .await; + .await?; } } - let mut cur_ts_locked = cur_tipset.write().await; - *cur_ts_locked = ts; - drop(cur_ts_locked); + *cur_tipset.write().await = ts; } for (_, hm) in rmsgs { for (_, msg) in hm { - let api_locked = api.clone(); - let pending = pending.clone(); - let bls_sig_cache = bls_sig_cache.clone(); - add_helper(api_locked, bls_sig_cache, pending, msg.clone()) - .await - .ok(); + if let Err(e) = + add_helper(api.clone(), bls_sig_cache.clone(), pending.clone(), msg).await + { + error!("Failed to readd message from reorg to mpool: {}", e); + } } } Ok(()) @@ -649,31 +619,27 @@ async fn rm( pending: Arc>>, sequence: u64, rmsgs: &mut HashMap>, -) { - let s = rmsgs.get_mut(from); - if s.is_none() { - remove(from, pending.clone(), sequence).await.ok(); - return; - } - let temp = s.unwrap(); - if temp.get_mut(&sequence).is_some() { - temp.remove(&sequence); - return; +) -> Result<(), Error> { + if let Some(temp) = rmsgs.get_mut(from) { + if temp.get_mut(&sequence).is_some() { + temp.remove(&sequence); + } + remove(from, pending, sequence).await?; + } else { + remove(from, pending.clone(), sequence).await?; } - remove(from, pending, sequence).await.ok(); + Ok(()) } /// This function is a helper method for head_change. This method will add a signed message to the given rmsgs HashMap fn add(m: SignedMessage, rmsgs: &mut HashMap>) { let s = rmsgs.get_mut(m.from()); - if s.is_none() { - let mut temp = HashMap::new(); - temp.insert(m.sequence(), m.clone()); - rmsgs.insert(*m.from(), temp); - return; + if let Some(temp) = s { + temp.insert(m.sequence(), m); + } else { + rmsgs.insert(*m.from(), HashMap::new()); + rmsgs.get_mut(m.from()).unwrap().insert(m.sequence(), m); } - let temp = s.unwrap(); - temp.insert(m.sequence(), m); } #[cfg(test)] @@ -692,6 +658,8 @@ mod tests { use num_bigint::BigUint; use std::borrow::BorrowMut; use std::convert::TryFrom; + use std::thread::sleep; + use std::time::Duration; struct TestApi { bmsgs: HashMap>, @@ -890,13 +858,13 @@ mod tests { } assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 0); - mpool.push(&smsg_vec[0]).await.unwrap(); + mpool.push(smsg_vec[0].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 1); - mpool.push(&smsg_vec[1]).await.unwrap(); + mpool.push(smsg_vec[1].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 2); - mpool.push(&smsg_vec[2]).await.unwrap(); + mpool.push(smsg_vec[2].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 3); - mpool.push(&smsg_vec[3]).await.unwrap(); + mpool.push(smsg_vec[3].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); let a = mock_block(1, 1); @@ -997,9 +965,7 @@ mod tests { assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 4); - let mut api_temp = mpool.api.write().await; - api_temp.set_state_sequence(&sender, 0); - drop(api_temp); + mpool.api.write().await.set_state_sequence(&sender, 0); head_change( api, @@ -1039,22 +1005,23 @@ mod tests { } assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 0); - mpool.push(&smsg_vec[0]).await.unwrap(); + mpool.push(smsg_vec[0].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 1); - mpool.push(&smsg_vec[1]).await.unwrap(); + mpool.push(smsg_vec[1].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 2); - mpool.push(&smsg_vec[2]).await.unwrap(); + mpool.push(smsg_vec[2].clone()).await.unwrap(); assert_eq!(mpool.get_sequence(&sender).await.unwrap(), 3); let header = mock_block(1, 1); let tipset = Tipset::new(vec![header.clone()]).unwrap(); - let temp = mpool.api.clone(); - let ts = tipset.clone(); - let mut api = temp.write().await; - api.set_heaviest_tipset(Arc::new(ts)).await; - drop(api); + mpool + .api + .write() + .await + .set_heaviest_tipset(Arc::new(ts)) + .await; // sleep allows for async block to update mpool's cur_tipset sleep(Duration::new(2, 0)); From 57b6eb9467a3e5a5e07ad790c91dfb2cb75e7945 Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 6 Jul 2020 11:14:01 -0400 Subject: [PATCH 25/30] fixes and code cleanup --- blockchain/chain/src/store/chain_store.rs | 2 +- blockchain/message_pool/src/errors.rs | 8 ++--- blockchain/message_pool/src/msgpool.rs | 42 ++++++++++------------- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/blockchain/chain/src/store/chain_store.rs b/blockchain/chain/src/store/chain_store.rs index 3f43af433a3c..c7fcdf9e9c5d 100644 --- a/blockchain/chain/src/store/chain_store.rs +++ b/blockchain/chain/src/store/chain_store.rs @@ -211,7 +211,7 @@ where for bh in h.blocks().iter() { let (mut bh_umsg, bh_msg) = block_messages(db, bh)?; umsg.append(&mut bh_umsg); - umsg.append(&mut bh_msg.into_iter().map(|msg| msg.into_message()).collect()); + umsg.extend(bh_msg.into_iter().map(|msg| msg.into_message())); } Ok(umsg) } diff --git a/blockchain/message_pool/src/errors.rs b/blockchain/message_pool/src/errors.rs index 156d68a12db9..f1a1ea8cf0b9 100644 --- a/blockchain/message_pool/src/errors.rs +++ b/blockchain/message_pool/src/errors.rs @@ -11,6 +11,8 @@ pub enum Error { /// Error indicating message that's too large #[error("Message is too big")] MessageTooBig, + #[error("gas price is lower than min gas price")] + GasPriceTooLow, #[error("Cannot send more Filecoin than will ever exist")] MessageValueTooHigh, #[error("Message sequence too low")] @@ -23,12 +25,6 @@ pub enum Error { InvalidFromAddr, #[error("Message with sequence already in mempool")] DuplicateSequence, - #[error("Signature validation failed")] - SigVerification, - #[error("Unknown signature type")] - UnknownSigType, - #[error("BLS signature too short")] - BLSSigTooShort, #[error("{0}")] Other(String), } diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index d1691f52cb80..aef79a9eb0b0 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,7 +3,7 @@ use super::errors::Error; use address::Address; -use async_std::sync::{Arc, RwLock}; +use async_std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; @@ -57,9 +57,8 @@ impl MsgSet { let rbf_denom = BigUint::from(RBF_DENOM); let min_price = gas_price.clone() + ((gas_price * &rbf_num) / rbf_denom) + 1u8; if m.message().gas_price() <= &min_price { - // message with duplicate sequence is already in mpool - warn!("try to add message with duplicate sequence"); - return Err(Error::DuplicateSequence); + warn!("mesage gas price is below min gas price"); + return Err(Error::GasPriceTooLow); } } else { warn!("try to add message with duplicate sequence"); @@ -310,13 +309,10 @@ where /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending phashmap async fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { - add_helper( - self.api.clone(), - self.bls_sig_cache.clone(), - self.pending.clone(), - msg, - ) - .await + let api = self.api.read().await; + let sig_cache = self.bls_sig_cache.write().await; + let pending = self.pending.write().await; + add_helper(api, sig_cache, pending, msg).await } /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence @@ -503,19 +499,16 @@ async fn recover_sig( /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending hashmap async fn add_helper( - api: Arc>, - bls_sig_cache: Arc>>, - pending: Arc>>, + api: RwLockReadGuard<'_, T>, + mut bls_sig_cache: RwLockWriteGuard<'_, LruCache>, + mut pending: RwLockWriteGuard<'_, HashMap>, msg: SignedMessage, ) -> Result<(), Error> where T: Provider, { if msg.signature().signature_type() == SignatureType::BLS { - bls_sig_cache - .write() - .await - .put(msg.cid()?, msg.signature().clone()); + bls_sig_cache.put(msg.cid()?, msg.signature().clone()); } if msg.message().gas_limit() > 100_000_000 { @@ -524,9 +517,9 @@ where )); } - api.read().await.put_message(&msg)?; + api.put_message(&msg)?; - let mut pending = pending.write().await; + // let mut pending = pending.write().await; let msett = pending.get_mut(msg.message().from()); match msett { Some(mset) => mset.add(msg)?, @@ -552,7 +545,7 @@ pub async fn head_change( apply: Vec, ) -> Result<(), Error> where - T: Provider, + T: Provider + 'static, { let mut rmsgs: HashMap> = HashMap::new(); for ts in revert { @@ -602,9 +595,10 @@ where } for (_, hm) in rmsgs { for (_, msg) in hm { - if let Err(e) = - add_helper(api.clone(), bls_sig_cache.clone(), pending.clone(), msg).await - { + let api_lock = api.read().await; + let sig_cache_lock = bls_sig_cache.write().await; + let pending_lock = pending.write().await; + if let Err(e) = add_helper(api_lock, sig_cache_lock, pending_lock, msg).await { error!("Failed to readd message from reorg to mpool: {}", e); } } From 1c9b32bfe316c1ca893248b879a99a3e4ca560cb Mon Sep 17 00:00:00 2001 From: flodesi Date: Mon, 6 Jul 2020 17:54:12 -0400 Subject: [PATCH 26/30] add box to local_msgs --- blockchain/message_pool/src/msgpool.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index aef79a9eb0b0..c0435e7d86dd 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -157,17 +157,18 @@ where } /// This is the main MessagePool struct +#[allow(clippy::box_vec)] pub struct MessagePool { local_addrs: Vec
, - pending: Arc>>, // mutex this + pending: Arc>>, pub cur_tipset: Arc>, api: Arc>, pub min_gas_price: BigInt, pub max_tx_pool_size: i64, pub network_name: String, - bls_sig_cache: Arc>>, // mutex this + bls_sig_cache: Arc>>, sig_val_cache: LruCache, - local_msgs: Vec, + local_msgs: Box>, } impl MessagePool @@ -198,7 +199,7 @@ where network_name, bls_sig_cache, sig_val_cache, - local_msgs: Vec::new(), + local_msgs: Box::new(Vec::new()), }; mp.load_local().await?; @@ -441,15 +442,19 @@ where /// Load local messages into pending. As of right now messages are not deleted from self's /// local_message field, possibly implement this in the future? pub async fn load_local(&mut self) -> Result<(), Error> { - let msg_vec = self.local_msgs.clone(); - for msg in msg_vec { + let mut msg_vec = Vec::new(); + while let Some(msg) = self.local_msgs.pop() { self.add(&msg).await.unwrap_or_else(|err| { if err == Error::SequenceTooLow { warn!("error adding message: {:?}", err); - self.local_msgs.retain(|smsg| *smsg != msg); + } else { + msg_vec.push(msg); } }) } + + *self.local_msgs = msg_vec; + Ok(()) } } From ce90bddb6329c9eabac8598510b65f04e48b7e05 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 7 Jul 2020 03:07:14 -0400 Subject: [PATCH 27/30] remove box --- blockchain/message_pool/src/msgpool.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index c0435e7d86dd..732f0e50d5a0 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -157,7 +157,6 @@ where } /// This is the main MessagePool struct -#[allow(clippy::box_vec)] pub struct MessagePool { local_addrs: Vec
, pending: Arc>>, @@ -168,7 +167,7 @@ pub struct MessagePool { pub network_name: String, bls_sig_cache: Arc>>, sig_val_cache: LruCache, - local_msgs: Box>, + local_msgs: Vec, } impl MessagePool @@ -199,7 +198,7 @@ where network_name, bls_sig_cache, sig_val_cache, - local_msgs: Box::new(Vec::new()), + local_msgs: Vec::new(), }; mp.load_local().await?; @@ -453,7 +452,7 @@ where }) } - *self.local_msgs = msg_vec; + self.local_msgs = msg_vec; Ok(()) } From 6d2bc4834bf919a5a221779408f000bb314b0c09 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 7 Jul 2020 09:51:10 -0400 Subject: [PATCH 28/30] added push to add_local when no error --- blockchain/message_pool/src/msgpool.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index c0435e7d86dd..5e7010a77b97 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -444,13 +444,15 @@ where pub async fn load_local(&mut self) -> Result<(), Error> { let mut msg_vec = Vec::new(); while let Some(msg) = self.local_msgs.pop() { - self.add(&msg).await.unwrap_or_else(|err| { + if let Err(err) = self.add(&msg).await { if err == Error::SequenceTooLow { warn!("error adding message: {:?}", err); } else { msg_vec.push(msg); } - }) + } else { + msg_vec.push(msg); + } } *self.local_msgs = msg_vec; From a0456543ced2f4304b2b21d423b5ebfa2258d722 Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 7 Jul 2020 17:38:05 -0400 Subject: [PATCH 29/30] revert add_helper to use refs --- blockchain/message_pool/src/msgpool.rs | 35 +++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index 62b91ded8d83..a52743a33363 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -3,7 +3,7 @@ use super::errors::Error; use address::Address; -use async_std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use async_std::sync::{Arc, RwLock}; use async_std::task; use blocks::{BlockHeader, Tipset, TipsetKeys}; use blockstore::BlockStore; @@ -309,10 +309,13 @@ where /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending phashmap async fn add_helper(&mut self, msg: SignedMessage) -> Result<(), Error> { - let api = self.api.read().await; - let sig_cache = self.bls_sig_cache.write().await; - let pending = self.pending.write().await; - add_helper(api, sig_cache, pending, msg).await + add_helper( + self.api.as_ref(), + self.bls_sig_cache.as_ref(), + self.pending.as_ref(), + msg, + ) + .await } /// Get the sequence for a given address, return Error if there is a failure to retrieve sequence @@ -505,16 +508,19 @@ async fn recover_sig( /// in the hashmap does not yet exist, create a new mset that will correspond to the from message /// and push it to the pending hashmap async fn add_helper( - api: RwLockReadGuard<'_, T>, - mut bls_sig_cache: RwLockWriteGuard<'_, LruCache>, - mut pending: RwLockWriteGuard<'_, HashMap>, + api: &RwLock, + bls_sig_cache: &RwLock>, + pending: &RwLock>, msg: SignedMessage, ) -> Result<(), Error> where T: Provider, { if msg.signature().signature_type() == SignatureType::BLS { - bls_sig_cache.put(msg.cid()?, msg.signature().clone()); + bls_sig_cache + .write() + .await + .put(msg.cid()?, msg.signature().clone()); } if msg.message().gas_limit() > 100_000_000 { @@ -523,9 +529,9 @@ where )); } - api.put_message(&msg)?; + api.read().await.put_message(&msg)?; - // let mut pending = pending.write().await; + let mut pending = pending.write().await; let msett = pending.get_mut(msg.message().from()); match msett { Some(mset) => mset.add(msg)?, @@ -601,10 +607,9 @@ where } for (_, hm) in rmsgs { for (_, msg) in hm { - let api_lock = api.read().await; - let sig_cache_lock = bls_sig_cache.write().await; - let pending_lock = pending.write().await; - if let Err(e) = add_helper(api_lock, sig_cache_lock, pending_lock, msg).await { + if let Err(e) = + add_helper(api.as_ref(), bls_sig_cache.as_ref(), pending.as_ref(), msg).await + { error!("Failed to readd message from reorg to mpool: {}", e); } } From be9a58daa87e7307875d8bbafbf73c6a5248055c Mon Sep 17 00:00:00 2001 From: flodesi Date: Tue, 7 Jul 2020 19:46:08 -0400 Subject: [PATCH 30/30] changed local_msgs to hashset and refactor --- Cargo.lock | 2 +- blockchain/message_pool/src/msgpool.rs | 106 +++++++++++-------------- crypto/src/signature.rs | 6 +- vm/message/src/signed_message.rs | 2 +- vm/message/src/unsigned_message.rs | 2 +- vm/src/method.rs | 2 +- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3f0b5068851..04c539f1a2bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4042,7 +4042,7 @@ dependencies = [ "key_management", "libsecp256k1", "log", - "lru 0.5.2", + "lru 0.5.3", "serde", "state_tree", "thiserror", diff --git a/blockchain/message_pool/src/msgpool.rs b/blockchain/message_pool/src/msgpool.rs index a52743a33363..a3d3409fc087 100644 --- a/blockchain/message_pool/src/msgpool.rs +++ b/blockchain/message_pool/src/msgpool.rs @@ -20,7 +20,7 @@ use message::{Message, SignedMessage, UnsignedMessage}; use num_bigint::{BigInt, BigUint}; use state_tree::StateTree; use std::borrow::BorrowMut; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use vm::ActorState; const REPLACE_BY_FEE_RATIO: f32 = 1.25; @@ -167,7 +167,8 @@ pub struct MessagePool { pub network_name: String, bls_sig_cache: Arc>>, sig_val_cache: LruCache, - local_msgs: Vec, + // TODO look into adding a cap to local_msgs + local_msgs: HashSet, } impl MessagePool @@ -198,7 +199,7 @@ where network_name, bls_sig_cache, sig_val_cache, - local_msgs: Vec::new(), + local_msgs: HashSet::new(), }; mp.load_local().await?; @@ -214,10 +215,10 @@ where loop { if let Some(ts) = subscriber.next().await { head_change( - api.clone(), - bls_sig_cache.clone(), - pending.clone(), - cur_tipset.clone(), + api.as_ref(), + bls_sig_cache.as_ref(), + pending.as_ref(), + cur_tipset.as_ref(), Vec::new(), vec![ts.as_ref().clone()], ) @@ -232,7 +233,7 @@ where /// Add a signed message to local_addrs and local_msgs fn add_local(&mut self, m: SignedMessage) -> Result<(), Error> { self.local_addrs.push(*m.from()); - self.local_msgs.push(m); + self.local_msgs.insert(m); Ok(()) } @@ -369,7 +370,7 @@ where /// Remove a message given a sequence and address from the messagepool pub async fn remove(&mut self, from: &Address, sequence: u64) -> Result<(), Error> { - remove(from, self.pending.clone(), sequence).await + remove(from, self.pending.as_ref(), sequence).await } /// Return a tuple that contains a vector of all signed messages and the current tipset for @@ -444,20 +445,21 @@ where /// Load local messages into pending. As of right now messages are not deleted from self's /// local_message field, possibly implement this in the future? pub async fn load_local(&mut self) -> Result<(), Error> { - let mut msg_vec = Vec::new(); - while let Some(msg) = self.local_msgs.pop() { - if let Err(err) = self.add(&msg).await { + let mut rm_vec = Vec::new(); + let thing: Vec = self.local_msgs.iter().cloned().collect(); + + for k in thing { + self.add(&k).await.unwrap_or_else(|err| { if err == Error::SequenceTooLow { warn!("error adding message: {:?}", err); - } else { - msg_vec.push(msg); + rm_vec.push(k); } - } else { - msg_vec.push(msg); - } + }) } - self.local_msgs = msg_vec; + for item in rm_vec { + self.local_msgs.remove(&item); + } Ok(()) } @@ -466,7 +468,7 @@ where /// Remove a message from pending given the from address and sequence pub async fn remove( from: &Address, - pending: Arc>>, + pending: &RwLock>, sequence: u64, ) -> Result<(), Error> { let mut pending = pending.write().await; @@ -549,10 +551,10 @@ where /// This function will revert and/or apply tipsets to the message pool. This function should be /// called every time that there is a head change in the message pool pub async fn head_change( - api: Arc>, - bls_sig_cache: Arc>>, - pending: Arc>>, - cur_tipset: Arc>, + api: &RwLock, + bls_sig_cache: &RwLock>, + pending: &RwLock>, + cur_tipset: &RwLock, revert: Vec, apply: Vec, ) -> Result<(), Error> @@ -585,31 +587,17 @@ where let (msgs, smsgs) = api.read().await.messages_for_block(b)?; for msg in smsgs { - rm( - msg.from(), - pending.clone(), - msg.sequence(), - rmsgs.borrow_mut(), - ) - .await?; + rm(msg.from(), pending, msg.sequence(), rmsgs.borrow_mut()).await?; } for msg in msgs { - rm( - msg.from(), - pending.clone(), - msg.sequence(), - rmsgs.borrow_mut(), - ) - .await?; + rm(msg.from(), pending, msg.sequence(), rmsgs.borrow_mut()).await?; } } *cur_tipset.write().await = ts; } for (_, hm) in rmsgs { for (_, msg) in hm { - if let Err(e) = - add_helper(api.as_ref(), bls_sig_cache.as_ref(), pending.as_ref(), msg).await - { + if let Err(e) = add_helper(api, bls_sig_cache, pending, msg).await { error!("Failed to readd message from reorg to mpool: {}", e); } } @@ -621,7 +609,7 @@ where /// from the rmsgs hashmap. Also remove the from address and sequence from the mmessagepool. async fn rm( from: &Address, - pending: Arc>>, + pending: &RwLock>, sequence: u64, rmsgs: &mut HashMap>, ) -> Result<(), Error> { @@ -631,7 +619,7 @@ async fn rm( } remove(from, pending, sequence).await?; } else { - remove(from, pending.clone(), sequence).await?; + remove(from, pending, sequence).await?; } Ok(()) } @@ -688,7 +676,7 @@ mod tests { } pub fn set_block_messages(&mut self, h: &BlockHeader, msgs: Vec) { - self.bmsgs.insert(h.cid().clone(), msgs.clone()); + self.bmsgs.insert(h.cid().clone(), msgs); self.tipsets.push(Tipset::new(vec![h.clone()]).unwrap()) } @@ -881,10 +869,10 @@ mod tests { let cur_tipset = mpool.cur_tipset.clone(); head_change( - api, - bls_sig_cache, - pending, - cur_tipset, + api.as_ref(), + bls_sig_cache.as_ref(), + pending.as_ref(), + cur_tipset.as_ref(), Vec::new(), vec![Tipset::new(vec![a]).unwrap()], ) @@ -938,10 +926,10 @@ mod tests { let cur_tipset = mpool.cur_tipset.clone(); head_change( - api.clone(), - bls_sig_cache.clone(), - pending.clone(), - cur_tipset.clone(), + api.as_ref(), + bls_sig_cache.as_ref(), + pending.as_ref(), + cur_tipset.as_ref(), Vec::new(), vec![Tipset::new(vec![a]).unwrap()], ) @@ -958,10 +946,10 @@ mod tests { let cur_tipset = mpool.cur_tipset.clone(); head_change( - api.clone(), - bls_sig_cache.clone(), - pending.clone(), - cur_tipset.clone(), + api.as_ref(), + bls_sig_cache.as_ref(), + pending.as_ref(), + cur_tipset.as_ref(), Vec::new(), vec![Tipset::new(vec![b.clone()]).unwrap()], ) @@ -973,10 +961,10 @@ mod tests { mpool.api.write().await.set_state_sequence(&sender, 0); head_change( - api, - bls_sig_cache, - pending, - cur_tipset, + api.as_ref(), + bls_sig_cache.as_ref(), + pending.as_ref(), + cur_tipset.as_ref(), vec![Tipset::new(vec![b]).unwrap()], Vec::new(), ) diff --git a/crypto/src/signature.rs b/crypto/src/signature.rs index 5d060aaaefa5..3ce041b3beb4 100644 --- a/crypto/src/signature.rs +++ b/crypto/src/signature.rs @@ -18,7 +18,9 @@ pub const BLS_SIG_LEN: usize = 96; pub const BLS_PUB_LEN: usize = 48; /// Signature variants for Forest signatures -#[derive(Clone, Debug, PartialEq, FromPrimitive, Copy, Eq, Serialize_repr, Deserialize_repr)] +#[derive( + Clone, Debug, PartialEq, FromPrimitive, Copy, Eq, Serialize_repr, Deserialize_repr, Hash, +)] #[repr(u8)] pub enum SignatureType { Secp256k1 = 1, @@ -40,7 +42,7 @@ impl SignatureType { } /// A cryptographic signature, represented in bytes, of any key protocol -#[derive(Clone, Debug, PartialEq, Default, Eq)] +#[derive(Clone, Debug, PartialEq, Default, Eq, Hash)] pub struct Signature { sig_type: SignatureType, bytes: Vec, diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index 1e2963c2d001..1d286175b800 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -9,7 +9,7 @@ use encoding::Cbor; use vm::{MethodNum, Serialized, TokenAmount}; /// Represents a wrapped message with signature bytes -#[derive(PartialEq, Clone, Debug, Serialize_tuple, Deserialize_tuple)] +#[derive(PartialEq, Clone, Debug, Serialize_tuple, Deserialize_tuple, Hash, Eq)] pub struct SignedMessage { message: UnsignedMessage, signature: Signature, diff --git a/vm/message/src/unsigned_message.rs b/vm/message/src/unsigned_message.rs index 9fefbc4aebe6..031987c5c1a7 100644 --- a/vm/message/src/unsigned_message.rs +++ b/vm/message/src/unsigned_message.rs @@ -39,7 +39,7 @@ use vm::{MethodNum, Serialized, TokenAmount}; /// let msg = message_builder.build().unwrap(); /// assert_eq!(msg.sequence(), 1); /// ``` -#[derive(PartialEq, Clone, Debug, Builder)] +#[derive(PartialEq, Clone, Debug, Builder, Hash, Eq)] #[builder(name = "MessageBuilder")] pub struct UnsignedMessage { #[builder(default)] diff --git a/vm/src/method.rs b/vm/src/method.rs index eb5cd6743f9d..eff5ca8d576d 100644 --- a/vm/src/method.rs +++ b/vm/src/method.rs @@ -14,7 +14,7 @@ pub const METHOD_SEND: MethodNum = 0; pub const METHOD_CONSTRUCTOR: MethodNum = 1; /// Serialized bytes to be used as parameters into actor methods -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)] #[serde(transparent)] pub struct Serialized { #[serde(with = "serde_bytes")]