Skip to content

Commit 9a6d805

Browse files
feat(drive-abci): add shielded pool drive-abci integration (medusa part 3) (#3220)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c0d23c2 commit 9a6d805

138 files changed

Lines changed: 11647 additions & 323 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Cargo.lock‎

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

‎packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_transition/v0/state_transition_validation.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ impl StateTransitionStructureValidation for ShieldTransitionV0 {
7777
);
7878
}
7979

80+
// amount must fit in i64 (Orchard protocol uses i64 internally for value_balance)
81+
if self.amount > i64::MAX as u64 {
82+
return SimpleConsensusValidationResult::new_with_error(
83+
BasicError::ShieldedInvalidValueBalanceError(
84+
ShieldedInvalidValueBalanceError::new(
85+
"shield amount exceeds maximum allowed value".to_string(),
86+
),
87+
)
88+
.into(),
89+
);
90+
}
91+
8092
// Proof must not be empty
8193
let result = validate_proof_not_empty(&self.proof);
8294
if !result.is_valid() {

‎packages/rs-dpp/src/state_transition/state_transitions/shielded/shielded_transfer_transition/v0/state_transition_validation.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ impl StateTransitionStructureValidation for ShieldedTransferTransitionV0 {
2424
return result;
2525
}
2626

27+
// value_balance must be positive (it IS the fee for shielded transfers)
28+
if self.value_balance == 0 {
29+
return SimpleConsensusValidationResult::new_with_error(
30+
BasicError::ShieldedInvalidValueBalanceError(
31+
ShieldedInvalidValueBalanceError::new(
32+
"shielded transfer value_balance must be greater than zero".to_string(),
33+
),
34+
)
35+
.into(),
36+
);
37+
}
38+
2739
// value_balance must fit in i64 (required for Orchard protocol)
2840
if self.value_balance > i64::MAX as u64 {
2941
return SimpleConsensusValidationResult::new_with_error(

‎packages/rs-drive-abci/Cargo.toml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] }
8282
async-trait = "0.1.77"
8383
console-subscriber = { version = "0.4", optional = true }
8484
bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true }
85+
grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dd99ed1db0350e5f39127573808dd172c6bc2346" }
86+
nonempty = "0.11"
8587

8688
[dev-dependencies]
8789
platform-version = { path = "../rs-platform-version", features = [
@@ -101,6 +103,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [
101103
drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] }
102104
drive-proof-verifier = { path = "../rs-drive-proof-verifier" }
103105
strategy-tests = { path = "../strategy-tests" }
106+
grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dd99ed1db0350e5f39127573808dd172c6bc2346", features = ["client"] }
104107
assert_matches = "1.5.0"
105108
drive-abci = { path = ".", features = ["testing-config", "mocks"] }
106109
bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" }
@@ -120,6 +123,8 @@ testing-config = []
120123
grovedbg = ["drive/grovedbg"]
121124
# `abci-server replay` command
122125
replay = ["dep:time", "tenderdash-abci/serde"]
126+
# Long-running shielded strategy tests (ZK proof generation)
127+
__shielded_strategy_tests = []
123128
[[bin]]
124129
name = "drive-abci"
125130
path = "src/main.rs"
@@ -128,4 +133,5 @@ path = "src/main.rs"
128133
unexpected_cfgs = { level = "warn", check-cfg = [
129134
'cfg(tokio_unstable)',
130135
'cfg(create_sdk_test_data)',
136+
'cfg(feature, values("__shielded_strategy_tests"))',
131137
] }

‎packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,21 @@ where
344344
platform_version,
345345
)?;
346346

347+
// Clean up expired compacted nullifier entries
348+
self.cleanup_recent_block_storage_nullifiers(&block_info, transaction, platform_version)?;
349+
350+
// Record shielded pool anchor if the commitment tree changed this block.
351+
// This stores block_height → anchor_bytes so shielded transactions can
352+
// reference a recent anchor for spend authorization.
353+
self.record_shielded_pool_anchor_if_changed(
354+
block_proposal.height,
355+
transaction,
356+
platform_version,
357+
)?;
358+
359+
// Prune anchors older than the configured retention depth
360+
self.prune_shielded_pool_anchors(block_proposal.height, transaction, platform_version)?;
361+
347362
// Pool withdrawals into transactions queue
348363

349364
// Takes queued withdrawals, creates untiled withdrawal transaction payload, saves them to queue
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod add_process_epoch_change_operations;
22
pub mod process_block_fees_and_validate_sum_trees;
3+
mod prune_shielded_pool_anchors;
4+
mod record_shielded_pool_anchor;
35

