Skip to content

Commit fd7eebc

Browse files
feat(drive): add shielded pool storage, actions, and verification (Medusa part 2) (#3198)
1 parent 06cef2c commit fd7eebc

143 files changed

Lines changed: 6567 additions & 367 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: 361 additions & 164 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/rs-dpp/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] }
7171
json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true }
7272
once_cell = "1.19.0"
7373
tracing = { version = "0.1.41" }
74-
grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "7ecb8465fad750c7cddd5332adb6f97fcceb498b", optional = true }
74+
grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dd99ed1db0350e5f39127573808dd172c6bc2346", optional = true }
7575

7676
[dev-dependencies]
7777
tokio = { version = "1.40", features = ["full"] }

‎packages/rs-dpp/src/address_funds/platform_address.rs‎

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,10 @@ impl PlatformAddress {
307307
pubkey_hash,
308308
)
309309
.map_err(|e| {
310-
ProtocolError::Generic(format!("P2PKH signature verification failed: {}", e))
310+
ProtocolError::AddressWitnessError(format!(
311+
"P2PKH signature verification failed: {}",
312+
e
313+
))
311314
})?;
312315

313316
Ok(AddressWitnessVerificationOperations::for_p2pkh(
@@ -325,7 +328,7 @@ impl PlatformAddress {
325328
let script = ScriptBuf::from_bytes(redeem_script.to_vec());
326329
let computed_hash = script.script_hash();
327330
if computed_hash.as_byte_array() != script_hash {
328-
return Err(ProtocolError::Generic(format!(
331+
return Err(ProtocolError::AddressWitnessError(format!(
329332
"Script hash {} does not match address hash {}",
330333
hex::encode(computed_hash.as_byte_array()),
331334
hex::encode(script_hash)
@@ -343,7 +346,7 @@ impl PlatformAddress {
343346
.collect();
344347

345348
if valid_signatures.len() < threshold {
346-
return Err(ProtocolError::Generic(format!(
349+
return Err(ProtocolError::AddressWitnessError(format!(
347350
"Not enough signatures: got {}, need {}",
348351
valid_signatures.len(),
349352
threshold
@@ -368,11 +371,14 @@ impl PlatformAddress {
368371
valid_signatures[sig_idx].as_slice(),
369372
)
370373
.map_err(|e| {
371-
ProtocolError::Generic(format!("Invalid signature format: {}", e))
374+
ProtocolError::AddressWitnessError(format!(
375+
"Invalid signature format: {}",
376+
e
377+
))
372378
})?;
373379

374380
let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
375-
ProtocolError::Generic(format!("Invalid public key: {}", e))
381+
ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
376382
})?;
377383

378384
if secp
@@ -391,20 +397,22 @@ impl PlatformAddress {
391397
signable_bytes.len(),
392398
))
393399
} else {
394-
Err(ProtocolError::Generic(format!(
400+
Err(ProtocolError::AddressWitnessError(format!(
395401
"Not enough valid signatures: verified {}, need {}",
396402
matched, threshold
397403
)))
398404
}
399405
}
400406
(PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
401-
Err(ProtocolError::Generic(
407+
Err(ProtocolError::AddressWitnessError(
402408
"P2PKH address requires P2pkh witness, got P2sh".to_string(),
403409
))
404410
}
405-
(PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => Err(
406-
ProtocolError::Generic("P2SH address requires P2sh witness, got P2pkh".to_string()),
407-
),
411+
(PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
412+
Err(ProtocolError::AddressWitnessError(
413+
"P2SH address requires P2sh witness, got P2pkh".to_string(),
414+
))
415+
}
408416
}
409417
}
410418

@@ -435,28 +443,28 @@ impl PlatformAddress {
435443
if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
436444
(byte - OP_PUSHNUM_1.to_u8() + 1) as usize
437445
} else {
438-
return Err(ProtocolError::Generic(format!(
446+
return Err(ProtocolError::AddressWitnessError(format!(
439447
"Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
440448
First opcode was 0x{:02x}, expected OP_1 through OP_16",
441449
byte
442450
)));
443451
}
444452
}
445453
Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
446-
return Err(ProtocolError::Generic(
454+
return Err(ProtocolError::AddressWitnessError(
447455
"Unsupported P2SH script type: only standard multisig is supported. \
448456
Script starts with a data push instead of OP_M threshold."
449457
.to_string(),
450458
))
451459
}
452460
Some(Err(e)) => {
453-
return Err(ProtocolError::Generic(format!(
461+
return Err(ProtocolError::AddressWitnessError(format!(
454462
"Error parsing P2SH script: {:?}",
455463
e
456464
)))
457465
}
458466
None => {
459-
return Err(ProtocolError::Generic(
467+
return Err(ProtocolError::AddressWitnessError(
460468
"Empty P2SH redeem script".to_string(),
461469
))
462470
}
@@ -483,7 +491,7 @@ impl PlatformAddress {
483491
// This is OP_N, the total number of keys
484492
let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
485493
if pubkeys.len() != n {
486-
return Err(ProtocolError::Generic(format!(
494+
return Err(ProtocolError::AddressWitnessError(format!(
487495
"Multisig script declares {} keys but contains {}",
488496
n,
489497
pubkeys.len()
@@ -492,24 +500,24 @@ impl PlatformAddress {
492500
break;
493501
} else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
494502
// Hit CHECKMULTISIG without seeing OP_N - malformed
495-
return Err(ProtocolError::Generic(
503+
return Err(ProtocolError::AddressWitnessError(
496504
"Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
497505
));
498506
} else {
499-
return Err(ProtocolError::Generic(format!(
507+
return Err(ProtocolError::AddressWitnessError(format!(
500508
"Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
501509
byte
502510
)));
503511
}
504512
}
505513
Some(Err(e)) => {
506-
return Err(ProtocolError::Generic(format!(
514+
return Err(ProtocolError::AddressWitnessError(format!(
507515
"Error parsing multisig script: {:?}",
508516
e
509517
)))
510518
}
511519
None => {
512-
return Err(ProtocolError::Generic(
520+
return Err(ProtocolError::AddressWitnessError(
513521
"Incomplete multisig script: unexpected end before OP_N".to_string(),
514522
))
515523
}
@@ -518,7 +526,7 @@ impl PlatformAddress {
518526

519527
// Validate threshold
520528
if threshold > pubkeys.len() {
521-
return Err(ProtocolError::Generic(format!(
529+
return Err(ProtocolError::AddressWitnessError(format!(
522530
"Invalid multisig: threshold {} exceeds number of keys {}",
523531
threshold,
524532
pubkeys.len()
@@ -531,24 +539,24 @@ impl PlatformAddress {
531539
if op == OP_CHECKMULTISIG {
532540
// Standard multisig - verify script is complete
533541
if instructions.next().is_some() {
534-
return Err(ProtocolError::Generic(
542+
return Err(ProtocolError::AddressWitnessError(
535543
"Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
536544
));
537545
}
538546
Ok((threshold, pubkeys))
539547
} else if op == OP_CHECKMULTISIGVERIFY {
540-
Err(ProtocolError::Generic(
548+
Err(ProtocolError::AddressWitnessError(
541549
"OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
542550
.to_string(),
543551
))
544552
} else {
545-
Err(ProtocolError::Generic(format!(
553+
Err(ProtocolError::AddressWitnessError(format!(
546554
"Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
547555
op.to_u8()
548556
)))
549557
}
550558
}
551-
_ => Err(ProtocolError::Generic(
559+
_ => Err(ProtocolError::AddressWitnessError(
552560
"Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
553561
)),
554562
}

‎packages/rs-dpp/src/errors/protocol_error.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ pub enum ProtocolError {
140140
#[error("Generic Error: {0}")]
141141
Generic(String),
142142

143+
#[error("Address witness verification error: {0}")]
144+
AddressWitnessError(String),
145+
146+
#[error("Shielded transaction build error: {0}")]
147+
ShieldedBuildError(String),
148+
143149
#[error("Not supported Error: {0}")]
144150
NotSupported(String),
145151

‎packages/rs-dpp/src/shielded/builder/mod.rs‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ pub(crate) fn build_output_only_bundle<P: OrchardProver>(
157157

158158
builder
159159
.add_output(None, payment_address, NoteValue::from_raw(amount), memo)
160-
.map_err(|e| ProtocolError::Generic(format!("failed to add output: {:?}", e)))?;
160+
.map_err(|e| ProtocolError::ShieldedBuildError(format!("failed to add output: {:?}", e)))?;
161161

162162
prove_and_sign_bundle(builder, prover, &[], &[])
163163
}
@@ -185,7 +185,9 @@ pub(crate) fn build_spend_bundle<P: OrchardProver>(
185185
for spend in spends {
186186
builder
187187
.add_spend(fvk.clone(), spend.note, spend.merkle_path)
188-
.map_err(|e| ProtocolError::Generic(format!("failed to add spend: {:?}", e)))?;
188+
.map_err(|e| {
189+
ProtocolError::ShieldedBuildError(format!("failed to add spend: {:?}", e))
190+
})?;
189191
}
190192

191193
builder
@@ -195,7 +197,7 @@ pub(crate) fn build_spend_bundle<P: OrchardProver>(
195197
NoteValue::from_raw(output_amount),
196198
memo,
197199
)
198-
.map_err(|e| ProtocolError::Generic(format!("failed to add output: {:?}", e)))?;
200+
.map_err(|e| ProtocolError::ShieldedBuildError(format!("failed to add output: {:?}", e)))?;
199201

200202
prove_and_sign_bundle(
201203
builder,
@@ -217,19 +219,25 @@ pub(crate) fn prove_and_sign_bundle<P: OrchardProver>(
217219

218220
let (unauthorized, _) = builder
219221
.build::<i64>(&mut rng)
220-
.map_err(|e| ProtocolError::Generic(format!("failed to build bundle: {:?}", e)))?
221-
.ok_or_else(|| ProtocolError::Generic("bundle was empty after build".to_string()))?;
222+
.map_err(|e| ProtocolError::ShieldedBuildError(format!("failed to build bundle: {:?}", e)))?
223+
.ok_or_else(|| {
224+
ProtocolError::ShieldedBuildError("bundle was empty after build".to_string())
225+
})?;
222226

223227
let bundle_commitment: [u8; 32] = unauthorized.commitment().into();
224228
let sighash = compute_platform_sighash(&bundle_commitment, extra_sighash_data);
225229

226230
let proven = unauthorized
227231
.create_proof(prover.proving_key(), &mut rng)
228-
.map_err(|e| ProtocolError::Generic(format!("failed to create proof: {:?}", e)))?;
232+
.map_err(|e| {
233+
ProtocolError::ShieldedBuildError(format!("failed to create proof: {:?}", e))
234+
})?;
229235

230236
proven
231237
.apply_signatures(rng, sighash, signing_keys)
232-
.map_err(|e| ProtocolError::Generic(format!("failed to apply signatures: {:?}", e)))
238+
.map_err(|e| {
239+
ProtocolError::ShieldedBuildError(format!("failed to apply signatures: {:?}", e))
240+
})
233241
}
234242

235243
/// Shared test utilities for builder tests.

‎packages/rs-dpp/src/shielded/builder/shield.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn build_shield_transition<S: Signer<PlatformAddress>, P: OrchardProver>(
4141
platform_version: &PlatformVersion,
4242
) -> Result<StateTransition, ProtocolError> {
4343
if fee_strategy.is_empty() {
44-
return Err(ProtocolError::Generic(
44+
return Err(ProtocolError::ShieldedBuildError(
4545
"fee_strategy must have at least one step".to_string(),
4646
));
4747
}

‎packages/rs-dpp/src/shielded/builder/shield_from_asset_lock.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn build_shield_from_asset_lock_transition<P: OrchardProver>(
4141
.checked_neg()
4242
.and_then(|v| u64::try_from(v).ok())
4343
.ok_or_else(|| {
44-
ProtocolError::Generic(
44+
ProtocolError::ShieldedBuildError(
4545
"shield_from_asset_lock: bundle value_balance is not negative".to_string(),
4646
)
4747
})?;

‎packages/rs-dpp/src/shielded/builder/shielded_transfer.rs‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ pub fn build_shielded_transfer_transition<P: OrchardProver>(
5555
let min_fee = compute_minimum_shielded_fee(num_actions, platform_version);
5656
let effective_fee = match fee {
5757
Some(f) if f < min_fee => {
58-
return Err(ProtocolError::Generic(format!(
58+
return Err(ProtocolError::ShieldedBuildError(format!(
5959
"fee {} is below minimum required fee {}",
6060
f, min_fee
6161
)));
6262
}
6363
Some(f) if f > min_fee.saturating_mul(1000) => {
64-
return Err(ProtocolError::Generic(format!(
64+
return Err(ProtocolError::ShieldedBuildError(format!(
6565
"fee {} exceeds 1000x the minimum fee {}",
6666
f, min_fee
6767
)));
@@ -70,11 +70,11 @@ pub fn build_shielded_transfer_transition<P: OrchardProver>(
7070
None => min_fee,
7171
};
7272

73-
let required = transfer_amount
74-
.checked_add(effective_fee)
75-
.ok_or_else(|| ProtocolError::Generic("fee + transfer_amount overflows u64".to_string()))?;
73+
let required = transfer_amount.checked_add(effective_fee).ok_or_else(|| {
74+
ProtocolError::ShieldedBuildError("fee + transfer_amount overflows u64".to_string())
75+
})?;
7676
if required > total_spent {
77-
return Err(ProtocolError::Generic(format!(
77+
return Err(ProtocolError::ShieldedBuildError(format!(
7878
"transfer amount {} + fee {} = {} exceeds total spendable value {}",
7979
transfer_amount, effective_fee, required, total_spent
8080
)));
@@ -89,7 +89,9 @@ pub fn build_shielded_transfer_transition<P: OrchardProver>(
8989
for spend in spends {
9090
builder
9191
.add_spend(fvk.clone(), spend.note, spend.merkle_path)
92-
.map_err(|e| ProtocolError::Generic(format!("failed to add spend: {:?}", e)))?;
92+
.map_err(|e| {
93+
ProtocolError::ShieldedBuildError(format!("failed to add spend: {:?}", e))
94+
})?;
9395
}
9496

9597
// Primary output to recipient
@@ -100,7 +102,7 @@ pub fn build_shielded_transfer_transition<P: OrchardProver>(
100102
NoteValue::from_raw(transfer_amount),
101103
memo,
102104
)
103-
.map_err(|e| ProtocolError::Generic(format!("failed to add output: {:?}", e)))?;
105+
.map_err(|e| ProtocolError::ShieldedBuildError(format!("failed to add output: {:?}", e)))?;
104106

105107
// Change output (if any)
106108
if change_amount > 0 {
@@ -112,7 +114,9 @@ pub fn build_shielded_transfer_transition<P: OrchardProver>(
112114
NoteValue::from_raw(change_amount),
113115
[0u8; 36],
114116
)
115-
.map_err(|e| ProtocolError::Generic(format!("failed to add change output: {:?}", e)))?;
117+
.map_err(|e| {
118+
ProtocolError::ShieldedBuildError(format!("failed to add change output: {:?}", e))
119+
})?;
116120
}
117121

118122
// ShieldedTransfer has no extra_data in sighash

‎packages/rs-dpp/src/shielded/builder/shielded_withdrawal.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub fn build_shielded_withdrawal_transition<P: OrchardProver>(
5151
platform_version: &PlatformVersion,
5252
) -> Result<StateTransition, ProtocolError> {
5353
if withdrawal_amount > i64::MAX as u64 {
54-
return Err(ProtocolError::Generic(format!(
54+
return Err(ProtocolError::ShieldedBuildError(format!(
5555
"withdrawal amount {} exceeds maximum allowed value {}",
5656
withdrawal_amount,
5757
i64::MAX as u64
@@ -65,7 +65,7 @@ pub fn build_shielded_withdrawal_transition<P: OrchardProver>(
6565
let min_fee = compute_minimum_shielded_fee(num_actions, platform_version);
6666
let effective_fee = match fee {
6767
Some(f) if f < min_fee => {
68-
return Err(ProtocolError::Generic(format!(
68+
return Err(ProtocolError::ShieldedBuildError(format!(
6969
"fee {} is below minimum required fee {}",
7070
f, min_fee
7171
)));
@@ -77,10 +77,10 @@ pub fn build_shielded_withdrawal_transition<P: OrchardProver>(
7777
let required = withdrawal_amount
7878
.checked_add(effective_fee)
7979
.ok_or_else(|| {
80-
ProtocolError::Generic("fee + withdrawal_amount overflows u64".to_string())
80+
ProtocolError::ShieldedBuildError("fee + withdrawal_amount overflows u64".to_string())
8181
})?;
8282
if required > total_spent {
83-
return Err(ProtocolError::Generic(format!(
83+
return Err(ProtocolError::ShieldedBuildError(format!(
8484
"withdrawal amount {} + fee {} = {} exceeds total spendable value {}",
8585
withdrawal_amount, effective_fee, required, total_spent
8686
)));

0 commit comments

Comments
 (0)