Skip to content

Commit 840074e

Browse files
committed
Cache proof receipts across the proving pipeline
Wire the proof-receipt store into the transaction, batch, and aggregate provers so a replay (including a flip reorg back onto the same fork) reuses a cached receipt instead of re-proving. - Processor gains tx/batch/aggregator image-id accessors and an AggregatorArtifact type; artifacts are bounded Clone + Borsh so the scheduler can cache and restore them. - ScheduledTransaction and ScheduledBatch read/write their per-tx and per-batch receipts through the storage workers; ScheduledBundle owns the aggregate (settlement) receipt, keyed off its start coordinate with a batch as the storage gateway. - Receipts are keyed by (checkpoint, block_hash, image_id, ...) so competing forks stay distinct; the pruning worker drops a checkpoint's receipts across all forks and programs in one prefix scan. - The proving workers prove on a cache miss, wait for the receipt to be durable, then publish the artifact; the transaction worker proves inline so only one proof occupies the GPU at a time. ChainBlockMetadata supplies the block hash; a test-utils-gated BatchMetadata for u64 stands in for tests. Round-trips the tx/batch/agg receipt paths through the storage workers in a new scheduler test.
1 parent c32636f commit 840074e

35 files changed

Lines changed: 728 additions & 74 deletions

File tree

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ borsh = { workspace = true, default-features = false }
88
rkyv = { workspace = true, default-features = false }
99
vprogs-core-codec = { workspace = true }
1010
zerocopy = { workspace = true, features = ["derive"] }
11+
12+
[features]
13+
# Stand-in trait impls for tests only (e.g. `BatchMetadata for u64`); never enabled by the node.
14+
test-utils = []

core/types/src/batch_metadata.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,27 @@ use borsh::{BorshDeserialize, BorshSerialize};
44

55
/// Opaque metadata attached to each scheduler batch, supporting serialization.
66
///
7-
/// Implementors derive `BorshSerialize` and `BorshDeserialize`; the trait is implemented
8-
/// automatically via a blanket impl.
7+
/// Implementors derive `BorshSerialize` and `BorshDeserialize` and supply the chain block hash
8+
/// the batch was formed against via [`block_hash`](Self::block_hash), which keys the batch's
9+
/// proof receipts in the proof-receipt store.
910
pub trait BatchMetadata:
1011
BorshSerialize + BorshDeserialize + Clone + Debug + Default + Send + Sync + 'static
1112
{
13+
/// The chain block hash this batch was formed against, as raw bytes.
14+
///
15+
/// The proving workers fold it into each receipt's cache key so the same checkpoint index on
16+
/// two competing chains yields distinct keys.
17+
fn block_hash(&self) -> [u8; 32];
1218
}
1319

14-
impl<T> BatchMetadata for T where
15-
T: BorshSerialize + BorshDeserialize + Clone + Debug + Default + Send + Sync + 'static
16-
{
20+
/// A `u64` batch index doubles as minimal test metadata: its big-endian bytes stand in for a
21+
/// block hash, keeping per-index receipts distinct without a real chain. Gated behind `test-utils`
22+
/// so the node never treats a bare index as batch metadata.
23+
#[cfg(feature = "test-utils")]
24+
impl BatchMetadata for u64 {
25+
fn block_hash(&self) -> [u8; 32] {
26+
let mut bytes = [0u8; 32];
27+
bytes[..8].copy_from_slice(&self.to_be_bytes());
28+
bytes
29+
}
1730
}

l1/types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ kaspa-hashes.workspace = true
1010
kaspa-rpc-core.workspace = true
1111
kaspa-txscript.workspace = true
1212
kaspa-wrpc-client.workspace = true
13+
vprogs-core-types.workspace = true
1314
zerocopy.workspace = true

l1/types/src/chain_block_metadata.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use borsh::{BorshDeserialize, BorshSerialize};
22
use kaspa_rpc_core::{RpcHeader, RpcOptionalHeader};
3+
use vprogs_core_types::BatchMetadata;
34

45
use crate::{Hash, SettlementInfo};
56

@@ -39,6 +40,12 @@ pub struct ChainBlockMetadata {
3940
pub last_settlement: Option<SettlementInfo>,
4041
}
4142

43+
impl BatchMetadata for ChainBlockMetadata {
44+
fn block_hash(&self) -> [u8; 32] {
45+
self.hash.as_bytes()
46+
}
47+
}
48+
4249
impl From<&RpcHeader> for ChainBlockMetadata {
4350
/// Builds metadata from a regular RPC header, populating only the header-derived fields and
4451
/// leaving lane / parent-derived state at its default.

node/test-utils/src/vm.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,23 @@ impl<S: Store> Processor<S> for TestNodeVm {
2222
Ok(())
2323
}
2424

25+
// This test VM does not prove, so its receipt-cache image ids are unset.
26+
fn tx_image_id(&self) -> [u8; 32] {
27+
[0u8; 32]
28+
}
29+
30+
fn batch_image_id(&self) -> [u8; 32] {
31+
[0u8; 32]
32+
}
33+
34+
fn aggregator_image_id(&self) -> [u8; 32] {
35+
[0u8; 32]
36+
}
37+
2538
type Transaction = L1Transaction;
2639
type TransactionArtifact = ();
2740
type BatchArtifact = ();
41+
type AggregatorArtifact = ();
2842
type BatchMetadata = ChainBlockMetadata;
2943
type Error = ();
3044
}

node/vm/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,23 @@ impl<S: Store> Processor<S> for VM {
1818
todo!("transaction execution from SchedulerTransaction<L1Transaction>")
1919
}
2020

21+
// The node VM does not yet drive proving, so its receipt-cache image ids are unset.
22+
fn tx_image_id(&self) -> [u8; 32] {
23+
[0u8; 32]
24+
}
25+
26+
fn batch_image_id(&self) -> [u8; 32] {
27+
[0u8; 32]
28+
}
29+
30+
fn aggregator_image_id(&self) -> [u8; 32] {
31+
[0u8; 32]
32+
}
33+
2134
type Transaction = L1Transaction;
2235
type TransactionArtifact = TransactionEffects;
2336
type BatchArtifact = ();
37+
type AggregatorArtifact = ();
2438
type BatchMetadata = ChainBlockMetadata;
2539
type Error = VmError;
2640
}

