Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions src/chain/store/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use super::{
index::{ChainIndex, ResolveNullTipset},
tipset_tracker::TipsetTracker,
};
use crate::db::{EthMappingsStore, EthMappingsStoreExt};
use crate::interpreter::{BlockMessages, VMTrace};
use crate::libp2p_bitswap::{BitswapStoreRead, BitswapStoreReadWrite};
use crate::message::{ChainMessage, Message as MessageTrait, SignedMessage};
use crate::networks::{ChainConfig, Height};
Expand All @@ -23,7 +21,15 @@ use crate::{
blocks::{CachingBlockHeader, Tipset, TipsetKey, TxMeta},
db::HeaviestTipsetKeyProvider,
};
use crate::{
db::{EthMappingsStore, EthMappingsStoreExt},
rpc::chain::PathChange,
};
use crate::{fil_cns, utils::cache::SizeTrackingLruCache};
use crate::{
interpreter::{BlockMessages, VMTrace},
rpc::chain::PathChanges,
};
use ahash::{HashMap, HashMapExt, HashSet};
use anyhow::Context as _;
use cid::Cid;
Expand All @@ -47,17 +53,16 @@ pub type ChainEpochDelta = ChainEpoch;

/// `Enum` for `pubsub` channel that defines message type variant and data
/// contained in message type.
#[derive(Clone, Debug)]
pub enum HeadChange {
Apply(Tipset),
}
pub type HeadChange = PathChange<Tipset>;

pub type HeadChanges = PathChanges<Tipset>;

