diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index fc5723a506..37606c6f5a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -135,13 +135,13 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Download executables AMD - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries-amd64-${{ matrix.just_variants }} path: target/amd64/debug/examples - name: Download executables ARM - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries-aarch64-${{ matrix.just_variants }} path: target/arm64/debug/examples diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 63f8d93ac4..5318fe8061 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -191,7 +191,7 @@ impl> SystemContext { // insert genesis (or latest block) to state map let mut validated_state_map = BTreeMap::default(); - let validated_state = TYPES::ValidatedState::genesis(&instance_state); + let validated_state = Arc::new(TYPES::ValidatedState::genesis(&instance_state)); validated_state_map.insert( anchored_leaf.get_view_number(), View { @@ -345,11 +345,11 @@ impl> SystemContext { .map(|guard| guard.get_decided_leaf()) } - /// Returns a copy of the last decided validated state. + /// Returns the last decided validated state. /// /// # Panics /// Panics if internal state for consensus is inconsistent - pub async fn get_decided_state(&self) -> TYPES::ValidatedState { + pub async fn get_decided_state(&self) -> Arc { self.inner .consensus .read() @@ -358,6 +358,17 @@ impl> SystemContext { .clone() } + /// Get the validated state from a given `view`. + /// + /// Returns the requested state, if the [`SystemContext`] is tracking this view. Consensus + /// tracks views that have not yet been decided but could be in the future. This function may + /// return [`None`] if the requested view has already been decided (but see + /// [`get_decided_state`](Self::get_decided_state)) or if there is no path for the requested + /// view to ever be decided. + pub async fn get_state(&self, view: TYPES::Time) -> Option> { + self.inner.consensus.read().await.get_state(view).cloned() + } + /// Initializes a new [`SystemContext`] and does the work of setting up all the background tasks /// /// Assumes networking implementation is already primed. diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 20f6daf059..862fa8d27a 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -90,10 +90,21 @@ impl + 'static> SystemContextHandl /// /// # Panics /// If the internal consensus is in an inconsistent state. - pub async fn get_decided_state(&self) -> TYPES::ValidatedState { + pub async fn get_decided_state(&self) -> Arc { self.hotshot.get_decided_state().await } + /// Get the validated state from a given `view`. + /// + /// Returns the requested state, if the [`SystemContext`] is tracking this view. Consensus + /// tracks views that have not yet been decided but could be in the future. This function may + /// return [`None`] if the requested view has already been decided (but see + /// [`get_decided_state`](Self::get_decided_state)) or if there is no path for the requested + /// view to ever be decided. + pub async fn get_state(&self, view: TYPES::Time) -> Option> { + self.hotshot.get_state(view).await + } + /// Get the last decided leaf of the [`SystemContext`] instance. /// /// # Panics diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 9c35a62abe..34ad675ee2 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -538,9 +538,9 @@ impl, A: ConsensusApi + block_payload: None, proposer_id: sender, }; - let state = ::from_header( + let state = Arc::new(::from_header( &proposal.data.block_header, - ); + )); consensus.validated_state_map.insert( view, @@ -600,6 +600,7 @@ impl, A: ConsensusApi + error!("Block header doesn't extend the proposal",); return; }; + let state = Arc::new(state); let parent_commitment = parent.commit(); let leaf: Leaf<_> = Leaf { view_number: view, diff --git a/crates/testing/src/task_helpers.rs b/crates/testing/src/task_helpers.rs index 73068e94eb..a8c752a1d4 100644 --- a/crates/testing/src/task_helpers.rs +++ b/crates/testing/src/task_helpers.rs @@ -42,7 +42,7 @@ use hotshot_types::vote::Certificate; use hotshot_types::vote::Vote; use serde::Serialize; -use std::{fmt::Debug, hash::Hash}; +use std::{fmt::Debug, hash::Hash, sync::Arc}; /// create the [`SystemContextHandle`] from a node id /// # Panics @@ -238,10 +238,11 @@ async fn build_quorum_proposal_and_signature( .quorum_membership .total_nodes(), ); - let mut parent_state = - ::from_header(&parent_leaf.block_header); + let mut parent_state = Arc::new(::from_header( + &parent_leaf.block_header, + )); let block_header = TestBlockHeader::new( - &parent_state, + &*parent_state, &TestInstanceState {}, &parent_leaf.block_header, payload_commitment, @@ -269,9 +270,11 @@ async fn build_quorum_proposal_and_signature( // Only view 2 is tested, higher views are not tested for cur_view in 2..=view { - let state_new_view = parent_state - .validate_and_apply_header(&TestInstanceState {}, &block_header, &block_header) - .unwrap(); + let state_new_view = Arc::new( + parent_state + .validate_and_apply_header(&TestInstanceState {}, &block_header, &block_header) + .unwrap(), + ); // save states for the previous view to pass all the qc checks // In the long term, we want to get rid of this, do not manually update consensus state consensus.validated_state_map.insert( diff --git a/crates/types/src/consensus.rs b/crates/types/src/consensus.rs index 6c62cb3d1b..d668cd8f39 100644 --- a/crates/types/src/consensus.rs +++ b/crates/types/src/consensus.rs @@ -341,7 +341,7 @@ impl Consensus { /// Gets the validated state with the given view number, if in the state map. #[must_use] - pub fn get_state(&self, view_number: TYPES::Time) -> Option<&TYPES::ValidatedState> { + pub fn get_state(&self, view_number: TYPES::Time) -> Option<&Arc> { match self.validated_state_map.get(&view_number) { Some(view) => view.get_state(), None => None, @@ -354,7 +354,7 @@ impl Consensus { /// If the last decided view's state does not exist in the state map, which should never /// happen. #[must_use] - pub fn get_decided_state(&self) -> &TYPES::ValidatedState { + pub fn get_decided_state(&self) -> &Arc { let decided_view_num = self.last_decided_view; self.get_state(decided_view_num) .expect("Decided state not found! Consensus internally inconsistent") diff --git a/crates/types/src/traits/states.rs b/crates/types/src/traits/states.rs index 0d7d5fe5ee..522793aa1f 100644 --- a/crates/types/src/traits/states.rs +++ b/crates/types/src/traits/states.rs @@ -22,7 +22,7 @@ pub trait InstanceState: Clone + Debug + Send + Sync {} /// produce a new state, with the modifications from the block applied /// ([`validate_and_apply_header`](`ValidatedState::validate_and_apply_header)) pub trait ValidatedState: - Serialize + DeserializeOwned + Clone + Debug + Default + Hash + PartialEq + Eq + Send + Sync + Serialize + DeserializeOwned + Debug + Default + Hash + PartialEq + Eq + Send + Sync { /// The error type for this particular type of ledger state type Error: Error + Debug + Send + Sync; diff --git a/crates/types/src/utils.rs b/crates/types/src/utils.rs index 46b33c1e0d..f55aca47fa 100644 --- a/crates/types/src/utils.rs +++ b/crates/types/src/utils.rs @@ -8,7 +8,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use commit::Commitment; use digest::OutputSizeUser; use sha2::Digest; -use std::ops::Deref; +use std::{ops::Deref, sync::Arc}; use tagged_base64::tagged; use typenum::Unsigned; @@ -27,17 +27,21 @@ pub enum ViewInner { /// Undecided view Leaf { /// Proposed leaf - leaf: Commitment>, + leaf: LeafCommitment, /// Validated state. - state: TYPES::ValidatedState, + state: Arc, }, /// Leaf has failed Failed, } +/// The hash of a leaf. +pub type LeafCommitment = Commitment>; + impl ViewInner { /// Return the underlying undecide leaf view if it exists. - pub fn get_leaf(&self) -> Option<(Commitment>, &TYPES::ValidatedState)> { + #[must_use] + pub fn get_leaf(&self) -> Option<(LeafCommitment, &Arc)> { if let Self::Leaf { leaf, state } = self { Some((*leaf, state)) } else { @@ -47,7 +51,7 @@ impl ViewInner { /// return the underlying leaf hash if it exists #[must_use] - pub fn get_leaf_commitment(&self) -> Option>> { + pub fn get_leaf_commitment(&self) -> Option> { if let Self::Leaf { leaf, .. } = self { Some(*leaf) } else { @@ -57,7 +61,7 @@ impl ViewInner { /// return the underlying validated state if it exists #[must_use] - pub fn get_state(&self) -> Option<&TYPES::ValidatedState> { + pub fn get_state(&self) -> Option<&Arc> { if let Self::Leaf { state, .. } = self { Some(state) } else {