46
#[cfg(test)]
57
mod tests;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
mod v0;
2+
3+
use crate::error::execution::ExecutionError;
4+
use crate::error::Error;
5+
use crate::platform_types::platform::Platform;
6+
use crate::rpc::core::CoreRPCLike;
7+
use dpp::version::PlatformVersion;
8+
use drive::grovedb::Transaction;
9+
10+
impl<C> Platform<C>
11+
where
12+
C: CoreRPCLike,
13+
{
14+
/// Prunes shielded pool anchors older than the configured retention depth.
15+
pub(in crate::execution) fn prune_shielded_pool_anchors(
16+
&self,
17+
block_height: u64,
18+
transaction: &Transaction,
19+
platform_version: &PlatformVersion,
20+
) -> Result<(), Error> {
21+
match platform_version
22+
.drive_abci
23+
.methods
24+
.block_end
25+
.prune_shielded_pool_anchors
26+
{
27+
None => Ok(()),
28+
Some(0) => {
29+
self.prune_shielded_pool_anchors_v0(block_height, transaction, platform_version)
30+
}
31+
Some(version) => Err(Error::Execution(ExecutionError::UnknownVersionMismatch {
32+
method: "prune_shielded_pool_anchors".to_string(),
33+
known_versions: vec![0],
34+
received: version,
35+
})),
36+
}
37+
}
38+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::error::Error;
2+
use crate::platform_types::platform::Platform;
3+
use crate::rpc::core::CoreRPCLike;
4+
use dpp::version::PlatformVersion;
5+
use drive::grovedb::Transaction;
6+
7+
impl<C> Platform<C>
8+
where
9+
C: CoreRPCLike,
10+
{
11+
/// Prunes anchors older than `shielded_anchor_retention_blocks` from the current height.
12+
///
13+
/// Checks interval and retention depth conditions, then delegates to
14+
/// `Drive::prune_shielded_pool_anchors` for the actual GroveDB operations.
15+
pub(super) fn prune_shielded_pool_anchors_v0(
16+
&self,
17+
block_height: u64,
18+
transaction: &Transaction,
19+
platform_version: &PlatformVersion,
20+
) -> Result<(), Error> {
21+
let event_constants = &platform_version
22+
.drive_abci
23+
.validation_and_processing
24+
.event_constants;
25+
let retention_blocks = event_constants.shielded_anchor_retention_blocks;
26+
let pruning_interval = event_constants.shielded_anchor_pruning_interval;
27+
28+
// Only prune every N blocks to avoid unnecessary work
29+
if !block_height.is_multiple_of(pruning_interval) {
30+
return Ok(());
31+
}
32+
33+
// Nothing to prune if we haven't reached the retention depth yet
34+
if block_height <= retention_blocks {
35+
return Ok(());
36+
}
37+
38+
let cutoff_height = block_height - retention_blocks;
39+
40+
self.drive
41+
.prune_shielded_pool_anchors(cutoff_height, transaction, platform_version)
42+
.map_err(Error::Drive)
43+
}
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
mod v0;
2+
3+
use crate::error::execution::ExecutionError;
4+
use crate::error::Error;
5+
use crate::platform_types::platform::Platform;
6+
use crate::rpc::core::CoreRPCLike;
7+
use dpp::version::PlatformVersion;
8+
use drive::grovedb::Transaction;
9+
10+
impl<C> Platform<C>
11+
where
12+
C: CoreRPCLike,
13+
{
14+
/// Records the current shielded pool anchor if the commitment tree changed this block.
15+
pub(in crate::execution) fn record_shielded_pool_anchor_if_changed(
16+
&self,
17+
block_height: u64,
18+
transaction: &Transaction,
19+
platform_version: &PlatformVersion,
20+
) -> Result<(), Error> {
21+
match platform_version
22+
.drive_abci
23+
.methods
24+
.block_end
25+
.record_shielded_pool_anchor
26+
{
27+
None => Ok(()),
28+
Some(0) => self.record_shielded_pool_anchor_if_changed_v0(
29+
block_height,
30+
transaction,
31+
platform_version,
32+
),
33+
Some(version) => Err(Error::Execution(ExecutionError::UnknownVersionMismatch {
34+
method: "record_shielded_pool_anchor_if_changed".to_string(),
35+
known_versions: vec![0],
36+
received: version,
37+
})),
38+
}
39+
}
40+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use crate::error::Error;
2+
use crate::platform_types::platform::Platform;
3+
use crate::rpc::core::CoreRPCLike;
4+
use dpp::version::PlatformVersion;
5+
use drive::grovedb::Transaction;
6+
7+
impl<C> Platform<C>
8+
where
9+
C: CoreRPCLike,
10+
{
11+
/// Records the current shielded pool anchor if the commitment tree changed this block.
12+
///
13+
/// Delegates to `Drive::record_shielded_pool_anchor_if_changed` which handles
14+
/// all GroveDB operations: reading the current and most recent anchors, and
15+
/// conditionally writing to the anchors tree, anchors-by-height tree, and
16+
/// most recent anchor item.
17+
pub(super) fn record_shielded_pool_anchor_if_changed_v0(
18+
&self,
19+
block_height: u64,
20+
transaction: &Transaction,
21+
platform_version: &PlatformVersion,
22+
) -> Result<(), Error> {
23+
self.drive
24+
.record_shielded_pool_anchor_if_changed(block_height, transaction, platform_version)
25+
.map_err(Error::Drive)
26+
}
27+
}

0 commit comments

Comments
 (0)