/// Stores chain data such as heaviest tipset and cached tipset info at each
/// epoch. This structure is thread-safe, and all caches are wrapped in a mutex
/// to allow a consistent `ChainStore` to be shared across tasks.
pub struct ChainStore<DB> {
/// Publisher for head change events
publisher: Publisher<HeadChange>,
publisher: Publisher<HeadChanges>,

/// key-value `datastore`.
db: Arc<DB>,
Expand All @@ -66,7 +71,7 @@ pub struct ChainStore<DB> {
heaviest_tipset_key_provider: Arc<dyn HeaviestTipsetKeyProvider + Sync + Send>,

/// Heaviest tipset cache
heaviest_tipset_cache: Arc<RwLock<Option<Tipset>>>,
heaviest_tipset_cache: Arc<RwLock<Tipset>>,

/// Used as a cache for tipset `lookbacks`.
chain_index: Arc<ChainIndex<Arc<DB>>>,
Expand Down Expand Up @@ -124,14 +129,24 @@ where
let (publisher, _) = broadcast::channel(SINK_CAP);
let chain_index = Arc::new(ChainIndex::new(Arc::clone(&db)));
let validated_blocks = Mutex::new(HashSet::default());

let head = if let Some(head_tsk) = heaviest_tipset_key_provider
.heaviest_tipset_key()
.context("failed to load head tipset key")?
&& let Some(head) = chain_index
.load_tipset(&head_tsk)
.context("failed to load head tipset")?
{
head
} else {
Tipset::from(&genesis_block_header)
};
let cs = Self {
publisher,
chain_index,
tipset_tracker: TipsetTracker::new(Arc::clone(&db), chain_config.clone()),
db,
heaviest_tipset_key_provider,
heaviest_tipset_cache: Default::default(),
heaviest_tipset_cache: Arc::new(RwLock::new(head)),
genesis_block_header,
validated_blocks,
eth_mappings,
Expand All @@ -142,14 +157,31 @@ where
}

/// Sets heaviest tipset
pub fn set_heaviest_tipset(&self, ts: Tipset) -> Result<(), Error> {
pub fn set_heaviest_tipset(&self, head: Tipset) -> Result<(), Error> {
head.key().save(self.blockstore())?;
self.heaviest_tipset_key_provider
.set_heaviest_tipset_key(ts.key())?;
*self.heaviest_tipset_cache.write() = Some(ts.clone());
ts.key().save(self.blockstore())?;
if self.publisher.send(HeadChange::Apply(ts)).is_err() {
debug!("did not publish head change, no active receivers");
.set_heaviest_tipset_key(head.key())?;
let old_head = std::mem::replace(&mut *self.heaviest_tipset_cache.write(), head.clone());

let changes = match crate::rpc::chain::chain_get_path(self, old_head.key(), head.key()) {
Ok(changes) => changes,
Err(e) => {
// Do not warn when the old head is genesis
if old_head.epoch() > 0 {
warn!("failed to get chain path changes: {e}");
}
// Fallback to single apply
PathChanges {
applies: vec![head],
reverts: vec![],
}
}
};

if self.publisher.send(changes).is_err() {
debug!("did not publish changes, no active receivers");
}

Ok(())
}

Expand Down Expand Up @@ -200,16 +232,7 @@ where

/// Returns the currently tracked heaviest tipset.
pub fn heaviest_tipset(&self) -> Tipset {
if let Some(ts) = &*self.heaviest_tipset_cache.read() {
return ts.clone();
}
let tsk = self
.heaviest_tipset_key_provider
.heaviest_tipset_key()
.unwrap_or_else(|_| TipsetKey::from(nunny::vec![*self.genesis_block_header.cid()]));
self.chain_index
.load_required_tipset(&tsk)
.expect("failed to load heaviest tipset")
self.heaviest_tipset_cache.read().clone()
}

/// Returns the genesis tipset.
Expand All @@ -218,7 +241,7 @@ where
}

/// Returns a reference to the publisher of head changes.
pub fn publisher(&self) -> &Publisher<HeadChange> {
pub fn publisher(&self) -> &Publisher<HeadChanges> {
&self.publisher
}

Expand Down
15 changes: 6 additions & 9 deletions src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub mod db_util;
pub mod main;

use crate::blocks::Tipset;
use crate::chain::HeadChange;
use crate::chain::index::ResolveNullTipset;
use crate::chain_sync::network_context::SyncNetworkContext;
use crate::chain_sync::{ChainFollower, SyncStatus};
Expand Down Expand Up @@ -505,14 +504,12 @@ fn maybe_start_indexer_service(

// Continuously listen for head changes
loop {
let HeadChange::Apply(ts) = receiver.recv().await?;

tracing::debug!("Indexing tipset {}", ts.key());

let delegated_messages =
chain_store.headers_delegated_messages(ts.block_headers().iter())?;

chain_store.process_signed_messages(&delegated_messages)?;
for ts in receiver.recv().await?.applies {
tracing::debug!("Indexing tipset {}", ts.key());
let delegated_messages =
chain_store.headers_delegated_messages(ts.block_headers().iter())?;
chain_store.process_signed_messages(&delegated_messages)?;
}
}
});

Expand Down
12 changes: 6 additions & 6 deletions src/db/car/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ impl<WriterT> ManyCar<WriterT> {
Ok(())
}

pub fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey> {
self.read_only
pub fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
Ok(self
.read_only
.read()
.peek()
.map(|w| AnyCar::heaviest_tipset_key(&w.car))
.context("ManyCar store doesn't have a heaviest tipset key")
.map(|w| AnyCar::heaviest_tipset_key(&w.car)))
}

pub fn heaviest_tipset(&self) -> anyhow::Result<Tipset> {
Expand Down Expand Up @@ -252,9 +252,9 @@ impl<WriterT: EthMappingsStore> EthMappingsStore for ManyCar<WriterT> {
}

impl<T: Blockstore + SettingsStore> super::super::HeaviestTipsetKeyProvider for ManyCar<T> {
fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey> {
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
match SettingsStoreExt::read_obj::<TipsetKey>(self, crate::db::setting_keys::HEAD_KEY)? {
Some(tsk) => Ok(tsk),
Some(tsk) => Ok(Some(tsk)),
None => self.heaviest_tipset_key(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/db/gc/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ where
tracing::warn!("{e}");
}

*self.memory_db_head_key.write() = db.heaviest_tipset_key().ok();
*self.memory_db_head_key.write() = db.heaviest_tipset_key()?;
db.unsubscribe_write_ops();
match joinset.join_next().await {
Some(Ok(map)) => {
Expand Down
5 changes: 2 additions & 3 deletions src/db/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,8 @@ impl BitswapStoreReadWrite for MemoryDB {
}

impl super::HeaviestTipsetKeyProvider for MemoryDB {
fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey> {
SettingsStoreExt::read_obj::<TipsetKey>(self, crate::db::setting_keys::HEAD_KEY)?
.context("head key not found")
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
SettingsStoreExt::read_obj::<TipsetKey>(self, crate::db::setting_keys::HEAD_KEY)
}

fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()> {
Expand Down
4 changes: 2 additions & 2 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,14 @@ impl<T: PersistentStore> PersistentStore for &Arc<T> {

pub trait HeaviestTipsetKeyProvider {
/// Returns the currently tracked heaviest tipset.
fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey>;
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>>;

/// Sets heaviest tipset.
fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()>;
}

impl<T: HeaviestTipsetKeyProvider> HeaviestTipsetKeyProvider for Arc<T> {
fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey> {
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
self.as_ref().heaviest_tipset_key()
}

Expand Down
5 changes: 2 additions & 3 deletions src/db/parity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,8 @@ impl SettingsStore for ParityDb {
}

impl super::HeaviestTipsetKeyProvider for ParityDb {
fn heaviest_tipset_key(&self) -> anyhow::Result<TipsetKey> {
super::SettingsStoreExt::read_obj::<TipsetKey>(self, super::setting_keys::HEAD_KEY)?
.context("head key not found")
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
super::SettingsStoreExt::read_obj::<TipsetKey>(self, super::setting_keys::HEAD_KEY)
}

fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()> {
Expand Down
21 changes: 17 additions & 4 deletions src/message_pool/msgpool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,25 @@ where
let mut repub = false;
let mut rmsgs: HashMap<Address, HashMap<u64, SignedMessage>> = HashMap::new();
for ts in revert {
let pts = api.load_tipset(ts.parents())?;
let Ok(pts) = api.load_tipset(ts.parents()) else {
tracing::error!("error loading reverted tipset parent");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continue;
};
*cur_tipset.write() = pts;

let mut msgs: Vec<SignedMessage> = Vec::new();
for block in ts.block_headers() {
let (umsg, smsgs) = api.messages_for_block(block)?;
let Ok((umsg, smsgs)) = api.messages_for_block(block) else {
tracing::error!("error retrieving messages for reverted block");
continue;
};
msgs.extend(smsgs);
for msg in umsg {
let smsg = recover_sig(bls_sig_cache, msg)?;
let msg_cid = msg.cid();
let Ok(smsg) = recover_sig(bls_sig_cache, msg) else {
tracing::debug!("could not recover signature for bls message {}", msg_cid);
continue;
};
msgs.push(smsg)
}
}
Expand All @@ -246,7 +256,10 @@ where

for ts in apply {
for b in ts.block_headers() {
let (msgs, smsgs) = api.messages_for_block(b)?;
let Ok((msgs, smsgs)) = api.messages_for_block(b) else {
tracing::error!("error retrieving messages for block");
continue;
};

for msg in smsgs {
remove_from_selected_msgs(
Expand Down
25 changes: 11 additions & 14 deletions src/message_pool/msgpool/msg_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use std::{num::NonZeroUsize, sync::Arc, time::Duration};

use crate::blocks::{CachingBlockHeader, Tipset};
use crate::chain::{HeadChange, MINIMUM_BASE_FEE};
use crate::chain::{HeadChanges, MINIMUM_BASE_FEE};
#[cfg(test)]
use crate::db::SettingsStore;
use crate::eth::is_valid_eth_tx_for_sending;
Expand Down Expand Up @@ -551,34 +551,31 @@ where
let pending = mp.pending.clone();
let republished = mp.republished.clone();

let cur_tipset = mp.cur_tipset.clone();
let current_ts = mp.cur_tipset.clone();
let repub_trigger = mp.repub_trigger.clone();

// Reacts to new HeadChanges
services.spawn(async move {
loop {
match subscriber.recv().await {
Ok(ts) => {
let (cur, rev, app) = match ts {
HeadChange::Apply(tipset) => {
(cur_tipset.clone(), Vec::new(), vec![tipset])
}
};
head_change(
Ok(HeadChanges { reverts, applies }) => {
if let Err(e) = head_change(
api.as_ref(),
bls_sig_cache.as_ref(),
repub_trigger.clone(),
republished.as_ref(),
pending.as_ref(),
cur.as_ref(),
rev,
app,
&current_ts,
reverts,
applies,
)
.await
.context("Error changing head")?;
{
tracing::warn!("Error changing head: {e}");
}
}
Err(RecvError::Lagged(e)) => {
warn!("Head change subscriber lagged: skipping {} events", e);
warn!("Head change subscriber lagged: skipping {e} events");
}
Err(RecvError::Closed) => {
break Ok(());
Expand Down
8 changes: 4 additions & 4 deletions src/message_pool/msgpool/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::sync::Arc;

use crate::blocks::{CachingBlockHeader, Tipset, TipsetKey};
use crate::chain::HeadChange;
use crate::chain::HeadChanges;
use crate::message::{ChainMessage, SignedMessage};
use crate::message_pool::msg_pool::{
MAX_ACTOR_PENDING_MESSAGES, MAX_UNTRUSTED_ACTOR_PENDING_MESSAGES,
Expand All @@ -31,7 +31,7 @@ use crate::message_pool::errors::Error;
#[async_trait]
pub trait Provider {
/// Update `Mpool`'s `cur_tipset` whenever there is a change to the provider
fn subscribe_head_changes(&self) -> Subscriber<HeadChange>;
fn subscribe_head_changes(&self) -> Subscriber<HeadChanges>;
/// Get the heaviest Tipset in the provider
fn get_heaviest_tipset(&self) -> Tipset;
/// Add a message to the `MpoolProvider`, return either Cid or Error
Expand Down Expand Up @@ -64,7 +64,7 @@ pub trait Provider {
/// `mpool` RPC.
#[derive(derive_more::Constructor)]
pub struct MpoolRpcProvider<DB> {
subscriber: Publisher<HeadChange>,
subscriber: Publisher<HeadChanges>,
sm: Arc<StateManager<DB>>,
}

Expand All @@ -73,7 +73,7 @@ impl<DB> Provider for MpoolRpcProvider<DB>
where
DB: Blockstore + Sync + Send + 'static,
{
fn subscribe_head_changes(&self) -> Subscriber<HeadChange> {
fn subscribe_head_changes(&self) -> Subscriber<HeadChanges> {
self.subscriber.subscribe()
}

Expand Down
Loading
Loading