diff --git a/src/builder.rs b/src/builder.rs index acb0665bf..9b51655d1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -969,7 +969,28 @@ fn build_bundle( // as we can estimate the vector size beforehand. let mut indexed_spends_outputs = Vec::with_capacity(num_actions); - let spends_outputs_by_asset = partition_by_asset(&spends, &outputs); + let mut spends_outputs_by_asset = partition_by_asset(&spends, &outputs); + + // For zatoshi-only bundles, pad spends and outputs to num_actions + // before per-asset processing, so that dummies are created before the shuffle — + // matching vanilla Orchard RNG consumption order. + if spends_outputs_by_asset + .keys() + .all(|asset| asset == &AssetBase::zatoshi()) + { + let (asset_spends, asset_outputs) = spends_outputs_by_asset + .entry(AssetBase::zatoshi()) + .or_insert_with(|| (vec![], vec![])); + asset_spends.extend( + iter::repeat_with(|| (SpendInfo::dummy(AssetBase::zatoshi(), &mut rng), None)) + .take(num_actions.saturating_sub(asset_spends.len())), + ); + asset_outputs.extend( + iter::repeat_with(|| (OutputInfo::dummy(&mut rng, AssetBase::zatoshi()), None)) + .take(num_actions.saturating_sub(asset_outputs.len())), + ); + } + let asset_count = spends_outputs_by_asset.len(); indexed_spends_outputs.extend(spends_outputs_by_asset.into_iter().flat_map( |(asset, (spends, outputs))| { @@ -1009,6 +1030,9 @@ fn build_bundle( }, )); + // Pad total actions to num_actions. + // This covers the edge case of a single non-zatoshi asset with fewer than + // MIN_ACTIONS spends/outputs (e.g. a bundle that only burns a custom asset). indexed_spends_outputs.extend( iter::repeat_with(|| { ( @@ -1021,7 +1045,9 @@ fn build_bundle( // We shuffled the spends and outputs within each `AssetBase` above; now we // shuffle the actions to achieve a similar property across `AssetBase`s. - indexed_spends_outputs.shuffle(&mut rng); + if asset_count > 1 { + indexed_spends_outputs.shuffle(&mut rng); + } let mut bundle_meta = BundleMetadata::new(num_requested_spends, num_requested_outputs); let pre_actions = indexed_spends_outputs diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index aca7d2198..549b1402a 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -168,7 +168,9 @@ mod tests { let sighash = hash_bundle_txid_data(&bundle); assert_eq!( sighash.to_hex().as_str(), - "f3ea89ea2b1e17b3313a6f2f9e4e47c21eec1574902f5ea6961227e1eaed2327" + // Bundle hash for Orchard (vanilla) generated using + // Zcash/Orchard commit: 9d89b504 + "0ac1e319f6761a8561b7bd3fc0907a5c73ed5590a6c210c4d39ffae1d5741875" ); } @@ -184,7 +186,7 @@ mod tests { let sighash = hash_bundle_txid_data(&bundle); assert_eq!( sighash.to_hex().as_str(), - "a0d843b7278788e3b47dc9fe1e1da227a94898b7111d76514a87df486d32773c" + "f84871d872081fa7744cbaf575e342cf81951a9b17818264170243d1551a99ea" ); } @@ -213,7 +215,9 @@ mod tests { let orchard_auth_digest = hash_bundle_auth_data(&bundle, test_sighash_info_for_kind); assert_eq!( orchard_auth_digest.to_hex().as_str(), - "c99aa5a33fd4e7b78de0ee846397e2eb0da3a5d176e6df57d0401c49f51d7295" + // Bundle hash for Orchard (vanilla) generated using + // Zcash/Orchard commit: 9d89b504 + "5f3bcf759cddf19170ec47a882a470b5767d66c95fc72ffc360f31324474a06b" ); } @@ -230,7 +234,7 @@ mod tests { let orchard_auth_digest = hash_bundle_auth_data(&bundle, test_sighash_info_for_kind); assert_eq!( orchard_auth_digest.to_hex().as_str(), - "9d47819082f2323b30ceabe0fea993b39541cc0e62a8be6e1bc2a19840b0d9ab" + "0c29408a07863016f5b4c5c0ccc5b944f24c686d06035945c5514f8b8c195a99" ); } diff --git a/tests/builder.rs b/tests/builder.rs index ba4c350aa..b563594de 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -213,16 +213,16 @@ fn bundle_chain_vanilla() { orchard_digest_1, // Locks the `orchard_digest` for OrchardVanilla [ - 25, 143, 25, 148, 146, 133, 196, 243, 163, 122, 136, 217, 179, 122, 70, 233, 4, 4, 26, - 170, 152, 243, 177, 199, 226, 241, 63, 143, 104, 77, 149, 254 + 165, 242, 106, 135, 168, 224, 110, 252, 175, 110, 63, 29, 78, 243, 33, 14, 152, 202, + 209, 47, 68, 32, 138, 96, 79, 213, 218, 93, 45, 87, 221, 174 ] ); assert_eq!( orchard_digest_2, // Locks the `orchard_digest` for OrchardVanilla [ - 164, 197, 26, 212, 108, 232, 219, 47, 64, 35, 3, 171, 77, 191, 253, 173, 173, 0, 148, - 119, 98, 210, 134, 196, 201, 205, 117, 10, 37, 72, 234, 3 + 74, 174, 42, 41, 68, 92, 171, 110, 10, 148, 217, 61, 68, 50, 49, 1, 1, 180, 221, 210, + 97, 237, 25, 198, 195, 77, 19, 160, 186, 172, 8, 26 ] ); } @@ -234,16 +234,16 @@ fn bundle_chain_zsa() { orchard_digest_1, // Locks the `orchard_digest` for OrchardZSA [ - 47, 247, 30, 9, 58, 47, 181, 208, 48, 162, 133, 51, 186, 54, 13, 82, 207, 227, 33, 48, - 223, 31, 90, 129, 96, 166, 247, 156, 122, 125, 100, 190 + 176, 24, 152, 89, 60, 222, 215, 240, 176, 197, 147, 81, 4, 84, 61, 189, 163, 117, 43, + 201, 63, 140, 116, 211, 133, 186, 54, 58, 171, 124, 192, 215 ] ); assert_eq!( orchard_digest_2, // Locks the `orchard_digest` for OrchardZSA [ - 40, 249, 161, 168, 11, 100, 205, 146, 11, 203, 210, 239, 51, 73, 208, 236, 47, 110, 49, - 18, 132, 199, 179, 63, 140, 28, 106, 34, 155, 93, 111, 254 + 161, 158, 107, 122, 89, 77, 236, 178, 130, 85, 148, 101, 237, 1, 67, 119, 76, 126, 233, + 123, 94, 240, 183, 227, 9, 245, 74, 51, 16, 12, 157, 60 ] ); }