Merge pull request #6048 from nuttycom/sapling-batch-validation

Use batch validation for Sapling proofs and signatures
This commit is contained in:
str4d 2022-07-05 20:47:17 +01:00 committed by GitHub
commit 9d93dff4c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 844 additions and 537 deletions

26
Cargo.lock generated
View File

@ -120,9 +120,9 @@ checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b"
[[package]]
name = "bellman"
version = "0.13.0"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d96d7f4f3dc9a699bdef1d19648f6f20ef966b51892d224582a4475be669cb5"
checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807"
dependencies = [
"bitvec",
"blake2s_simd",
@ -1641,6 +1641,22 @@ dependencies = [
"zeroize",
]
[[package]]
name = "redjubjub"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6039ff156887caf92df308cbaccdc058c9d3155a913da046add6e48c4cdbd91d"
dependencies = [
"blake2b_simd",
"byteorder",
"digest 0.9.0",
"jubjub",
"rand_core 0.6.3",
"serde",
"thiserror",
"zeroize",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
@ -2320,9 +2336,9 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c0928a33ab146a4e93e2a1141ce589de1bbe93c90219965dcccadc15f4e4e8"
checksum = "98bf5f6af051dd929263f279b21b9c04c1f30116c70f3c190de2566677f245ef"
dependencies = [
"bellman",
"blake2b_simd",
@ -2334,6 +2350,8 @@ dependencies = [
"jubjub",
"lazy_static",
"rand_core 0.6.3",
"redjubjub",
"tracing",
"zcash_primitives",
]

View File

@ -56,7 +56,7 @@ zcash_encoding = "0.1"
zcash_history = "0.3"
zcash_note_encryption = "0.1"
zcash_primitives = { version = "0.7", features = ["transparent-inputs"] }
zcash_proofs = "0.7"
zcash_proofs = "0.7.1"
ed25519-zebra = "3"
zeroize = "1.4.2"

View File

@ -7,6 +7,12 @@ description = "The cryptographic code in this crate has been reviewed for correc
[criteria.license-reviewed]
description = "The license of this crate has been reviewed for compatibility with its usage in this repository. If the crate is not available under the MIT license, `contrib/debian/copyright` has been updated with a corresponding copyright notice for files under `depends/*/vendored-sources/CRATE_NAME`."
[[audits.bellman]]
who = "Jack Grigg <jack@z.cash>"
criteria = ["crypto-reviewed", "safe-to-deploy"]
delta = "0.13.0 -> 0.13.1"
notes = "Adds multi-threaded batch validation, which I checked against the existing single-threaded batch validation."
[[audits.equihash]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
@ -21,25 +27,13 @@ notes = "The ECC core team maintains this crate, and we have reviewed every line
[[audits.f4jumble]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.f4jumble]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.halo2_gadgets]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.halo2_gadgets]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
@ -51,13 +45,7 @@ notes = "The ECC core team maintains this crate, and we have reviewed every line
[[audits.halo2_proofs]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.halo2_proofs]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
@ -69,13 +57,7 @@ notes = "The ECC core team maintains this crate, and we have reviewed every line
[[audits.orchard]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.orchard]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
@ -105,25 +87,13 @@ notes = "The ECC core team maintains this crate, and we have reviewed every line
[[audits.zcash_note_encryption]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.zcash_note_encryption]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.1.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.zcash_primitives]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.6.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.zcash_primitives]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.6.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
@ -135,13 +105,7 @@ notes = "The ECC core team maintains this crate, and we have reviewed every line
[[audits.zcash_proofs]]
who = "Jack Grigg <jack@z.cash>"
criteria = "crypto-reviewed"
version = "0.6.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.zcash_proofs]]
who = "Jack Grigg <jack@z.cash>"
criteria = "safe-to-deploy"
criteria = ["crypto-reviewed", "safe-to-deploy"]
version = "0.6.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
@ -151,3 +115,9 @@ criteria = ["crypto-reviewed", "safe-to-deploy"]
delta = "0.6.0 -> 0.7.0"
notes = "The ECC core team maintains this crate, and we have reviewed every line."
[[audits.zcash_proofs]]
who = "Jack Grigg <jack@z.cash>"
criteria = ["crypto-reviewed", "safe-to-deploy"]
delta = "0.7.0 -> 0.7.1"
notes = "The ECC core team maintains this crate, and we have reviewed every line."

File diff suppressed because it is too large Load Diff

View File

@ -176,6 +176,11 @@ def create_benchmark_archive(blk_hash):
unique_inputs = {}
for i in sorted(inputs):
if i[0] in blk['tx']:
# This is a child transaction spending the output of a parent transaction in
# the same block. We don't want to store the parent coin in the inputs table,
# or this will fail the BIP 30 consensus rule.
continue
if i[0] in unique_inputs:
unique_inputs[i[0]].append(i[1])
else:
@ -262,3 +267,4 @@ if __name__ == '__main__':
check_deps()
create_benchmark_archive('0000000007cdb809e48e51dd0b530e8f5073e0a9e9bd7ae920fe23e874658c74')
create_benchmark_archive('0000000000651d7fb11f0dd1be8dc87c29dca74cbf91140c6aafbacc09e7da04')
create_benchmark_archive('00000000012e0b5a8fb0d67c995b92e7c097ddeeab1151de61c4f484611fde11')

