Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fa07c7e
feat(dpp): IdentityCreateFromShieldedPool transition foundation (type…
QuantumExplorer Jun 8, 2026
17b47c4
feat(drive): IdentityCreateFromShieldedPool action, converter, prove/…
QuantumExplorer Jun 8, 2026
0bcb9de
feat(drive-abci): IdentityCreateFromShieldedPool validation + metered…
QuantumExplorer Jun 8, 2026
968e86c
feat(wasm): wire IdentityCreateFromShieldedPool into wasm-dpp/wasm-dp…
QuantumExplorer Jun 8, 2026
31980ef
feat(sdk): IdentityCreateFromShieldedPool broadcast helper (type 20)
QuantumExplorer Jun 8, 2026
aab3735
test(dpp)+docs: IdentityCreateFromShieldedPool sighash/fee tests + bo…
QuantumExplorer Jun 8, 2026
8d288ad
test(drive): IdentityCreateFromShieldedPool strict-verify empty-proof…
QuantumExplorer Jun 8, 2026
7e1734e
feat(swift-sdk): IdentityCreateFromShieldedPool wallet + FFI + Swift …
QuantumExplorer Jun 8, 2026
08679ff
fix(dpp,drive-abci,sdk): address review — id malleability, PoP-before…
QuantumExplorer Jun 8, 2026
febc057
docs(book): add PaidFromShieldedPoolToNewIdentity to the ExecutionEve…
QuantumExplorer Jun 8, 2026
52bb6d7
fix(drive-abci,wallet): restore per-key PoP fee accounting + checked …
QuantumExplorer Jun 8, 2026
4085683
fix(drive-abci): IdentityCreateFromShieldedPool — add identity-absenc…
QuantumExplorer Jun 8, 2026
a9c8e13
fix: address CodeRabbit review — verify binding, checked arithmetic, …
QuantumExplorer Jun 8, 2026
ed377d5
test(drive-abci): targeted transformer tests for IdentityCreateFromSh…
QuantumExplorer Jun 8, 2026
d879f4c
fix(drive)!: remove AddToSystemCredits over-mint in IdentityCreateFro…
QuantumExplorer Jun 8, 2026
e04dbaf
fix(wasm-dpp2): rebuild nullifiers Map on fromObject/fromJSON + cover…
QuantumExplorer Jun 8, 2026
4e13526
test(drive-abci): sum-tree credit-conservation regression for Identit…
QuantumExplorer Jun 8, 2026
56881e8
feat: fallback-on-failure for IdentityCreateFromShieldedPool (charge …
QuantumExplorer Jun 9, 2026
e636a06
fix(drive-abci): clippy — PlatformAddress is Copy, don't clone the fa…
QuantumExplorer Jun 9, 2026
86591a4
fix: address CodeRabbit on the fallback feature — builder fail-fast, …
QuantumExplorer Jun 9, 2026
e0804c4
docs(ffi): generalize parse_optional_platform_address doc (shared hel…
QuantumExplorer Jun 9, 2026
c85f0d8
fix(drive-abci)!: execute the IdentityCreateFromShieldedPool fallback…
QuantumExplorer Jun 9, 2026
37de254
fix: type-enforce the shielded-pool apply-despite-errors invariant + …
QuantumExplorer Jun 9, 2026
94f42ae
fix(drive): add chargeable_failure to the 5 Unshield converter test l…
QuantumExplorer Jun 9, 2026
9a855a1
refactor(dpp): reuse consensus identity-create cost constants in shie…
QuantumExplorer Jun 9, 2026
4964155
Merge branch 'v3.1-dev' into claude/jovial-khorana-5f2bba
QuantumExplorer Jun 9, 2026
04fa1b6
fix(dpp): version shielded sighash-data helpers; drop consensus debug…
QuantumExplorer Jun 9, 2026
73b58d7
refactor(dpp): move shielded sighash preimage builders into their own…
QuantumExplorer Jun 9, 2026
c419440
Update packages/rs-drive-abci/src/execution/platform_events/state_tra…
shumkov Jun 9, 2026
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
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion book/src/error-handling/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Error codes are organized into ranges that correspond to error categories and su
| 10600-10603 | State Transition | `InvalidStateTransitionTypeError` (10600), `StateTransitionMaxSizeExceededError` (10602) |
| 10700-10700 | General | `OverflowError` (10700) |
| 10800-10818 | Address | `TransitionOverMaxInputsError` (10800), `WithdrawalBelowMinAmountError` (10818) |
| 10819-10826 | Shielded | `ShieldedNoActionsError` (10819), `ShieldedTooManyActionsError` (10825), `ShieldedImplicitFeeCapExceededError` (10826) |
| 10819-10827 | Shielded | `ShieldedNoActionsError` (10819), `ShieldedTooManyActionsError` (10825), `ShieldedImplicitFeeCapExceededError` (10826), `ShieldedInvalidDenominationError` (10827 — `IdentityCreateFromShieldedPool` exit amount not a member of the versioned denomination set) |

### SignatureError codes (20000-20012)

Expand Down
3 changes: 2 additions & 1 deletion book/src/fees/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ fn apply_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
## ExecutionEvent Variants

The `ExecutionEvent` enum (in `rs-drive-abci`) determines how fees are collected
for each state transition. There are seven variants:
for each state transition. There are eight variants:

| Variant | Fee Source | Used By |
|---|---|---|
Expand All @@ -170,6 +170,7 @@ for each state transition. There are seven variants:
| `PaidFromAddressInputs` | Platform address balances | All address-based transitions; `Shield` (metered + a ZK compute fee via `additional_fixed_fee_cost`) |
| `PaidFixedCost` | Fixed fee to pool | MasternodeVote |
| `PaidFromShieldedPool` | Shielded pool value_balance | ShieldedTransfer, Unshield, ShieldedWithdrawal |
| `PaidFromShieldedPoolToNewIdentity` | Shielded pool (the fixed `denomination`); the metered write + ZK compute fee is moved from the new identity's balance into the fee pools | IdentityCreateFromShieldedPool |

Each variant carries the operations to execute and enough context for the fee
validation and execution pipeline to deduct the correct amount from the correct
Expand Down
1 change: 1 addition & 0 deletions book/src/fees/shielded-fees.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The fee is derived differently depending on the shielded transition type:
| **Unshield** | `fee = compute_minimum_shielded_fee(num_actions) + unshield_address_storage_fee` | `value_balance` (the transition's `unshielding_amount`) is the **gross** amount leaving the pool. The output address receives `unshielding_amount − fee`; validation requires `unshielding_amount ≥ fee`. Unshield also writes the net to the output platform address (`AddBalanceToAddress`), a real storage write priced on top of the base shielded minimum (`unshield_address_storage_fee = 222 × per_byte_rate`, ≈6.08M credits, flat regardless of action count — 222 bytes is the *storage* portion of the ≈6.24M metered address write) so the address write is covered and the proof fee isn't diverted to pay for it. See [Per-Action Storage Fee](#3-per-action-storage-fee). |
| **ShieldedWithdrawal** | `fee = compute_minimum_shielded_fee(num_actions) + withdrawal_document_storage_fee` | `value_balance` (`unshielding_amount`) is the **gross** amount leaving the pool. The Core withdrawal document receives `unshielding_amount − fee` (which must also clear `MIN_WITHDRAWAL_AMOUNT`). Unlike the other pool-paid transitions, ShieldedWithdrawal also **writes a Core withdrawal document** — a real document insert into the withdrawals contract plus its index entries (`AddWithdrawalDocument`), with a real metered cost of ≈110M credits that is **flat regardless of action count**. That cost is priced on top of the base shielded minimum as a flat ~4,100-byte storage component (`withdrawal_document_storage_fee = 4100 × per_byte_rate`), so the document write is covered and the proof-verification fee isn't diverted from the proposer to pay for it. See [Per-Action Storage Fee](#3-per-action-storage-fee). |
| **ShieldFromAssetLock** | `pool_fee = compute_minimum_shielded_fee(num_actions) + asset_lock_base_cost`, paid from the asset lock | The flat shielded minimum plus the asset-lock processing base cost is routed to the fee pools. Any remaining asset-lock value (the *surplus*) goes to an optional signed `surplus_output` platform address, or — if none is set — folds into the fee pools up to `shielded_implicit_fee_cap`. See [Entry-Transition Fees](#entry-transition-fees-shield-and-shieldfromassetlock). |
| **IdentityCreateFromShieldedPool** | `total_fee = metered(insert_nullifiers + AddNewIdentity(identity + N keys)) + shielded_verification_fee`, **moved from the new identity's balance** | `value_balance` is a **fixed `denomination`** (a member of the versioned set `{0.1, 0.3, 0.5, 1.0}` DASH) and must equal it EXACTLY. The new identity is created holding the full `denomination`, funded by decrementing the shielded pool by exactly that amount — a move *between* two balance trees (like `Unshield`'s pool→address), so the global system-credit supply is unchanged (**no** `AddToSystemCredits`); the fee is then **moved** from that balance into the fee pools, so the identity ends with `denomination − total_fee`. Unlike the flat pool-paid transitions, the `AddNewIdentity` write grows with the key count, so the cost is **metered** (not a flat carve) — only the ZK compute fee (`compute_shielded_verification_fee`) is added on top, exactly like the transparent `Shield`. The client predicts it offline with `compute_shielded_identity_create_fee(num_actions, num_keys)`; consensus rejects `denomination < total_fee` with `IdentityInsufficientBalanceError`. |

For `ShieldedTransfer`, the client constructs the bundle so that `total_spent −
total_output = desired_fee`. The Orchard circuit proves that value is conserved
Expand Down
12 changes: 8 additions & 4 deletions packages/rs-dpp/src/errors/consensus/basic/basic_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,11 @@ use crate::consensus::basic::state_transition::{
MissingStateTransitionTypeError, OutputAddressAlsoInputError, OutputBelowMinimumError,
OutputsNotGreaterThanInputsError, ShieldedEmptyProofError,
ShieldedEncryptedNoteSizeMismatchError, ShieldedImplicitFeeCapExceededError,
ShieldedInvalidValueBalanceError, ShieldedNoActionsError, ShieldedTooManyActionsError,
ShieldedZeroAnchorError, StateTransitionMaxSizeExceededError, StateTransitionNotActiveError,
TransitionNoInputsError, TransitionNoOutputsError, TransitionOverMaxInputsError,
TransitionOverMaxOutputsError, WithdrawalBalanceMismatchError, WithdrawalBelowMinAmountError,
ShieldedInvalidDenominationError, ShieldedInvalidValueBalanceError, ShieldedNoActionsError,
ShieldedTooManyActionsError, ShieldedZeroAnchorError, StateTransitionMaxSizeExceededError,
StateTransitionNotActiveError, TransitionNoInputsError, TransitionNoOutputsError,
TransitionOverMaxInputsError, TransitionOverMaxOutputsError, WithdrawalBalanceMismatchError,
WithdrawalBelowMinAmountError,
};
use crate::consensus::basic::{
IncompatibleProtocolVersionError, UnsupportedFeatureError, UnsupportedProtocolVersionError,
Expand Down Expand Up @@ -688,6 +689,9 @@ pub enum BasicError {
// (codes.rs) is independent of variant order.
#[error(transparent)]
ShieldedImplicitFeeCapExceededError(ShieldedImplicitFeeCapExceededError),

#[error(transparent)]
ShieldedInvalidDenominationError(ShieldedInvalidDenominationError),
}

impl From<BasicError> for ConsensusError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod outputs_not_greater_than_inputs_error;
mod shielded_empty_proof_error;
mod shielded_encrypted_note_size_mismatch_error;
mod shielded_implicit_fee_cap_exceeded_error;
mod shielded_invalid_denomination_error;
mod shielded_invalid_value_balance_error;
mod shielded_no_actions_error;
mod shielded_too_many_actions_error;
Expand Down Expand Up @@ -47,6 +48,7 @@ pub use outputs_not_greater_than_inputs_error::*;
pub use shielded_empty_proof_error::*;
pub use shielded_encrypted_note_size_mismatch_error::*;
pub use shielded_implicit_fee_cap_exceeded_error::*;
pub use shielded_invalid_denomination_error::*;
pub use shielded_invalid_value_balance_error::*;
pub use shielded_no_actions_error::*;
pub use shielded_too_many_actions_error::*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::consensus::basic::BasicError;
use crate::consensus::ConsensusError;
use crate::errors::ProtocolError;
use bincode::{Decode, Encode};
use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
use thiserror::Error;

#[derive(
Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize,
)]
#[error("Invalid shielded identity-create denomination {denomination}: must be one of the allowed exit denominations")]
#[platform_serialize(unversioned)]
pub struct ShieldedInvalidDenominationError {
/*

DO NOT CHANGE ORDER OF FIELDS WITHOUT INTRODUCING OF NEW VERSION

*/
/// The rejected denomination (in credits). `IdentityCreateFromShieldedPool` may only exit one of
/// a small versioned set of fixed denominations so every exit of a given size is indistinguishable
/// on-chain (maximizing the anonymity set). Any other value — including a non-member amount or a
/// `value_balance` that does not equal the declared denomination — is rejected with this error
/// (consensus error code 10827).
denomination: u64,
}

impl ShieldedInvalidDenominationError {
pub fn new(denomination: u64) -> Self {
Self { denomination }
}

pub fn denomination(&self) -> u64 {
self.denomination
}
}

impl From<ShieldedInvalidDenominationError> for ConsensusError {
fn from(err: ShieldedInvalidDenominationError) -> Self {
Self::BasicError(BasicError::ShieldedInvalidDenominationError(err))
}
}
3 changes: 2 additions & 1 deletion packages/rs-dpp/src/errors/consensus/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,15 @@ impl ErrorWithCode for BasicError {
Self::OutputAddressAlsoInputError(_) => 10816,
Self::InvalidRemainderOutputCountError(_) => 10817,
Self::WithdrawalBelowMinAmountError(_) => 10818,
// Shielded transition errors (10819-10826)
// Shielded transition errors (10819-10827)
Self::ShieldedNoActionsError(_) => 10819,
Self::ShieldedEmptyProofError(_) => 10820,
Self::ShieldedZeroAnchorError(_) => 10821,
Self::ShieldedInvalidValueBalanceError(_) => 10822,
Self::ShieldedEncryptedNoteSizeMismatchError(_) => 10823,
Self::ShieldedTooManyActionsError(_) => 10825,
Self::ShieldedImplicitFeeCapExceededError(_) => 10826,
Self::ShieldedInvalidDenominationError(_) => 10827,
}
}
}
Expand Down
Loading
Loading