Skip to content

Commit

Permalink
sct(component): implement Component trait and split modules (#3723)
Browse files Browse the repository at this point in the history
* sct(view): remove catch-all view module

* sct(component): remove top-level module sig

* sct(component): add `component::clock`

I am not completely sold on the name, but the idea is that what you
find here are extension traits that let you manage the blockchain
clocks. Those clocks can be logical e.g. a block or an epoch, or
literal wall clock (recorded by block timestamps, and eventually
PBTS).

In particular, we define `EpochManager` and `EpochRead` which
implement write and read capabiltiies respectively.

* sct(component): add `component::Tree`

In this module, we find composable extension traits that let us
interface with the chain's commitment tree. This is done by plugging
into `SctRead` or `SctManager`.

We also add a third interface (`tree:VerificationExt`) which action
handlers can pull in order to perform SCT specific verification of
anchors or nullifiers.

* sct(component): move `SourceContext`

This is a purely mechanical refactor, and might be better to
merge into the tree module to increase cohesion.

* sct(component): update query service imports

* sct(component): implement `cnidarium_component::Component` on the sct

* sct(component): propagate api changes

* sct(component): sketch out updated state key schema

* app: use `Sct::init_chain` and `Sct::begin_block`

* sct(component): track module definition

* sct(component): use updated state key schema

* penumbra: misc small fixes
  • Loading branch information
erwanor authored Feb 2, 2024
1 parent c1bb046 commit 8a3733e
Show file tree
Hide file tree
Showing 42 changed files with 631 additions and 497 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/bin/pcli/src/command/query/shielded_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ impl ShieldedPool {
pub fn key(&self) -> String {
use penumbra_sct::state_key as sct_state_key;
match self {
ShieldedPool::Anchor { height } => sct_state_key::anchor_by_height(*height),
ShieldedPool::Anchor { height } => sct_state_key::tree::anchor_by_height(*height),
ShieldedPool::CompactBlock { .. } => {
unreachable!("should be handled at outer level via rpc");
}
ShieldedPool::Commitment { commitment } => sct_state_key::note_source(commitment),
ShieldedPool::Commitment { commitment } => sct_state_key::tree::note_source(commitment),
ShieldedPool::Nullifier { nullifier } => {
sct_state_key::spent_nullifier_lookup(nullifier)
sct_state_key::nullifier_set::spent_nullifier_lookup(nullifier)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bin/pd/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::path::PathBuf;
use cnidarium::{StateDelta, StateWrite, Storage};
use jmt::RootHash;
use penumbra_app::{genesis, SUBSTORE_PREFIXES};
use penumbra_sct::component::{EpochManager, EpochRead};
use penumbra_sct::component::clock::{EpochManager, EpochRead};
use penumbra_stake::{genesis::Content as StakeContent, StateReadExt as _};

use crate::testnet::generate::TestnetConfig;
Expand Down
5 changes: 3 additions & 2 deletions crates/core/app/src/action_handler/actions/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use penumbra_community_pool::component::StateReadExt as _;
use penumbra_ibc::component::ClientStateReadExt;
use penumbra_keys::keys::{FullViewingKey, NullifierKey};
use penumbra_proto::DomainType;
use penumbra_sct::component::{EpochRead, StateReadExt as _};
use penumbra_sct::component::clock::EpochRead;
use penumbra_sct::component::tree::SctRead;
use penumbra_shielded_pool::component::SupplyWrite;

use penumbra_transaction::plan::TransactionPlan;
Expand Down Expand Up @@ -293,7 +294,7 @@ impl ActionHandler for ProposalSubmit {

// Compute the effective starting TCT position for the proposal, by rounding the current
// position down to the start of the block.
let Some(sct_position) = state.state_commitment_tree().await.position() else {
let Some(sct_position) = state.get_sct().await.position() else {
anyhow::bail!("state commitment tree is full");
};
// All proposals start are considered to start at the beginning of the block, because this
Expand Down
2 changes: 1 addition & 1 deletion crates/core/app/src/action_handler/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
use cnidarium::{StateRead, StateWrite};
use penumbra_sct::{component::SourceContext as _, CommitmentSource};
use penumbra_sct::{component::source::SourceContext, CommitmentSource};
use penumbra_transaction::Transaction;
use tokio::task::JoinSet;
use tracing::{instrument, Instrument};
Expand Down
26 changes: 12 additions & 14 deletions crates/core/app/src/action_handler/transaction/stateful.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
use anyhow::Result;
use cnidarium::StateRead;
use penumbra_fee::component::StateReadExt as _;
use penumbra_sct::component::{EpochRead, StateReadExt as _};
use penumbra_sct::component::clock::EpochRead;
use penumbra_sct::component::tree::VerificationExt;
use penumbra_shielded_pool::component::StateReadExt as _;
use penumbra_shielded_pool::fmd;
use penumbra_transaction::gas::GasCost;
use penumbra_transaction::Transaction;

pub(super) async fn claimed_anchor_is_valid<S: StateRead>(
state: S,
transaction: &Transaction,
) -> Result<()> {
state.check_claimed_anchor(transaction.anchor).await
}
const FMD_GRACE_PERIOD_BLOCKS: u64 = 10;

pub(super) async fn fmd_parameters_valid<S: StateRead>(
state: S,
transaction: &Transaction,
) -> Result<()> {
pub async fn fmd_parameters_valid<S: StateRead>(state: S, transaction: &Transaction) -> Result<()> {
let previous_fmd_parameters = state
.get_previous_fmd_parameters()
.await
Expand All @@ -35,8 +28,6 @@ pub(super) async fn fmd_parameters_valid<S: StateRead>(
)
}

const FMD_GRACE_PERIOD_BLOCKS: u64 = 10;

pub fn fmd_precision_within_grace_period(
tx: &Transaction,
previous_fmd_parameters: fmd::Parameters,
Expand Down Expand Up @@ -64,7 +55,14 @@ pub fn fmd_precision_within_grace_period(
Ok(())
}

pub(super) async fn fee_greater_than_base_fee<S: StateRead>(
pub async fn claimed_anchor_is_valid<S: StateRead>(
state: S,
transaction: &Transaction,
) -> Result<()> {
state.check_claimed_anchor(transaction.anchor).await
}

pub async fn fee_greater_than_base_fee<S: StateRead>(
state: S,
transaction: &Transaction,
) -> Result<()> {
Expand Down
51 changes: 16 additions & 35 deletions crates/core/app/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use penumbra_ibc::component::{Ibc, StateWriteExt as _};
use penumbra_ibc::StateReadExt as _;
use penumbra_proto::core::app::v1alpha1::TransactionsByHeightResponse;
use penumbra_proto::DomainType;
use penumbra_sct::component::{EpochManager, EpochRead, SctParameterWriter, StateReadExt as _};
use penumbra_sct::component::clock::EpochRead;
use penumbra_sct::component::sct::Sct;
use penumbra_sct::component::{StateReadExt as _, StateWriteExt as _};
use penumbra_sct::epoch::Epoch;
use penumbra_shielded_pool::component::{ShieldedPool, StateReadExt as _, StateWriteExt as _};
use penumbra_stake::component::{Staking, StateReadExt as _, StateWriteExt as _, ValidatorUpdates};
Expand Down Expand Up @@ -98,29 +100,7 @@ impl App {
match app_state {
genesis::AppState::Content(genesis) => {
state_tx.put_chain_id(genesis.chain_id.clone());
state_tx.put_sct_params(genesis.sct_content.sct_params.clone()); // TODO(erwan): promote Sct to component?

// The genesis block height is 0
state_tx.put_block_height(0);

state_tx.put_epoch_by_height(
0,
Epoch {
index: 0,
start_height: 0,
},
);

// We need to set the epoch for the first block as well, since we set
// the epoch by height in end_block, and end_block isn't called after init_chain.
state_tx.put_epoch_by_height(
1,
Epoch {
index: 0,
start_height: 0,
},
);

Sct::init_chain(&mut state_tx, Some(&genesis.sct_content)).await;
ShieldedPool::init_chain(&mut state_tx, Some(&genesis.shielded_pool_content)).await;
Distributions::init_chain(&mut state_tx, Some(&genesis.distributions_content))
.await;
Expand Down Expand Up @@ -219,11 +199,6 @@ impl App {
pub async fn begin_block(&mut self, begin_block: &request::BeginBlock) -> Vec<abci::Event> {
let mut state_tx = StateDelta::new(self.state.clone());

// store the block height
state_tx.put_block_height(begin_block.header.height.into());
// store the block time
state_tx.put_block_timestamp(begin_block.header.time);

// If a app parameter change is scheduled for this block, apply it here, before any other
// component has executed. This ensures that app parameter changes are consistently
// applied precisely at the boundary between blocks:
Expand Down Expand Up @@ -266,6 +241,7 @@ impl App {

// Run each of the begin block handlers for each component, in sequence:
let mut arc_state_tx = Arc::new(state_tx);
Sct::begin_block(&mut arc_state_tx, begin_block).await;
ShieldedPool::begin_block(&mut arc_state_tx, begin_block).await;
Distributions::begin_block(&mut arc_state_tx, begin_block).await;
Ibc::begin_block::<PenumbraHost, StateDelta<Arc<StateDelta<cnidarium::Snapshot>>>>(
Expand Down Expand Up @@ -461,17 +437,17 @@ impl App {
.await
.expect("able to get block height in end_block");
let current_epoch = state_tx
.current_epoch()
.get_current_epoch()
.await
.expect("able to get current epoch in end_block");

let is_end_epoch = current_epoch.is_scheduled_epoch_end(
current_height,
state_tx
.get_epoch_duration()
.get_epoch_duration_parameter()
.await
.expect("able to get epoch duration in end_block"),
) || state_tx.epoch_ending_early();
) || state_tx.is_epoch_ending_early().await;

// If a chain upgrade is scheduled for this block, we trigger an early epoch change
// so that the upgraded chain starts at a clean epoch boundary.
Expand Down Expand Up @@ -522,7 +498,8 @@ impl App {
.expect("must be able to finish compact block");

// set the epoch for the next block
state_tx.put_epoch_by_height(
penumbra_sct::component::clock::EpochManager::put_epoch_by_height(
&mut state_tx,
current_height + 1,
Epoch {
index: current_epoch.index + 1,
Expand All @@ -533,7 +510,11 @@ impl App {
self.apply(state_tx)
} else {
// set the epoch for the next block
state_tx.put_epoch_by_height(current_height + 1, current_epoch);
penumbra_sct::component::clock::EpochManager::put_epoch_by_height(
&mut state_tx,
current_height + 1,
current_epoch,
);

state_tx
.finish_block(state_tx.app_params_updated())
Expand Down Expand Up @@ -723,7 +704,7 @@ impl<
+ penumbra_governance::component::StateReadExt
+ penumbra_fee::component::StateReadExt
+ penumbra_community_pool::component::StateReadExt
+ penumbra_sct::component::EpochRead
+ penumbra_sct::component::clock::EpochRead
+ penumbra_ibc::component::StateReadExt
+ penumbra_distributions::component::StateReadExt
+ ?Sized,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/app/src/community_pool_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cnidarium::{StateRead, StateWrite};
use futures::{StreamExt, TryStreamExt};
use penumbra_governance::state_key;
use penumbra_proto::{StateReadProto, StateWriteProto};
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::clock::EpochRead;
use penumbra_transaction::Transaction;

// Note: These should live in `penumbra-governance` in the `StateReadExt` and `StateWriteExt`
Expand Down
4 changes: 2 additions & 2 deletions crates/core/app/src/mock_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cnidarium::StateRead;
use penumbra_compact_block::{component::StateReadExt as _, CompactBlock, StatePayload};
use penumbra_dex::swap::SwapPlaintext;
use penumbra_keys::FullViewingKey;
use penumbra_sct::component::StateReadExt as _;
use penumbra_sct::component::tree::SctRead;
use penumbra_shielded_pool::{note, Note};
use penumbra_tct as tct;

Expand Down Expand Up @@ -42,7 +42,7 @@ impl MockClient {
let (latest_height, root) = self.latest_height_and_sct_root();
anyhow::ensure!(latest_height == height, "latest height should be updated");
let expected_root = state
.anchor_by_height(height)
.get_anchor_by_height(height)
.await?
.ok_or_else(|| anyhow::anyhow!("missing sct anchor for height {}", height))?;
anyhow::ensure!(
Expand Down
2 changes: 1 addition & 1 deletion crates/core/app/src/penumbra_host_chain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use async_trait::async_trait;
use penumbra_ibc::component::HostInterface;
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::clock::EpochRead;

use crate::app::StateReadExt;

Expand Down
2 changes: 1 addition & 1 deletion crates/core/app/src/tests/spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use penumbra_compact_block::component::CompactBlockManager;
use penumbra_keys::{test_keys, PayloadKey};
use penumbra_num::Amount;
use penumbra_sct::{
component::{EpochManager, SourceContext},
component::{clock::EpochManager, source::SourceContext},
epoch::Epoch,
};
use penumbra_shielded_pool::{component::ShieldedPool, SpendPlan};
Expand Down
6 changes: 3 additions & 3 deletions crates/core/app/src/tests/swap_and_swap_claim.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ark_ff::UniformRand;
use penumbra_compact_block::component::CompactBlockManager as _;
use penumbra_sct::{
component::{EpochManager, EpochRead, SourceContext as _},
component::{clock::EpochManager, source::SourceContext as _, StateReadExt as _},
epoch::Epoch,
};
use std::{ops::Deref, sync::Arc};
Expand Down Expand Up @@ -94,7 +94,7 @@ async fn swap_and_swap_claim() -> anyhow::Result<()> {
// To do this, we need to have an auth path for the swap nft note, which
// means we have to synchronize a client's view of the test chain's SCT
// state.
let epoch_duration = state.get_epoch_duration().await?;
let epoch_duration = state.get_epoch_duration_parameter().await?;
let mut client = MockClient::new(test_keys::FULL_VIEWING_KEY.clone());
// TODO: generalize StateRead/StateWrite impls from impl for &S to impl for Deref<Target=S>
client.sync_to(1, state.deref()).await?;
Expand Down Expand Up @@ -205,7 +205,7 @@ async fn swap_claim_duplicate_nullifier_previous_transaction() {
state_tx.apply();

// 6. Create a SwapClaim action
let epoch_duration = state.get_epoch_duration().await.unwrap();
let epoch_duration = state.get_epoch_duration_parameter().await.unwrap();
let mut client = MockClient::new(test_keys::FULL_VIEWING_KEY.clone());
client.sync_to(1, state.deref()).await.unwrap();

Expand Down
6 changes: 3 additions & 3 deletions crates/core/component/compact-block/src/component/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use penumbra_dex::component::SwapManager as _;
use penumbra_fee::component::StateReadExt as _;
use penumbra_governance::StateReadExt as _;
use penumbra_proto::DomainType;
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::SctManager as _;
use penumbra_sct::component::StateReadExt as _;
use penumbra_sct::component::clock::EpochRead;
use penumbra_sct::component::tree::SctManager as _;
use penumbra_sct::component::tree::SctRead;
use penumbra_shielded_pool::component::NoteManager as _;
use tracing::instrument;

Expand Down
2 changes: 1 addition & 1 deletion crates/core/component/compact-block/src/component/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use penumbra_proto::core::component::compact_block::v1alpha1::{
query_service_server::QueryService, CompactBlockRangeRequest, CompactBlockRangeResponse,
CompactBlockRequest, CompactBlockResponse,
};
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::clock::EpochRead;
use tokio::sync::mpsc;
use tonic::Status;
use tracing::{instrument, Instrument};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cnidarium::{StateRead, StateWrite};
use cnidarium_component::ActionHandler;
use penumbra_proof_params::SWAP_PROOF_VERIFICATION_KEY;
use penumbra_proto::StateWriteProto;
use penumbra_sct::component::SourceContext as _;
use penumbra_sct::component::source::SourceContext;

use crate::{
component::{metrics, StateReadExt, StateWriteExt, SwapManager},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ use penumbra_txhash::TransactionContext;
use cnidarium::{StateRead, StateWrite};
use penumbra_proof_params::SWAPCLAIM_PROOF_VERIFICATION_KEY;
use penumbra_proto::StateWriteProto;
use penumbra_sct::component::{EpochRead, SctManager as _, SourceContext, StateReadExt as _};
use penumbra_sct::component::{
source::SourceContext,
tree::{SctManager, VerificationExt},
StateReadExt as _,
};
use penumbra_shielded_pool::component::NoteManager;

use crate::{
Expand Down Expand Up @@ -43,7 +47,7 @@ impl ActionHandler for SwapClaim {

// 1. Validate the epoch duration passed in the swap claim matches
// what we know.
let epoch_duration = state.get_epoch_duration().await?;
let epoch_duration = state.get_epoch_duration_parameter().await?;
let provided_epoch_duration = swap_claim.epoch_duration;
if epoch_duration != provided_epoch_duration {
anyhow::bail!("provided epoch duration does not match chain epoch duration");
Expand Down
2 changes: 1 addition & 1 deletion crates/core/component/dex/src/component/arb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use async_trait::async_trait;
use cnidarium::{StateDelta, StateWrite};
use penumbra_asset::{asset, Value};
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::clock::EpochRead;
use tracing::instrument;

use crate::{ExecutionCircuitBreaker, SwapExecution};
Expand Down
4 changes: 2 additions & 2 deletions crates/core/component/dex/src/component/dex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cnidarium::{StateRead, StateWrite};
use cnidarium_component::Component;
use penumbra_asset::{asset, STAKING_TOKEN_ASSET_ID};
use penumbra_proto::{StateReadProto, StateWriteProto};
use penumbra_sct::component::EpochRead;
use penumbra_sct::component::clock::EpochRead;
use tendermint::v0_37::abci;
use tracing::instrument;

Expand Down Expand Up @@ -41,7 +41,7 @@ impl Component for Dex {
state: &mut Arc<S>,
end_block: &abci::request::EndBlock,
) {
let current_epoch = state.current_epoch().await.expect("epoch is set");
let current_epoch = state.get_current_epoch().await.expect("epoch is set");

// For each batch swap during the block, calculate clearing prices and set in the JMT.
for (trading_pair, swap_flows) in state.swap_flows() {
Expand Down
2 changes: 1 addition & 1 deletion crates/core/component/dex/src/component/swap_manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use async_trait::async_trait;
use cnidarium::StateWrite;
use penumbra_sct::{component::SctManager as _, CommitmentSource};
use penumbra_sct::{component::tree::SctManager, CommitmentSource};
use penumbra_tct as tct;
use tracing::instrument;

Expand Down
2 changes: 1 addition & 1 deletion crates/core/component/dex/src/component/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub trait TempStorageExt: Sized {
#[async_trait]
impl TempStorageExt for TempStorage {
async fn apply_minimal_genesis(self) -> anyhow::Result<Self> {
use penumbra_sct::component::EpochManager as _;
use penumbra_sct::component::clock::EpochManager as _;
let mut state = StateDelta::new(self.latest_snapshot());

state.put_block_height(0);
Expand Down
Loading

0 comments on commit 8a3733e

Please sign in to comment.