Merge pull request #5804 from str4d/consensus-check-coinbase-shielded-spends
Apply HaveShieldedRequirements to coinbase transactions
This commit is contained in:
commit
fbddebc63f
|
@ -545,18 +545,20 @@ TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) {
|
|||
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||
|
||||
MockCValidationState state;
|
||||
AssumeShieldedInputsExistAndAreSpendable baseView;
|
||||
CCoinsViewCache view(&baseView);
|
||||
// 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, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return true; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, 0, false, false, [](const Consensus::Params&) { return false; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return true; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return false; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, 0, false, true, [](const Consensus::Params&) { return false; });
|
||||
}
|
||||
|
||||
TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
|
||||
|
@ -578,9 +580,11 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
|
|||
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||
|
||||
MockCValidationState state;
|
||||
AssumeShieldedInputsExistAndAreSpendable baseView;
|
||||
CCoinsViewCache view(&baseView);
|
||||
// Ensure that the transaction validates against Sapling.
|
||||
EXPECT_TRUE(ContextualCheckShieldedInputs(
|
||||
tx, txdata, state, orchardAuth, consensus, saplingBranchId, false, false,
|
||||
tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, false,
|
||||
[](const Consensus::Params&) { return false; }));
|
||||
|
||||
// Attempt to validate the inputs against Blossom. We should be notified
|
||||
|
@ -592,7 +596,7 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
|
|||
HexInt(saplingBranchId)),
|
||||
false, "")).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckShieldedInputs(
|
||||
tx, txdata, state, orchardAuth, consensus, blossomBranchId, false, false,
|
||||
tx, txdata, state, view, orchardAuth, consensus, blossomBranchId, false, false,
|
||||
[](const Consensus::Params&) { return false; }));
|
||||
|
||||
// Attempt to validate the inputs against Heartwood. All we should learn is
|
||||
|
@ -602,7 +606,7 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
|
|||
10, false, REJECT_INVALID,
|
||||
"bad-txns-invalid-joinsplit-signature", false, "")).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckShieldedInputs(
|
||||
tx, txdata, state, orchardAuth, consensus, heartwoodBranchId, false, false,
|
||||
tx, txdata, state, view, orchardAuth, consensus, heartwoodBranchId, false, false,
|
||||
[](const Consensus::Params&) { return false; }));
|
||||
}
|
||||
|
||||
|
@ -611,6 +615,9 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
|
|||
auto consensus = Params().GetConsensus();
|
||||
auto orchardAuth = orchard::AuthValidator::Disabled();
|
||||
|
||||
AssumeShieldedInputsExistAndAreSpendable baseView;
|
||||
CCoinsViewCache view(&baseView);
|
||||
|
||||
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
|
||||
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
|
||||
|
||||
|
@ -623,7 +630,7 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
|
|||
CTransaction tx(mtx);
|
||||
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||
MockCValidationState state;
|
||||
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, orchardAuth, consensus, saplingBranchId, false, true));
|
||||
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, true));
|
||||
}
|
||||
|
||||
// Copied from libsodium/crypto_sign/ed25519/ref10/open.c
|
||||
|
@ -647,15 +654,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, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return true; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, saplingBranchId, false, false, [](const Consensus::Params&) { return false; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return true; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, 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, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return false; });
|
||||
ContextualCheckShieldedInputs(tx, txdata, state, view, orchardAuth, consensus, saplingBranchId, false, true, [](const Consensus::Params&) { return false; });
|
||||
}
|
||||
|
||||
TEST(ChecktransactionTests, OverwinterConstructors) {
|
||||
|
@ -1322,9 +1329,11 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
|
|||
// 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, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true));
|
||||
tx, txdata, state, view, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true));
|
||||
|
||||
RegtestDeactivateHeartwood();
|
||||
}
|
||||
|
|
74
src/main.cpp
74
src/main.cpp
|
@ -1233,6 +1233,7 @@ bool ContextualCheckShieldedInputs(
|
|||
const CTransaction& tx,
|
||||
const PrecomputedTransactionData& txdata,
|
||||
CValidationState &state,
|
||||
const CCoinsViewCache &view,
|
||||
orchard::AuthValidator& orchardAuth,
|
||||
const Consensus::Params& consensus,
|
||||
uint32_t consensusBranchId,
|
||||
|
@ -1240,6 +1241,12 @@ bool ContextualCheckShieldedInputs(
|
|||
bool isMined,
|
||||
bool (*isInitBlockDownload)(const Consensus::Params&))
|
||||
{
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
if (!Consensus::CheckTxShieldedInputs(tx, state, view, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int DOS_LEVEL_BLOCK = 100;
|
||||
// DoS level set to 10 to be more forgiving.
|
||||
const int DOS_LEVEL_MEMPOOL = 10;
|
||||
|
@ -1868,16 +1875,10 @@ bool AcceptToMemoryPool(
|
|||
return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
||||
|
||||
// Are the shielded spends' requirements met?
|
||||
auto unmetShieldedReq = view.HaveShieldedRequirements(tx);
|
||||
if (unmetShieldedReq) {
|
||||
auto txid = tx.GetHash().ToString();
|
||||
auto rejectCode = ShieldedReqRejectCode(*unmetShieldedReq);
|
||||
auto rejectReason = ShieldedReqRejectReason(*unmetShieldedReq);
|
||||
TracingError(
|
||||
"main", "AcceptToMemoryPool(): shielded requirements not met",
|
||||
"txid", txid.c_str(),
|
||||
"reason", rejectReason.c_str());
|
||||
return state.Invalid(false, rejectCode, rejectReason);
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
if (!Consensus::CheckTxShieldedInputs(tx, state, view, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bring the best block into scope
|
||||
|
@ -1885,6 +1886,7 @@ bool AcceptToMemoryPool(
|
|||
|
||||
nValueIn = view.GetValueIn(tx);
|
||||
|
||||
// we have all inputs cached now, so switch back to dummy
|
||||
view.SetBackend(dummy);
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
|
@ -2004,6 +2006,7 @@ bool AcceptToMemoryPool(
|
|||
tx,
|
||||
txdata,
|
||||
state,
|
||||
view,
|
||||
orchardAuth,
|
||||
chainparams.GetConsensus(),
|
||||
consensusBranchId,
|
||||
|
@ -2551,26 +2554,39 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
|
|||
}
|
||||
|
||||
namespace Consensus {
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams)
|
||||
bool CheckTxShieldedInputs(
|
||||
const CTransaction& tx,
|
||||
CValidationState& state,
|
||||
const CCoinsViewCache& view,
|
||||
int dosLevel)
|
||||
{
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
if (!inputs.HaveInputs(tx))
|
||||
return state.Invalid(false, 0, "", "Inputs unavailable");
|
||||
|
||||
// Are the shielded spends' requirements met?
|
||||
auto unmetShieldedReq = inputs.HaveShieldedRequirements(tx);
|
||||
auto unmetShieldedReq = view.HaveShieldedRequirements(tx);
|
||||
if (unmetShieldedReq) {
|
||||
auto txid = tx.GetHash().ToString();
|
||||
auto rejectCode = ShieldedReqRejectCode(*unmetShieldedReq);
|
||||
auto rejectReason = ShieldedReqRejectReason(*unmetShieldedReq);
|
||||
TracingError(
|
||||
"main", "CheckInputs(): shielded requirements not met",
|
||||
"main", "CheckTxShieldedInputs(): shielded requirements not met",
|
||||
"txid", txid.c_str(),
|
||||
"reason", rejectReason.c_str());
|
||||
return state.Invalid(false, rejectCode, rejectReason);
|
||||
return state.DoS(dosLevel, false, rejectCode, rejectReason);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams)
|
||||
{
|
||||
assert(!tx.IsCoinBase());
|
||||
|
||||
// Indented to reduce conflicts with upstream.
|
||||
{
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
if (!inputs.HaveInputs(tx))
|
||||
return state.Invalid(false, 0, "", "Inputs unavailable");
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
CAmount nFees = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
|
@ -2621,6 +2637,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
nFees += nTxFee;
|
||||
if (!MoneyRange(nFees))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}// namespace Consensus
|
||||
|
@ -3220,25 +3237,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
// txid.
|
||||
std::vector<CTxOut> allPrevOutputs;
|
||||
|
||||
// Are the shielded spends' requirements met?
|
||||
if (!Consensus::CheckTxShieldedInputs(tx, state, view, 100)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
if (!view.HaveInputs(tx))
|
||||
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
|
||||
REJECT_INVALID, "bad-txns-inputs-missingorspent");
|
||||
|
||||
// Are the shielded spends' requirements met?
|
||||
auto unmetShieldedReq = view.HaveShieldedRequirements(tx);
|
||||
if (unmetShieldedReq) {
|
||||
auto txid = tx.GetHash().ToString();
|
||||
auto rejectCode = ShieldedReqRejectCode(*unmetShieldedReq);
|
||||
auto rejectReason = ShieldedReqRejectReason(*unmetShieldedReq);
|
||||
TracingError(
|
||||
"main", "ConnectBlock(): shielded requirements not met",
|
||||
"txid", txid.c_str(),
|
||||
"reason", rejectReason.c_str());
|
||||
return state.DoS(100, false, rejectCode, rejectReason);
|
||||
}
|
||||
|
||||
for (const auto& input : tx.vin) {
|
||||
allPrevOutputs.push_back(view.GetOutputFor(input));
|
||||
}
|
||||
|
@ -3303,6 +3312,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
tx,
|
||||
txdata.back(),
|
||||
state,
|
||||
view,
|
||||
orchardAuth,
|
||||
chainparams.GetConsensus(),
|
||||
consensusBranchId,
|
||||
|
|
28
src/main.h
28
src/main.h
|
@ -373,19 +373,29 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
|
|||
std::vector<CScriptCheck> *pvChecks = NULL);
|
||||
|
||||
/**
|
||||
* Checks the signatures for a transaction's shielded components.
|
||||
* Check whether all shielded inputs of this transaction are valid.
|
||||
*
|
||||
* This checks that:
|
||||
* - The anchors in the transaction exist in the given view.
|
||||
* - The nullifiers in the transaction do not exist in the given view.
|
||||
* - The signatures for the transaction's shielded components are valid.
|
||||
*
|
||||
* This also currently checks the Sapling proofs, due to the way the Rust verification
|
||||
* code is written. Sprout and Orchard proofs are currently checked in CheckTransaction().
|
||||
* Once we have batch proof validation implemented, these will all be accumulated in
|
||||
* CheckTransaction().
|
||||
*
|
||||
* To skip checking signatures, use `Consensus::CheckTxShieldedInputs` instead.
|
||||
*
|
||||
* This does not modify the view to add the nullifiers to the spent set.
|
||||
*
|
||||
* The `isInitBlockDownload` argument is a function parameter to assist with testing.
|
||||
*/
|
||||
bool ContextualCheckShieldedInputs(
|
||||
const CTransaction& tx,
|
||||
const PrecomputedTransactionData& txdata,
|
||||
CValidationState &state,
|
||||
const CCoinsViewCache &view,
|
||||
orchard::AuthValidator& orchardAuth,
|
||||
const Consensus::Params& consensus,
|
||||
uint32_t consensusBranchId,
|
||||
|
@ -410,6 +420,22 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||
|
||||
namespace Consensus {
|
||||
|
||||
/**
|
||||
* Check whether all shielded inputs of this transaction are valid.
|
||||
*
|
||||
* This checks that:
|
||||
* - The anchors in the transaction exist in the given view.
|
||||
* - The nullifiers in the transaction do not exist in the given view.
|
||||
*
|
||||
* This does not modify the view to add the nullifiers to the spent set.
|
||||
* This does not check proofs or signatures.
|
||||
*/
|
||||
bool CheckTxShieldedInputs(
|
||||
const CTransaction& tx,
|
||||
CValidationState& state,
|
||||
const CCoinsViewCache& view,
|
||||
int dosLevel);
|
||||
|
||||
/**
|
||||
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
||||
* This does not modify the UTXO set. This does not check scripts and sigs.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "test/test_util.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "transaction_builder.h"
|
||||
#include "utiltest.h"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
@ -436,6 +437,8 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa
|
|||
// joinsplits.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
AssumeShieldedInputsExistAndAreSpendable baseView;
|
||||
CCoinsViewCache view(&baseView);
|
||||
|
||||
Ed25519SigningKey joinSplitPrivKey;
|
||||
ed25519_generate_keypair(&joinSplitPrivKey, &newTx.joinSplitPubKey);
|
||||
|
@ -462,7 +465,7 @@ 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, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
|
||||
BOOST_CHECK(!ContextualCheckShieldedInputs(newTx, txdata, state, view, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature");
|
||||
|
||||
// Empty output script.
|
||||
|
@ -478,7 +481,7 @@ 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, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
|
||||
BOOST_CHECK(ContextualCheckShieldedInputs(newTx, txdata, state, view, orchardAuth, Params().GetConsensus(), consensusBranchId, false, true));
|
||||
BOOST_CHECK_EQUAL(state.GetRejectReason(), "");
|
||||
}
|
||||
{
|
||||
|
|
|
@ -583,42 +583,13 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
i++;
|
||||
}
|
||||
|
||||
// The SaltedTxidHasher is fine to use here; it salts the map keys automatically
|
||||
// with randomness generated on construction.
|
||||
boost::unordered_map<uint256, SproutMerkleTree, SaltedTxidHasher> intermediates;
|
||||
|
||||
for (const JSDescription &joinsplit : tx.vJoinSplit) {
|
||||
for (const uint256 &nf : joinsplit.nullifiers) {
|
||||
assert(!pcoins->GetNullifier(nf, SPROUT));
|
||||
}
|
||||
|
||||
SproutMerkleTree tree;
|
||||
auto it = intermediates.find(joinsplit.anchor);
|
||||
if (it != intermediates.end()) {
|
||||
tree = it->second;
|
||||
} else {
|
||||
assert(pcoins->GetSproutAnchorAt(joinsplit.anchor, tree));
|
||||
}
|
||||
|
||||
for (const uint256& commitment : joinsplit.commitments)
|
||||
{
|
||||
tree.append(commitment);
|
||||
}
|
||||
|
||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||
}
|
||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||
SaplingMerkleTree tree;
|
||||
|
||||
assert(pcoins->GetSaplingAnchorAt(spendDescription.anchor, tree));
|
||||
assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING));
|
||||
}
|
||||
if (fDependsWait)
|
||||
waitingOnDependants.push_back(&(*it));
|
||||
else {
|
||||
CValidationState state;
|
||||
bool fCheckResult = tx.IsCoinBase() ||
|
||||
Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, Params().GetConsensus());
|
||||
fCheckResult &= Consensus::CheckTxShieldedInputs(tx, state, mempoolDuplicate, 0);
|
||||
assert(fCheckResult);
|
||||
UpdateCoins(tx, mempoolDuplicate, 1000000);
|
||||
}
|
||||
|
@ -635,6 +606,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
} else {
|
||||
bool fCheckResult = entry->GetTx().IsCoinBase() ||
|
||||
Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, Params().GetConsensus());
|
||||
fCheckResult &= Consensus::CheckTxShieldedInputs(entry->GetTx(), state, mempoolDuplicate, 0);
|
||||
assert(fCheckResult);
|
||||
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
|
||||
stepsSinceLastRemove = 0;
|
||||
|
|
|
@ -5,12 +5,36 @@
|
|||
#ifndef ZCASH_UTILTEST_H
|
||||
#define ZCASH_UTILTEST_H
|
||||
|
||||
#include "coins.h"
|
||||
#include "key_io.h"
|
||||
#include "wallet/wallet.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/Note.hpp"
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
|
||||
// A fake chain state view where anchors and nullifiers are assumed to exist.
|
||||
class AssumeShieldedInputsExistAndAreSpendable : public CCoinsView {
|
||||
public:
|
||||
AssumeShieldedInputsExistAndAreSpendable() {}
|
||||
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||
// Always return false so we treat every nullifier as being unspent.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Sprout
|
||||
CWalletTx GetValidSproutReceive(const libzcash::SproutSpendingKey& sk,
|
||||
CAmount value,
|
||||
|
|
Loading…
Reference in New Issue