View File

@ -164,6 +164,10 @@ function extract_benchmark_data_1708048 {
extract_benchmark_data block-1708048 bdae4b1baf528fa93d86b902e1032d4d1fc3d3080f76cd284635787e472f2534
}
function extract_benchmark_data_1723244 {
extract_benchmark_data block-1723244 94acfef482103a61c848ddaa3129877cba987c4a00fa42425f4cf030b7c3fa4c
}
if [ $# -lt 2 ]
then
@ -229,6 +233,10 @@ case "$1" in
extract_benchmark_data_107134
zcash_rpc zcbenchmark connectblockslow 10
;;
connectblocksapling)
extract_benchmark_data_1723244
zcash_rpc zcbenchmark connectblocksapling 10
;;
connectblockorchard)
extract_benchmark_data_1708048
zcash_rpc zcbenchmark connectblockorchard 10
@ -295,6 +303,10 @@ case "$1" in
extract_benchmark_data_107134
zcash_rpc zcbenchmark connectblockslow 1
;;
connectblocksapling)
extract_benchmark_data_1723244
zcash_rpc zcbenchmark connectblocksapling 1
;;
connectblockorchard)
extract_benchmark_data_1708048
zcash_rpc zcbenchmark connectblockorchard 1
@ -359,6 +371,10 @@ case "$1" in
extract_benchmark_data_107134
zcash_rpc zcbenchmark connectblockslow 1
;;
connectblocksapling)
extract_benchmark_data_1723244
zcash_rpc zcbenchmark connectblocksapling 1
;;
connectblockorchard)
extract_benchmark_data_1708048
zcash_rpc zcbenchmark connectblockorchard 1

View File

@ -11,6 +11,7 @@
#include <librustzcash.h>
#include <rust/ed25519.h>
#include <rust/sapling.h>
#include <rust/orchard.h>
// Subclass of CTransaction which doesn't call UpdateHash when constructing
@ -534,6 +535,7 @@ TEST(ChecktransactionTests, BadTxnsPrevoutNull) {
TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) {
SelectParams(CBaseChainParams::REGTEST);
auto consensus = Params().GetConsensus();
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = std::nullopt;
auto orchardAuth = orchard::AuthValidator::Disabled();
CMutableTransaction mtx = GetValidTransaction();
@ -551,20 +553,21 @@ TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) {
// during initial block download, for transactions being accepted into the
// mempool (and thus not mined), DoS ban score should be zero, else 10
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return true; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return true; });
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return false; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return false; });
// for transactions that have been mined in a block, DoS ban score should
// always be 100.
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return true; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return true; });
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return false; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return false; });
}
TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
SelectParams(CBaseChainParams::REGTEST);
auto consensus = Params().GetConsensus();
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = std::nullopt;
auto orchardAuth = orchard::AuthValidator::Disabled();
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
@ -585,7 +588,7 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
CCoinsViewCache view(&baseView);
// Ensure that the transaction validates against Sapling.
EXPECT_TRUE(ContextualCheckShieldedInputs(
tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, false,
tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, false,
[](const Consensus::Params&) { return false; }));
// Attempt to validate the inputs against Blossom. We should be notified
@ -597,7 +600,7 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
HexInt(saplingBranchId)),
false, "")).Times(1);
EXPECT_FALSE(ContextualCheckShieldedInputs(
tx, txdata, state, view, orchardAuth, consensus, blossomBranchId, false, false,
tx, txdata, state, view, saplingAuth, orchardAuth, consensus, blossomBranchId, false, false,
[](const Consensus::Params&) { return false; }));
// Attempt to validate the inputs against Heartwood. All we should learn is
@ -607,13 +610,14 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
10, false, REJECT_INVALID,
"bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
EXPECT_FALSE(ContextualCheckShieldedInputs(
tx, txdata, state, view, orchardAuth, consensus, heartwoodBranchId, false, false,
tx, txdata, state, view, saplingAuth, orchardAuth, consensus, heartwoodBranchId, false, false,
[](const Consensus::Params&) { return false; }));
}
TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
SelectParams(CBaseChainParams::REGTEST);
auto consensus = Params().GetConsensus();
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = std::nullopt;
auto orchardAuth = orchard::AuthValidator::Disabled();
AssumeShieldedInputsExistAndAreSpendable baseView;
@ -631,7 +635,7 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
CTransaction tx(mtx);
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
MockCValidationState state;
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, true));
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, true));
}
// Copied from libsodium/crypto_sign/ed25519/ref10/open.c
@ -655,15 +659,15 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
// during initial block download, for transactions being accepted into the
// mempool (and thus not mined), DoS ban score should be zero, else 10
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return true; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return true; });
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return false; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return false; });
// for transactions that have been mined in a block, DoS ban score should
// always be 100.
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return true; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return true; });
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return false; });
ContextualCheckShieldedInputs(tx, txdata, state, view, saplingAuth, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return false; });
}
TEST(ChecktransactionTests, OverwinterConstructors) {
@ -1321,20 +1325,23 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
// Coinbase transaction should pass contextual checks.
EXPECT_TRUE(ContextualCheckTransaction(tx, state, chainparams, 10, 57));
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = sapling::init_batch_validator();
auto orchardAuth = orchard::AuthValidator::Disabled();
auto heartwoodBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_HEARTWOOD].nBranchId;
// Coinbase transaction does not pass shielded input checks, as bindingSig
// consensus rule is enforced.
// consensus rule is enforced. ContextualCheckShieldedInputs passes because
// the rest of the input checks pass, but saplingAuth fails when it attempts
// to validate the batch of signatures that includes bindingSig.
// - Note that coinbase txs don't have a previous output corresponding to
// their transparent input; ZIP 244 handles this by making the coinbase
// sighash the txid.
PrecomputedTransactionData txdata(tx, {});
AssumeShieldedInputsExistAndAreSpendable baseView;
CCoinsViewCache view(&baseView);
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid", false, "")).Times(1);
EXPECT_FALSE(ContextualCheckShieldedInputs(
tx, txdata, state, view, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true));
EXPECT_TRUE(ContextualCheckShieldedInputs(
tx, txdata, state, view, saplingAuth, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true));
EXPECT_FALSE(saplingAuth.value()->validate());
RegtestDeactivateHeartwood();
}