scheduling/scheduler/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@ vprogs-l1-types.workspace = true
2222
vprogs-scheduling-execution-workers.workspace = true
2323
vprogs-state-batch-metadata.workspace = true
2424
vprogs-state-metadata.workspace = true
25+
vprogs-state-proof-receipt.workspace = true
2526
vprogs-state-ptr-latest.workspace = true
2627
vprogs-state-ptr-rollback.workspace = true
2728
vprogs-state-version.workspace = true
2829
vprogs-storage-manager.workspace = true
2930
vprogs-storage-types.workspace = true
31+
zerocopy.workspace = true
3032

3133
[dev-dependencies]
34+
kaspa-hashes.workspace = true
3235
tempfile.workspace = true
3336
vprogs-core-test-utils.workspace = true
3437
vprogs-core-types.workspace = true

scheduling/scheduler/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ pub(crate) use scheduled_transaction::ScheduledTransactionRef;
3434
pub use scheduler::Scheduler;
3535
pub use state::SchedulerState;
3636
pub use state_diff::{StateDiff, StateDiffRef};
37-
pub use storage_cmd::{Read, Write};
37+
pub use storage_cmd::{Read, ReadReceipt, ReceiptRead, ReceiptValue, StoreReceipt, Write};
3838
pub use transaction_context::TransactionContext;

scheduling/scheduler/src/processor.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
12
use vprogs_core_types::BatchMetadata;
23
use vprogs_storage_types::Store;
34

@@ -24,13 +25,29 @@ pub trait Processor<S: Store>: Clone + Sized + Send + Sync + 'static {
2425
// Default implementation does nothing (override if needed).
2526
}
2627

28+
/// Program identifier keying a per-transaction receipt in the proof-receipt store.
29+
fn tx_image_id(&self) -> [u8; 32];
30+
31+
/// Program identifier keying a per-batch receipt in the proof-receipt store.
32+
fn batch_image_id(&self) -> [u8; 32];
33+
34+
/// Program identifier keying an aggregated-bundle (settlement) receipt in the proof-receipt
35+
/// store.
36+
fn aggregator_image_id(&self) -> [u8; 32];
37+
2738
/// The transaction payload type (e.g. kaspa `L1Transaction`, `usize` in tests).
2839
/// The scheduler wraps it in `SchedulerTransaction<Self::Transaction>`.
2940
type Transaction: Send + Sync + 'static;
3041
/// Artifact produced by a processed transaction ([`ScheduledTransaction::publish_artifact`]).
31-
type TransactionArtifact: Send + Sync + 'static;
32-
/// Artifact produced by a processed batch ([`ScheduledBatch::publish_artifact`]).
33-
type BatchArtifact: Send + Sync + 'static;
42+
/// The `Borsh` bounds let the scheduler cache it in (and restore it from) the proof-receipt
43+
/// store; `Clone` lets a served lookup hand the cached value back out of its shared slot.
44+
type TransactionArtifact: Clone + BorshSerialize + BorshDeserialize + Send + Sync + 'static;
45+
/// Artifact produced by a processed batch ([`ScheduledBatch::publish_artifact`]). Bounded for
46+
/// the same proof-receipt caching as [`TransactionArtifact`](Self::TransactionArtifact).
47+
type BatchArtifact: Clone + BorshSerialize + BorshDeserialize + Send + Sync + 'static;
48+
/// Receipt produced by aggregating a bundle of per-batch receipts into one settlement receipt.
49+
/// Bounded for the same proof-receipt caching as the other artifacts.
50+
type AggregatorArtifact: Clone + BorshSerialize + BorshDeserialize + Send + Sync + 'static;
3451
/// Opaque metadata attached to each batch for persistence.
3552
type BatchMetadata: BatchMetadata;
3653
/// Error type returned when transaction processing fails.

0 commit comments

Comments
 (0)