View File

@ -47,7 +47,6 @@
#include <rust/ed25519.h>
#include <rust/metrics.h>
#include <rust/sapling.h>
using namespace std;
@ -1246,6 +1245,7 @@ bool ContextualCheckShieldedInputs(
const PrecomputedTransactionData& txdata,
CValidationState &state,
const CCoinsViewCache &view,
std::optional<rust::Box<sapling::BatchValidator>>& saplingAuth,
std::optional<orchard::AuthValidator>& orchardAuth,
const Consensus::Params& consensus,
uint32_t consensusBranchId,
@ -1318,17 +1318,16 @@ bool ContextualCheckShieldedInputs(
if (!tx.vShieldedSpend.empty() ||
!tx.vShieldedOutput.empty())
{
auto ctx = sapling::init_verifier();
auto assembler = sapling::new_bundle_assembler();
for (const SpendDescription &spend : tx.vShieldedSpend) {
if (!ctx->check_spend(
if (!assembler->add_spend(
spend.cv.GetRawBytes(),
spend.anchor.GetRawBytes(),
spend.nullifier.GetRawBytes(),
spend.rk.GetRawBytes(),
spend.zkproof,
spend.spendAuthSig,
dataToBeSigned.GetRawBytes()
spend.spendAuthSig
)) {
return state.DoS(
dosLevelPotentiallyRelaxing,
@ -1338,10 +1337,12 @@ bool ContextualCheckShieldedInputs(
}
for (const OutputDescription &output : tx.vShieldedOutput) {
if (!ctx->check_output(
if (!assembler->add_output(
output.cv.GetRawBytes(),
output.cmu.GetRawBytes(),
output.ephemeralKey.GetRawBytes(),
output.encCiphertext,
output.outCiphertext,
output.zkproof
)) {
// This should be a non-contextual check, but we check it here
@ -1352,15 +1353,19 @@ bool ContextualCheckShieldedInputs(
}
}
if (!ctx->final_check(
auto bundle = sapling::finish_bundle_assembly(
std::move(assembler),
tx.GetValueBalanceSapling(),
tx.bindingSig,
dataToBeSigned.GetRawBytes()
)) {
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckShieldedInputs(): Sapling binding signature invalid"),
REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid");
tx.bindingSig);
// Queue Sapling bundle to be batch-validated. This also checks some consensus rules.
if (saplingAuth.has_value()) {
if (!saplingAuth.value()->check_bundle(std::move(bundle), dataToBeSigned.GetRawBytes())) {
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckShieldedInputs(): Sapling bundle invalid"),
REJECT_INVALID, "bad-txns-sapling-bundle-invalid");
}
}
}
@ -1986,6 +1991,11 @@ bool AcceptToMemoryPool(
__func__, hash.ToString(), FormatStateMessage(state));
}
// This will be a single-transaction batch, which will be more efficient
// than unbatched if the transaction contains at least one Sapling Spend
// or at least two Sapling Outputs.
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = sapling::init_batch_validator();
// This will be a single-transaction batch, which is still more efficient as every
// Orchard bundle contains at least two signatures.
std::optional<orchard::AuthValidator> orchardAuth = orchard::AuthValidator::Batch();
@ -1996,6 +2006,7 @@ bool AcceptToMemoryPool(
txdata,
state,
view,
saplingAuth,
orchardAuth,
chainparams.GetConsensus(),
consensusBranchId,
@ -2005,7 +2016,11 @@ bool AcceptToMemoryPool(
return false;
}
// Check Orchard bundle authorizations. `orchardAuth` here is known to be non-null
// Check Sapling and Orchard bundle authorizations.
// `saplingAuth` and `orchardAuth` are known here to be non-null.
if (!saplingAuth.value()->validate()) {
return state.DoS(100, false, REJECT_INVALID, "bad-sapling-bundle-authorization");
}
if (!orchardAuth.value().Validate()) {
return state.DoS(100, false, REJECT_INVALID, "bad-orchard-bundle-authorization");
}
@ -3059,7 +3074,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// proof verification is expensive, disable if possible
auto verifier = fExpensiveChecks ? ProofVerifier::Strict() : ProofVerifier::Disabled();
// Disable Orchard batch signature validation if possible.
// Disable Sapling and Orchard batch validation if possible.
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = fExpensiveChecks ?
std::optional(sapling::init_batch_validator()) : std::nullopt;
std::optional<orchard::AuthValidator> orchardAuth = fExpensiveChecks ?
orchard::AuthValidator::Batch() : orchard::AuthValidator::Disabled();
@ -3302,6 +3319,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
txdata.back(),
state,
view,
saplingAuth,
orchardAuth,
chainparams.GetConsensus(),
consensusBranchId,
@ -3502,6 +3520,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
block.vtx[0].GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount");
// Ensure Sapling authorizations are valid (if we are checking them)
if (saplingAuth.has_value() && !saplingAuth.value()->validate()) {
return state.DoS(100,
error("ConnectBlock(): a Sapling bundle within the block is invalid"),
REJECT_INVALID, "bad-sapling-bundle-authorization");
}
// Ensure Orchard signatures are valid (if we are checking them)
if (orchardAuth.has_value() && !orchardAuth.value().Validate()) {
return state.DoS(100,

View File

@ -43,6 +43,7 @@
#include <utility>
#include <vector>
#include <rust/sapling.h>
#include <rust/orchard.h>
#include <boost/unordered_map.hpp>
@ -402,6 +403,7 @@ bool ContextualCheckShieldedInputs(
const PrecomputedTransactionData& txdata,
CValidationState &state,
const CCoinsViewCache &view,
std::optional<rust::Box<sapling::BatchValidator>>& saplingAuth,
std::optional<orchard::AuthValidator>& orchardAuth,
const Consensus::Params& consensus,
uint32_t consensusBranchId,

View File

@ -19,7 +19,7 @@
// See https://github.com/rust-lang/rfcs/pull/2585 for more background.
#![allow(clippy::not_unsafe_ptr_arg_deref)]
use bellman::groth16::{Parameters, PreparedVerifyingKey};
use bellman::groth16::{self, prepare_verifying_key, Parameters, PreparedVerifyingKey};
use blake2s_simd::Params as Blake2sParams;
use bls12_381::Bls12;
use group::{cofactor::CofactorGroup, GroupEncoding};
@ -88,8 +88,8 @@ mod test_harness_ffi;
mod tests;
static PROOF_PARAMETERS_LOADED: Once = Once::new();
static mut SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SAPLING_SPEND_VK: Option<groth16::VerifyingKey<Bls12>> = None;
static mut SAPLING_OUTPUT_VK: Option<groth16::VerifyingKey<Bls12>> = None;
static mut SPROUT_GROTH16_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SAPLING_SPEND_PARAMS: Option<Parameters<Bls12>> = None;
@ -179,6 +179,11 @@ pub extern "C" fn librustzcash_init_zksnark_params(
let sapling_spend_params = params.spend_params;
let sapling_output_params = params.output_params;
// We need to clone these because we aren't necessarily storing the proving
// parameters in memory.
let sapling_spend_vk = sapling_spend_params.vk.clone();
let sapling_output_vk = sapling_output_params.vk.clone();
// Generate Orchard parameters.
info!(target: "main", "Loading Orchard parameters");
let orchard_pk = load_proving_keys.then(orchard::circuit::ProvingKey::build);
@ -191,8 +196,8 @@ pub extern "C" fn librustzcash_init_zksnark_params(
SAPLING_OUTPUT_PARAMS = load_proving_keys.then(|| sapling_output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SAPLING_SPEND_VK = Some(sapling_spend_vk);
SAPLING_OUTPUT_VK = Some(sapling_output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
ORCHARD_PK = orchard_pk;
@ -845,7 +850,7 @@ pub extern "C" fn librustzcash_sapling_spend_proof(
anchor,
merkle_path,
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
&prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()),
)
.expect("proving should not fail");

View File

@ -7,14 +7,19 @@
// on the entire module.
#![allow(clippy::too_many_arguments)]
use bellman::groth16::Proof;
use bellman::groth16::{prepare_verifying_key, Proof};
use group::GroupEncoding;
use rand_core::OsRng;
use zcash_note_encryption::EphemeralKeyBytes;
use zcash_primitives::{
sapling::redjubjub::{self, Signature},
transaction::components::Amount,
sapling::{
redjubjub::{self, Signature},
Nullifier,
},
transaction::components::{sapling, Amount},
};
use zcash_proofs::sapling::SaplingVerificationContext;
use zcash_proofs::sapling::{self as sapling_proofs, SaplingVerificationContext};
use super::GROTH_PROOF_SIZE;
use super::{de_ct, SAPLING_OUTPUT_VK, SAPLING_SPEND_VK};
@ -22,11 +27,38 @@ use super::{de_ct, SAPLING_OUTPUT_VK, SAPLING_SPEND_VK};
#[cxx::bridge(namespace = "sapling")]
mod ffi {
extern "Rust" {
type Bundle;
type BundleAssembler;
fn new_bundle_assembler() -> Box<BundleAssembler>;
fn add_spend(
self: &mut BundleAssembler,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: [u8; 32],
rk: &[u8; 32],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
spend_auth_sig: &[u8; 64],
) -> bool;
fn add_output(
self: &mut BundleAssembler,
cv: &[u8; 32],
cmu: &[u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn finish_bundle_assembly(
assembler: Box<BundleAssembler>,
value_balance: i64,
binding_sig: [u8; 64],
) -> Box<Bundle>;
type Verifier;
fn init_verifier() -> Box<Verifier>;
fn check_spend(
&mut self,
self: &mut Verifier,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: &[u8; 32],
@ -36,21 +68,139 @@ mod ffi {
sighash_value: &[u8; 32],
) -> bool;
fn check_output(
&mut self,
self: &mut Verifier,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: &[u8; 32],
zkproof: &[u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn final_check(
&self,
self: &Verifier,
value_balance: i64,
binding_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool;
type BatchValidator;
fn init_batch_validator() -> Box<BatchValidator>;
fn check_bundle(self: &mut BatchValidator, bundle: Box<Bundle>, sighash: [u8; 32]) -> bool;
fn validate(self: &mut BatchValidator) -> bool;
}
}
struct Bundle(sapling::Bundle<sapling::Authorized>);
struct BundleAssembler {
shielded_spends: Vec<sapling::SpendDescription<sapling::Authorized>>,
shielded_outputs: Vec<sapling::OutputDescription<[u8; 192]>>, // GROTH_PROOF_SIZE
}
fn new_bundle_assembler() -> Box<BundleAssembler> {
Box::new(BundleAssembler {
shielded_spends: vec![],
shielded_outputs: vec![],
})
}
impl BundleAssembler {
fn add_spend(
self: &mut BundleAssembler,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: [u8; 32],
rk: &[u8; 32],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
spend_auth_sig: &[u8; 64],
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
Some(p) => p,
None => return false,
};
// Deserialize the anchor, which should be an element
// of Fr.
let anchor = match de_ct(bls12_381::Scalar::from_bytes(anchor)) {
Some(a) => a,
None => return false,
};
// Deserialize rk
let rk = match redjubjub::PublicKey::read(&rk[..]) {
Ok(p) => p,
Err(_) => return false,
};
// Deserialize the signature
let spend_auth_sig = match Signature::read(&spend_auth_sig[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
self.shielded_spends.push(sapling::SpendDescription {
cv,
anchor,
nullifier: Nullifier(nullifier),
rk,
zkproof,
spend_auth_sig,
});
true
}
fn add_output(
self: &mut BundleAssembler,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
Some(p) => p,
None => return false,
};
// Deserialize the commitment, which should be an element
// of Fr.
let cmu = match de_ct(bls12_381::Scalar::from_bytes(cm)) {
Some(a) => a,
None => return false,
};
self.shielded_outputs.push(sapling::OutputDescription {
cv,
cmu,
ephemeral_key: EphemeralKeyBytes(ephemeral_key),
enc_ciphertext,
out_ciphertext,
zkproof,
});
true
}
}
#[allow(clippy::boxed_local)]
fn finish_bundle_assembly(
assembler: Box<BundleAssembler>,
value_balance: i64,
binding_sig: [u8; 64],
) -> Box<Bundle> {
let value_balance = Amount::from_i64(value_balance).expect("parsed elsewhere");
let binding_sig = redjubjub::Signature::read(&binding_sig[..]).expect("parsed elsewhere");
Box::new(Bundle(sapling::Bundle {
shielded_spends: assembler.shielded_spends,
shielded_outputs: assembler.shielded_outputs,
value_balance,
authorization: sapling::Authorized { binding_sig },
}))
}
struct Verifier(SaplingVerificationContext);
fn init_verifier() -> Box<Verifier> {
@ -110,7 +260,7 @@ impl Verifier {
sighash_value,
spend_auth_sig,
zkproof,
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
&prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()),
)
}
fn check_output(
@ -134,7 +284,7 @@ impl Verifier {
};
// Deserialize the ephemeral key
let ephemeral_key = match de_ct(jubjub::ExtendedPoint::from_bytes(ephemeral_key)) {
let epk = match de_ct(jubjub::ExtendedPoint::from_bytes(ephemeral_key)) {
Some(p) => p,
None => return false,
};
@ -148,9 +298,9 @@ impl Verifier {
self.0.check_output(
cv,
cm,
ephemeral_key,
epk,
zkproof,
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
&prepare_verifying_key(unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap()),
)
}
fn final_check(
@ -174,3 +324,43 @@ impl Verifier {
.final_check(value_balance, sighash_value, binding_sig)
}
}
struct BatchValidator(Option<sapling_proofs::BatchValidator>);
fn init_batch_validator() -> Box<BatchValidator> {
Box::new(BatchValidator(Some(sapling_proofs::BatchValidator::new())))
}
impl BatchValidator {
/// Checks the bundle against Sapling-specific consensus rules, and queues its
/// authorization for validation.
///
/// Returns `false` if the bundle doesn't satisfy the checked consensus rules. This
/// `BatchValidator` can continue to be used regardless, but some or all of the proofs
/// and signatures from this bundle may have already been added to the batch even if
/// it fails other consensus rules.
///
/// `sighash` must be for the transaction this bundle is within.
#[allow(clippy::boxed_local)]
fn check_bundle(&mut self, bundle: Box<Bundle>, sighash: [u8; 32]) -> bool {
if let Some(validator) = &mut self.0 {
validator.check_bundle(bundle.0, sighash)
} else {
tracing::error!("sapling::BatchValidator has already been used");
false
}
}
fn validate(&mut self) -> bool {
if let Some(validator) = self.0.take() {
validator.validate(
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
OsRng,
)
} else {
tracing::error!("sapling::BatchValidator has already been used");
false
}
}
}

View File

@ -36,6 +36,7 @@
#include <boost/test/data/test_case.hpp>
#include <rust/ed25519.h>
#include <rust/sapling.h>
#include <rust/orchard.h>
#include <univalue.h>
@ -356,6 +357,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact
void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransaction tx)
{
auto verifier = ProofVerifier::Strict();
std::optional<rust::Box<sapling::BatchValidator>> saplingAuth = std::nullopt;
auto orchardAuth = orchard::AuthValidator::Disabled();
{
// Ensure that empty vin/vout remain invalid without
@ -390,7 +392,13 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa
BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state));
BOOST_CHECK(ContextualCheckTransaction(newTx, state, Params(), 0, true));
BOOST_CHECK(!ContextualCheckShieldedInputs(newTx, txdata, state, view, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
BOOST_CHECK(!ContextualCheckShieldedInputs(
newTx, txdata,
state, view,
saplingAuth, orchardAuth,
Params().GetConsensus(),
consensusBranchId,
false, true));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature");
// Empty output script.
@ -406,7 +414,13 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa
state = CValidationState();
BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state));
BOOST_CHECK(ContextualCheckTransaction(newTx, state, Params(), 0, true));
BOOST_CHECK(ContextualCheckShieldedInputs(newTx, txdata, state, view, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
BOOST_CHECK(ContextualCheckShieldedInputs(
newTx, txdata,
state, view,
saplingAuth, orchardAuth,
Params().GetConsensus(),
consensusBranchId,
false, true));
BOOST_CHECK_EQUAL(state.GetRejectReason(), "");
}
{

View File

@ -92,7 +92,7 @@ public:
std::array<uint8_t, WIDTH> GetRawBytes() const
{
std::array<uint8_t, WIDTH> buf = {};
memcpy(buf.data(), this->begin(), WIDTH);
std::memcpy(buf.data(), this->begin(), WIDTH);
return buf;
}

View File

@ -2890,6 +2890,11 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode");
}
sample_times.push_back(benchmark_connectblock_slow());
} else if (benchmarktype == "connectblocksapling") {
if (Params().NetworkIDString() != "regtest") {
throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode");
}
sample_times.push_back(benchmark_connectblock_sapling());
} else if (benchmarktype == "connectblockorchard") {
if (Params().NetworkIDString() != "regtest") {
throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode");

View File

@ -604,6 +604,57 @@ double benchmark_connectblock_slow()
return duration;
}
double benchmark_connectblock_sapling()
{
// Test for slowness encountered on 2022-07-01
SelectParams(CBaseChainParams::MAIN);
CBlock block;
FILE* fp = fsbridge::fopen(GetDataDir() / "benchmark/block-1723244.dat", "rb");
if (!fp) throw new std::runtime_error("Failed to open block data file");
CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION);
blkFile >> block;
blkFile.fclose();
// Fake its inputs
auto hashPrev = uint256S("0000000001286c016284523a1b343e731539bdf1fac4159f96d70a711cf9c960");
FakeCoinsViewDB fakeDB("benchmark/block-1723244-inputs", hashPrev);
fakeDB.SetSaplingTrees({
"012380d4632f51d9aff726d9f2d0013691c86da31279e110e852d49e4737ce7c14001401c4dedf5f6a0e5be05be051fec4608153084b0fdb74cd92f01d8deb77e72b6a5f01d5891ebfeae46b30846a301f8aaba34d64428e91fc406d0db13fcf723100753100018b4af5e391e5720a2b7a20c289a7691bb6a29ffcda574e06eaf4d6597048fd2200017cc935f49a763d29f9e876fe0a85c6dbbd32ab5b632bc2da058c44c652f713190191cac78cfcb2ca0eaaad52778c93cc2549c6ae0460c37f0b304843ccf8c15e3301c4a86822bec7ea056f984aa72a059a566addd797c08be929289123f698a4631d01e7cbe9d650e4c7dfbc78b328744515345f3e0988ea8d0dd5ce2af025d56d710001278234295da81c7fbfbb336719718f78c16025032ef9cc6007bc49351213d701014656c38189b9f768a492ee90758c63d4be59c92e21b3e9d00916acf1e528774601add4db5d8e6357c91d3b1fc605c0961353b5aa683520923f47daa51b2864563c00000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009",
"0159a96b6b5a6dc61154f9b0cb66121ac04050978fdee3d1cf6deadac0b3118d4801318d4e70e26b35a2af00ee10cd4370553cfd6c058d762a9b37e7d43fba96a41f14013addafd08d0b8fcc60723f7ac79b589e6fd1e0209ec1865ef521ddc9537d010000018636e49531fdf3d5d7f817ed1488babf61ef0e750255e59f93cc111f521d800a0001a19338964f3cf13d3a74868b8a53c15dfd6b28f6aef345238372f0051dc3bc1d011f386f8123ec951aed8d51e25c549e3e007429c3d3882dc5daebf8d70e878e4d0001c4a86822bec7ea056f984aa72a059a566addd797c08be929289123f698a4631d01e7cbe9d650e4c7dfbc78b328744515345f3e0988ea8d0dd5ce2af025d56d710001278234295da81c7fbfbb336719718f78c16025032ef9cc6007bc49351213d701014656c38189b9f768a492ee90758c63d4be59c92e21b3e9d00916acf1e528774601add4db5d8e6357c91d3b1fc605c0961353b5aa683520923f47daa51b2864563c00000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009",
"0175638110ef078c9c5e45f34399131e652d7e7e4e3ea28fa13702854c3e889d38001401b445b70b61de6c561ae9203c5ddcd594c3da14550493154a4fb6e770bff2d65901981edfb711be11868d221643fdbc0403f607b8ec06f6c2b7968c8ddb2e0968080001b97e4e33d8c0c08cabf8ee7a897d09d1b2e6a395bc43214b61d7ae7cea560e650001060784c088dee8e54ec94a47e7de9308cab6069e77d7303e1ac9f8fbc920a45a000001e7cbe9d650e4c7dfbc78b328744515345f3e0988ea8d0dd5ce2af025d56d710001278234295da81c7fbfbb336719718f78c16025032ef9cc6007bc49351213d701014656c38189b9f768a492ee90758c63d4be59c92e21b3e9d00916acf1e528774601add4db5d8e6357c91d3b1fc605c0961353b5aa683520923f47daa51b2864563c00000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009",
"01ca5608a4c5e15340674e0a91ad1c128a49c04230141a955a9d87f2c84734ee2401ed8561fa9f71a0283dcc4865dc44dc886ceb8a91c36abbbb139795bc86cbba321401e2488b560f9f641e4371cadb8569776df9170f82888869c7902f80909aae5f1e01ad01014509c3242d3fbf6754e485f8c87eca413ceace72a742e033947e3f014c0001e6de90c780bf39b4a40dedee586a89686133013b138529eea245add88685e15c00013c16c0b1a41f32caebe41ec1322521b8eaf0303c3969044fa6daaa8a20948d2100000000000001591e0969b34ce995643b3f043be0b07f45b641dd90fce7f4713b90b4a545fa16000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009",
"01cb51537fbdc52f8c92ab7d14156dc3d81d9d433fa438d479f4b822924f4fb64500140139c4c235ce18df178bff8169c4d2ecd5c1eb1d112a0dbde3bc22349a2a7a1e590000014c998c0f24b3cc9640acccca4af19cba8c1dd728570fc7080ed7487e7a232c4100011f386f8123ec951aed8d51e25c549e3e007429c3d3882dc5daebf8d70e878e4d0001c4a86822bec7ea056f984aa72a059a566addd797c08be929289123f698a4631d01e7cbe9d650e4c7dfbc78b328744515345f3e0988ea8d0dd5ce2af025d56d710001278234295da81c7fbfbb336719718f78c16025032ef9cc6007bc49351213d701014656c38189b9f768a492ee90758c63d4be59c92e21b3e9d00916acf1e528774601add4db5d8e6357c91d3b1fc605c0961353b5aa683520923f47daa51b2864563c00000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009",
"01dffd4c5286c411a7efc955f740f311f979d6ceb39f37f12e2530e45610452b4200140001180211de427aaf56cc398c972a158fa0ac1a4fc79c76746972907fb2e7b668360001be1c46b85e67695418b39e799aa8bcc19a8634463e3fa293c3e75c471fb8475301072f3b28b20e0306b48c1b1fa25b2d845b6125069299df3028d58cfd360b4953010ed297014c2acf3d0840b3dad9038071e150935054c3dcc0f8981dfeb822821501b5cf5b5ac994c75a11e2e47a0d8077d39d01e79a9ac24a1b83b108128a86f35d0001e7cbe9d650e4c7dfbc78b328744515345f3e0988ea8d0dd5ce2af025d56d710001278234295da81c7fbfbb336719718f78c16025032ef9cc6007bc49351213d701014656c38189b9f768a492ee90758c63d4be59c92e21b3e9d00916acf1e528774601add4db5d8e6357c91d3b1fc605c0961353b5aa683520923f47daa51b2864563c00000001a02e924803cc430e6360a63732092f621a116b1f156ccdc3e0f815c30a01b2510001bcefb8862360baae2a9a164c1367df2c27372d0c6963068c9ea4b1f84f5c526d00015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009"
});
fakeDB.SetOrchardTrees({
"01a8fb1700000000002b768d317551b491c8b993cd7e74c9196f3028b2a1a7a3f22971f7f4007c833e000e7005f57c540bf3e32695854f52d27a0456ca1be179bd073d9ca87acc92248413aeacb64a31a230338cfa6a2907f12bc5321a709ef298fb93ced3082c444f8a3814764dc108e86dc2c6d7c20a6e1759027d87029b5dc7e1e6b5be970ecbed913a186d95ac66b184f8844e57fece62a4d64cfeb73e7ed6e99146e79aacae7f5e002f9cd86767aea1f11147f65c588f94dce188315c40b22c0fa8751365ba453c28130cfb41380fdd7836985e2c4c488fdc3d1d1bd4390f350f0e1b8a448f47ac1c2bcbdd308beca04006b18928c4418aad2b3650677289b1b45ea5a21095c53103100ed4d0a8a440b03f1254ce1efb2d82a94cf001cffa0e7fd6ada813a2688b2430a69de998b87aebcd4c556503a45e559a422ecfbdf2f0e6318a8427e41a7b097676cfe97afff13797f82f8d631bd29edde424854139c64ab8241e2a23315514da371f0d3294843fd8f645019a04c07607342c70cf4cc793068355eaccdd6716bc79f0119f97113775379bf58f3f5d9d122766909b797947127b28248ff07205c1eb3aa1717c2a696ce0aba6c5b5bda44c4eda5cf69ae376cc8b334a2c23fb2b374feb2041bfd423c6cc3e064ee2b4705748a082836d39dd723515357fb06e30",
});
CCoinsViewCache view(&fakeDB);
// Fake the chain
CBlockIndex index(block);
index.nHeight = 1723244;
CBlockIndex indexPrev;
indexPrev.phashBlock = &hashPrev;
indexPrev.nHeight = index.nHeight - 1;
indexPrev.hashFinalSaplingRoot = uint256S("3922921f92d9abe9e440bb5a21fa901a0647057aa85c201e38d150a5e00043b8");
indexPrev.hashFinalOrchardRoot = uint256S("0e9a0a55e201062a5677b8ab15c96e9f4983726af31cd96b14b71ac6a931aea2");
index.pprev = &indexPrev;
mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev));
CValidationState state;
struct timeval tv_start;
timer_start(tv_start);
assert(ConnectBlock(block, state, &index, view, Params(), true, false));
auto duration = timer_stop(tv_start);
// Undo alterations to global state
mapBlockIndex.erase(hashPrev);
SelectParams(ChainNameFromCommandLine());
return duration;
}
double benchmark_connectblock_orchard()
{
// Test for slowness encountered on 2022-06-20

View File

@ -17,6 +17,7 @@ extern double benchmark_try_decrypt_sapling_notes(size_t nAddrs);
extern double benchmark_increment_sprout_note_witnesses(size_t nTxs);
extern double benchmark_increment_sapling_note_witnesses(size_t nTxs);
extern double benchmark_connectblock_slow();
extern double benchmark_connectblock_sapling();
extern double benchmark_connectblock_orchard();
extern double benchmark_sendtoaddress(CAmount amount);
extern double benchmark_loadwallet();