mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
[BROKEN] Add CA validation
This commit is contained in:
parent
d53479c9ff
commit
0b5066143d
23 changed files with 712 additions and 124 deletions
|
|
@ -41,7 +41,7 @@ private:
|
|||
|
||||
//! The queue of elements to be processed.
|
||||
//! As the order of booleans doesn't matter, it is used as a LIFO (stack)
|
||||
std::vector<T> queue;
|
||||
std::vector<T*> queue;
|
||||
|
||||
//! The number of workers (including the master) that are idle.
|
||||
int nIdle;
|
||||
|
|
@ -66,7 +66,7 @@ private:
|
|||
bool Loop(bool fMaster = false)
|
||||
{
|
||||
boost::condition_variable& cond = fMaster ? condMaster : condWorker;
|
||||
std::vector<T> vChecks;
|
||||
std::vector<T*> vChecks;
|
||||
vChecks.reserve(nBatchSize);
|
||||
unsigned int nNow = 0;
|
||||
bool fOk = true;
|
||||
|
|
@ -105,20 +105,18 @@ private:
|
|||
// * Try to account for idle jobs which will instantly start helping.
|
||||
// * Don't do batches smaller than 1 (duh), or larger than nBatchSize.
|
||||
nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1)));
|
||||
vChecks.resize(nNow);
|
||||
for (unsigned int i = 0; i < nNow; i++) {
|
||||
// We want the lock on the mutex to be as short as possible, so swap jobs from the global
|
||||
// queue to the local batch vector instead of copying.
|
||||
vChecks[i].swap(queue.back());
|
||||
queue.pop_back();
|
||||
}
|
||||
vChecks.clear();
|
||||
vChecks.insert(vChecks.end(), queue.end() - nNow, queue.end());
|
||||
queue.resize(queue.size() - nNow);
|
||||
// Check whether we need to do work at all
|
||||
fOk = fAllOk;
|
||||
}
|
||||
// execute work
|
||||
for (T& check : vChecks)
|
||||
for (T* check : vChecks) {
|
||||
if (fOk)
|
||||
fOk = check();
|
||||
fOk = (*check)();
|
||||
delete check;
|
||||
}
|
||||
vChecks.clear();
|
||||
} while (true);
|
||||
}
|
||||
|
|
@ -143,13 +141,10 @@ public:
|
|||
}
|
||||
|
||||
//! Add a batch of checks to the queue
|
||||
void Add(std::vector<T>& vChecks)
|
||||
void Add(const std::vector<T*> vChecks)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
for (T& check : vChecks) {
|
||||
queue.push_back(T());
|
||||
check.swap(queue.back());
|
||||
}
|
||||
queue.insert(queue.end(), vChecks.begin(), vChecks.end());
|
||||
nTodo += vChecks.size();
|
||||
if (vChecks.size() == 1)
|
||||
condWorker.notify_one();
|
||||
|
|
@ -159,6 +154,7 @@ public:
|
|||
|
||||
~CCheckQueue()
|
||||
{
|
||||
assert(queue.empty());
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -171,14 +167,14 @@ template <typename T>
|
|||
class CCheckQueueControl
|
||||
{
|
||||
private:
|
||||
CCheckQueue<T> * const pqueue;
|
||||
CCheckQueue<T>* const pqueue;
|
||||
bool fDone;
|
||||
|
||||
public:
|
||||
CCheckQueueControl() = delete;
|
||||
CCheckQueueControl(const CCheckQueueControl&) = delete;
|
||||
CCheckQueueControl& operator=(const CCheckQueueControl&) = delete;
|
||||
explicit CCheckQueueControl(CCheckQueue<T> * const pqueueIn) : pqueue(pqueueIn), fDone(false)
|
||||
explicit CCheckQueueControl(CCheckQueue<T>* const pqueueIn) : pqueue(pqueueIn), fDone(false)
|
||||
{
|
||||
// passed queue is supposed to be unused, or nullptr
|
||||
if (pqueue != nullptr) {
|
||||
|
|
@ -195,7 +191,7 @@ public:
|
|||
return fRet;
|
||||
}
|
||||
|
||||
void Add(std::vector<T>& vChecks)
|
||||
void Add(std::vector<T*> vChecks)
|
||||
{
|
||||
if (pqueue != nullptr)
|
||||
pqueue->Add(vChecks);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,30 @@ public:
|
|||
static CSecp256k1Init instance_of_csecp256k1;
|
||||
}
|
||||
|
||||
bool HasValidFee(const CTransaction& tx) {
|
||||
CAmountMap totalFee;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
CAmount fee = 0;
|
||||
if (tx.vout[i].IsFee()) {
|
||||
fee = tx.vout[i].nValue.GetAmount();
|
||||
if (fee == 0 || !MoneyRange(fee))
|
||||
return false;
|
||||
totalFee[tx.vout[i].nAsset.GetAsset()] += fee;
|
||||
}
|
||||
}
|
||||
return MoneyRange(totalFee);
|
||||
}
|
||||
|
||||
CAmountMap GetFeeMap(const CTransaction& tx) {
|
||||
CAmountMap fee;
|
||||
for (const CTxOut& txout : tx.vout) {
|
||||
if (txout.IsFee()) {
|
||||
fee[txout.nAsset.GetAsset()] += txout.nValue.GetAmount();
|
||||
}
|
||||
}
|
||||
return fee;
|
||||
}
|
||||
|
||||
bool CRangeCheck::operator()() {
|
||||
if (val->IsExplicit()) {
|
||||
return true;
|
||||
|
|
@ -105,3 +129,297 @@ static bool VerifyIssuanceAmount(secp256k1_pedersen_commitment& value_commit, se
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyAmounts(const std::vector<CTxOut>& inputs, const CTransaction& tx, std::vector<CCheck*>* checks, const bool store_result) {
|
||||
assert(!tx.IsCoinBase());
|
||||
assert(inputs.size() == tx.vin.size());
|
||||
|
||||
std::vector<secp256k1_pedersen_commitment> vData;
|
||||
std::vector<secp256k1_pedersen_commitment *> vpCommitsIn, vpCommitsOut;
|
||||
|
||||
vData.reserve((tx.vin.size() + tx.vout.size() + GetNumIssuances(tx)));
|
||||
secp256k1_pedersen_commitment *p = vData.data();
|
||||
secp256k1_pedersen_commitment commit;
|
||||
secp256k1_generator gen;
|
||||
// This is used to add in the explicit values
|
||||
unsigned char explicit_blinds[32] = {0};
|
||||
int ret;
|
||||
|
||||
uint256 wtxid(tx.GetWitnessHash());
|
||||
|
||||
// This list is used to verify surjection proofs.
|
||||
// Proofs must be constructed with the list being in
|
||||
// order of input and non-null issuance pseudo-inputs, with
|
||||
// input first, asset issuance second, reissuance token third.
|
||||
std::vector<secp256k1_generator> target_generators;
|
||||
target_generators.reserve(tx.vin.size() + GetNumIssuances(tx));
|
||||
|
||||
// Tally up value commitments, check balance
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i) {
|
||||
const CConfidentialValue& val = inputs[i].nValue;
|
||||
const CConfidentialAsset& asset = inputs[i].nAsset;
|
||||
|
||||
if (val.IsNull() || asset.IsNull())
|
||||
return false;
|
||||
|
||||
if (asset.IsExplicit()) {
|
||||
ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
}
|
||||
else if (asset.IsCommitment()) {
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
target_generators.push_back(gen);
|
||||
|
||||
if (val.IsExplicit()) {
|
||||
if (!MoneyRange(val.GetAmount()))
|
||||
return false;
|
||||
|
||||
// Fails if val.GetAmount() == 0
|
||||
if (secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &commit, explicit_blinds, val.GetAmount(), &gen) != 1)
|
||||
return false;
|
||||
} else if (val.IsCommitment()) {
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
|
||||
// Each transaction input may have up to two "pseudo-inputs" to add to the LHS
|
||||
// for (re)issuance and may require up to two rangeproof checks:
|
||||
// blinded value of the new assets being made
|
||||
// blinded value of the issuance tokens being made (only for initial issuance)
|
||||
const CAssetIssuance& issuance = tx.vin[i].assetIssuance;
|
||||
|
||||
// No issuances to process, continue to next input
|
||||
if (issuance.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CAsset assetID;
|
||||
CAsset assetTokenID;
|
||||
|
||||
// First construct the assets of the issuances and reissuance token
|
||||
// These are calculated differently depending on if initial issuance or followup
|
||||
|
||||
// New issuance, compute the asset ids
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, tx.vin[i].prevout, issuance.assetEntropy);
|
||||
CalculateAsset(assetID, entropy);
|
||||
// Null nAmount is considered explicit 0, so just check for commitment
|
||||
CalculateReissuanceToken(assetTokenID, entropy, issuance.nAmount.IsCommitment());
|
||||
} else {
|
||||
// Re-issuance
|
||||
// hashAssetIdentifier doubles as the entropy on reissuance
|
||||
CalculateAsset(assetID, issuance.assetEntropy);
|
||||
CalculateReissuanceToken(assetTokenID, issuance.assetEntropy, issuance.nAmount.IsCommitment());
|
||||
|
||||
// Must check that prevout is the blinded issuance token
|
||||
// prevout's asset tag = assetTokenID + assetBlindingNonce
|
||||
if (secp256k1_generator_generate_blinded(secp256k1_ctx_verify_amounts, &gen, assetTokenID.begin(), issuance.assetBlindingNonce.begin()) != 1) {
|
||||
return false;
|
||||
}
|
||||
// Serialize the generator for direct comparison
|
||||
unsigned char derived_generator[33];
|
||||
secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, derived_generator, &gen);
|
||||
|
||||
// Belt-and-suspenders: Check that asset commitment from issuance input is correct size
|
||||
if (asset.vchCommitment.size() != sizeof(derived_generator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have already checked the outputs' generator commitment for general validity, so directly compare serialized bytes
|
||||
if (memcmp(asset.vchCommitment.data(), derived_generator, sizeof(derived_generator))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Process issuance of asset
|
||||
|
||||
if (!issuance.nAmount.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!issuance.nAmount.IsNull()) {
|
||||
// Note: This check disallows issuances in transactions with *no* witness data.
|
||||
// This can be relaxed in a future update as a HF by passing in an empty rangeproof
|
||||
// to `VerifyIssuanceAmount` instead.
|
||||
if (i >= tx.witness.vtxinwit.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!VerifyIssuanceAmount(commit, gen, assetID, issuance.nAmount, tx.witness.vtxinwit[i].vchIssuanceAmountRangeproof, checks, store_result)) {
|
||||
return false;
|
||||
}
|
||||
target_generators.push_back(gen);
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Process issuance of reissuance tokens
|
||||
|
||||
if (!issuance.nInflationKeys.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!issuance.nInflationKeys.IsNull()) {
|
||||
// Only initial issuance can have reissuance tokens
|
||||
if (!issuance.assetBlindingNonce.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: This check disallows issuances in transactions with *no* witness data.
|
||||
// This can be relaxed in a future update as a HF by passing in an empty rangeproof
|
||||
// to `VerifyIssuanceAmount` instead.
|
||||
if (i >= tx.witness.vtxinwit.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!VerifyIssuanceAmount(commit, gen, assetTokenID, issuance.nInflationKeys, tx.witness.vtxinwit[i].vchInflationKeysRangeproof, checks, store_result)) {
|
||||
return false;
|
||||
}
|
||||
target_generators.push_back(gen);
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
const CConfidentialValue& val = tx.vout[i].nValue;
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
if (!asset.IsValid())
|
||||
return false;
|
||||
if (!val.IsValid())
|
||||
return false;
|
||||
if (!tx.vout[i].nNonce.IsValid())
|
||||
return false;
|
||||
|
||||
if (asset.IsExplicit()) {
|
||||
ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
}
|
||||
else if (asset.IsCommitment()) {
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (val.IsExplicit()) {
|
||||
if (!MoneyRange(val.GetAmount()))
|
||||
return false;
|
||||
|
||||
if (val.GetAmount() == 0) {
|
||||
if (tx.vout[i].scriptPubKey.IsUnspendable()) {
|
||||
continue;
|
||||
} else {
|
||||
// No spendable 0-value outputs
|
||||
// Reason: A spendable output of 0 reissuance tokens would allow reissuance without reissuance tokens.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &commit, explicit_blinds, val.GetAmount(), &gen);
|
||||
// The explicit_blinds are all 0, and the amount is not 0. So secp256k1_pedersen_commit does not fail.
|
||||
assert(ret == 1);
|
||||
}
|
||||
else if (val.IsCommitment()) {
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
vData.push_back(commit);
|
||||
vpCommitsOut.push_back(p);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Check balance
|
||||
if (QueueCheck(checks, new CBalanceCheck(vData, vpCommitsIn, vpCommitsOut)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Range proofs
|
||||
for (size_t i = 0; i < tx.vout.size(); i++) {
|
||||
const CConfidentialValue& val = tx.vout[i].nValue;
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
std::vector<unsigned char> vchAssetCommitment = asset.vchCommitment;
|
||||
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
|
||||
if (val.IsExplicit())
|
||||
{
|
||||
if (ptxoutwit && !ptxoutwit->vchRangeproof.empty())
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (asset.IsExplicit()) {
|
||||
int ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, &vchAssetCommitment[0], &gen);
|
||||
}
|
||||
if (!ptxoutwit) {
|
||||
return false;
|
||||
}
|
||||
if (QueueCheck(checks, new CRangeCheck(&val, ptxoutwit->vchRangeproof, vchAssetCommitment, tx.vout[i].scriptPubKey, store_result)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Surjection proofs
|
||||
for (size_t i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
|
||||
// No need for surjection proof
|
||||
if (asset.IsExplicit()) {
|
||||
if (ptxoutwit && !ptxoutwit->vchSurjectionproof.empty()) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!ptxoutwit)
|
||||
return false;
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
secp256k1_surjectionproof proof;
|
||||
if (secp256k1_surjectionproof_parse(secp256k1_ctx_verify_amounts, &proof, &ptxoutwit->vchSurjectionproof[0], ptxoutwit->vchSurjectionproof.size()) != 1)
|
||||
return false;
|
||||
|
||||
if (QueueCheck(checks, new CSurjectionCheck(proof, target_generators, gen, wtxid, store_result)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees) {
|
||||
assert(tx.IsCoinBase());
|
||||
CAmountMap remaining = mapFees;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const CTxOut& out = tx.vout[i];
|
||||
if (!out.nValue.IsExplicit() || !out.nAsset.IsExplicit()) {
|
||||
return false;
|
||||
}
|
||||
if (!MoneyRange(out.nValue.GetAmount())) {
|
||||
return false;
|
||||
}
|
||||
if (g_con_elementsmode &&
|
||||
out.nValue.GetAmount() == 0 && !out.scriptPubKey.IsUnspendable()) {
|
||||
return false;
|
||||
}
|
||||
remaining[out.nAsset.GetAsset()] -= out.nValue.GetAmount();
|
||||
}
|
||||
return MoneyRange(remaining);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@
|
|||
#include <uint256.h>
|
||||
|
||||
|
||||
// Check if explicit TX fees overflow or are negative
|
||||
bool HasValidFee(const CTransaction& tx);
|
||||
|
||||
// Compute the fee from the explicit fee outputs. Must call HasValidFee first
|
||||
CAmountMap GetFeeMap(const CTransaction& tx);
|
||||
|
||||
/**
|
||||
* ELEMENTS:
|
||||
* Closure representing one verification, either script or range checks.
|
||||
|
|
@ -83,5 +89,9 @@ public:
|
|||
|
||||
ScriptError QueueCheck(std::vector<CCheck*>* queue, CCheck* check);
|
||||
|
||||
bool VerifyAmounts(const std::vector<CTxOut>& inputs, const CTransaction& tx, std::vector<CCheck*>* pvChecks, const bool cacheStore);
|
||||
|
||||
bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees);
|
||||
|
||||
|
||||
#endif // BITCOIN_CONFIDENTIAL_VALIDATION_H
|
||||
|
|
|
|||
|
|
@ -190,15 +190,19 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
// Check for negative or overflow output values
|
||||
CAmount nValueOut = 0;
|
||||
CAmount nValueOutExplicit = 0;
|
||||
for (const auto& txout : tx.vout)
|
||||
{
|
||||
if (txout.nValue < 0)
|
||||
if (!txout.nValue.IsValid())
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-amount-invalid");
|
||||
if (!txout.nValue.IsExplicit())
|
||||
continue;
|
||||
if (txout.nValue.GetAmount() < 0)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
if (txout.nValue > MAX_MONEY)
|
||||
if (txout.nValue.GetAmount() > MAX_MONEY)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
nValueOut += txout.nValue;
|
||||
if (!MoneyRange(nValueOut))
|
||||
nValueOutExplicit += txout.nValue.GetAmount();
|
||||
if (!MoneyRange(nValueOutExplicit))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +220,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
{
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
||||
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
if (tx.vout[i].IsFee()) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-fee");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -228,7 +238,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
}
|
||||
|
||||
namespace Consensus {
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent)
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent, std::vector<CCheck*> *pvChecks, const bool cacheStore, bool fScriptChecks)
|
||||
{
|
||||
// are the actual inputs available?
|
||||
if (!inputs.HaveInputs(tx)) {
|
||||
|
|
@ -236,6 +246,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
strprintf("%s: inputs missing/spent", __func__));
|
||||
}
|
||||
|
||||
std::vector<CTxOut> spent_inputs;
|
||||
CAmount nValueIn = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
|
|
@ -243,7 +254,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
// Check existence and validity of pegin witness
|
||||
std::string err;
|
||||
if (tx.witness.vtxinwit.size() <= i || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, prevout, err, true)) {
|
||||
return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness");
|
||||
return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness", false, err);
|
||||
}
|
||||
std::pair<uint256, COutPoint> pegin = std::make_pair(uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]), prevout);
|
||||
if (inputs.IsPeginSpent(pegin)) {
|
||||
|
|
@ -256,11 +267,12 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
setPeginsSpent.insert(pegin);
|
||||
|
||||
// Tally the input amount.
|
||||
const CTxOut out = GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness);
|
||||
if (!MoneyRange(out.nValue)) {
|
||||
spent_inputs.push_back(GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness));
|
||||
const CTxOut& out = spent_inputs.back();
|
||||
nValueIn += out.nValue.GetAmount(); // Non-explicit already filtered by IsValidPeginWitness
|
||||
if (!MoneyRange(out.nValue.GetAmount())) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-pegin-inputvalue-outofrange");
|
||||
}
|
||||
nValueIn += out.nValue;
|
||||
} else {
|
||||
const Coin& coin = inputs.AccessCoin(prevout);
|
||||
assert(!coin.IsSpent());
|
||||
|
|
@ -271,29 +283,40 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
}
|
||||
|
||||
// Check for negative or overflow input values
|
||||
nValueIn += coin.out.nValue;
|
||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
spent_inputs.push_back(coin.out);
|
||||
if (coin.out.nValue.IsExplicit()) {
|
||||
nValueIn += coin.out.nValue.GetAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO(rebase) you need to replace these two blocks with the `VerifyAmounts` and `HasValidFee` methods
|
||||
const CAmount value_out = tx.GetValueOut();
|
||||
if (nValueIn < value_out) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
if (g_con_elementsmode) {
|
||||
// Tally transaction fees
|
||||
if (!HasValidFee(tx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
// Verify that amounts add up.
|
||||
if (fScriptChecks && !VerifyAmounts(spent_inputs, tx, pvChecks, cacheStore)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-ne-out", false, "value in != value out");
|
||||
}
|
||||
fee_map += GetFeeMap(tx);
|
||||
} else {
|
||||
const CAmount value_out = tx.GetValueOutMap()[CAsset()];
|
||||
if (nValueIn < value_out) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
}
|
||||
|
||||
// Tally transaction fees
|
||||
const CAmount txfee_aux = nValueIn - value_out;
|
||||
if (!MoneyRange(txfee_aux)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
fee_map[CAsset()] += txfee_aux;
|
||||
}
|
||||
|
||||
// Tally transaction fees
|
||||
const CAmount txfee_aux = nValueIn - value_out;
|
||||
if (!MoneyRange(txfee_aux)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
txfee = txfee_aux;
|
||||
return true;
|
||||
}
|
||||
}// namespace Consensus
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <amount.h>
|
||||
|
||||
#include <confidential_validation.h>
|
||||
#include <set>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
|
@ -27,10 +28,10 @@ namespace Consensus {
|
|||
/**
|
||||
* 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.
|
||||
* @param[out] txfee Set to the transaction fee if successful.
|
||||
* @param[out] fee_map Set to the transaction fee if successful.
|
||||
* Preconditions: tx.IsCoinBase() is false.
|
||||
*/
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent);
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent, std::vector<CCheck*> *pvChecks, const bool cacheStore, bool fScriptChecks);
|
||||
} // namespace Consensus
|
||||
|
||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
||||
|
|
|
|||
|
|
@ -108,11 +108,14 @@ static inline int64_t GetTransactionInputWeight(const CTransaction& tx, const si
|
|||
{
|
||||
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
|
||||
assert(tx.witness.vtxinwit.size() > nIn);
|
||||
//TODO(rebase) only count CA/CT witnesses when g_con_elementsmode is true
|
||||
// ELEMENTS: This is only used for change size calculation in wallet, assert if
|
||||
// anything is unexpected for this call e.g. issuances, rangeproofs
|
||||
assert(tx.witness.vtxinwit[nIn].vchIssuanceAmountRangeproof.empty());
|
||||
assert(tx.witness.vtxinwit[nIn].vchInflationKeysRangeproof.empty());
|
||||
|
||||
return ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1)
|
||||
+ ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION)
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].scriptWitness.stack, PROTOCOL_VERSION)
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].m_pegin_witness.stack, PROTOCOL_VERSION);
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].scriptWitness.stack, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|
||||
|
|
|
|||
|
|
@ -1279,6 +1279,9 @@ bool AppInitMain()
|
|||
|
||||
InitSignatureCache();
|
||||
InitScriptExecutionCache();
|
||||
InitRangeproofCache();
|
||||
InitSurjectionproofCache();
|
||||
|
||||
|
||||
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
|
||||
if (nScriptCheckThreads) {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ static bool verify_flags(unsigned int flags)
|
|||
return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
|
||||
}
|
||||
|
||||
static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount,
|
||||
static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CConfidentialValue amount,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
|
||||
{
|
||||
|
|
@ -102,12 +102,20 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
|
|||
}
|
||||
}
|
||||
|
||||
int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
|
||||
int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
||||
const unsigned char *amount, unsigned int amountLen,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
|
||||
{
|
||||
CAmount am(amount);
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
try {
|
||||
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, amount, amountLen);
|
||||
CConfidentialValue am;
|
||||
stream >> am;
|
||||
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
} catch (const std::exception&) {
|
||||
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -119,7 +127,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i
|
|||
return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
|
||||
}
|
||||
|
||||
CAmount am(0);
|
||||
CConfidentialValue am(0);
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubK
|
|||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
|
||||
|
||||
EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
|
||||
EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
||||
const unsigned char *amount, unsigned int amountLen,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
|
||||
|
||||
|
|
|
|||
|
|
@ -1519,6 +1519,11 @@ public:
|
|||
::Serialize(s, (int)0);
|
||||
else
|
||||
::Serialize(s, txTo.vin[nInput].nSequence);
|
||||
// Serialize the asset issuance object
|
||||
if (!txTo.vin[nInput].assetIssuance.IsNull()) {
|
||||
assert(g_con_elementsmode);
|
||||
::Serialize(s, txTo.vin[nInput].assetIssuance);
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize an output of txTo */
|
||||
|
|
@ -1571,6 +1576,19 @@ uint256 GetSequenceHash(const T& txTo)
|
|||
return ss.GetHash();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint256 GetIssuanceHash(const T& txTo)
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
for (const auto& txin : txTo.vin) {
|
||||
if (txin.assetIssuance.IsNull())
|
||||
ss << (unsigned char)0;
|
||||
else
|
||||
ss << txin.assetIssuance;
|
||||
}
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint256 GetOutputsHash(const T& txTo)
|
||||
{
|
||||
|
|
@ -1590,6 +1608,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
|
|||
if (txTo.HasWitness()) {
|
||||
hashPrevouts = GetPrevoutHash(txTo);
|
||||
hashSequence = GetSequenceHash(txTo);
|
||||
hashIssuance = GetIssuanceHash(txTo);
|
||||
hashOutputs = GetOutputsHash(txTo);
|
||||
ready = true;
|
||||
}
|
||||
|
|
@ -1600,13 +1619,14 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
|
|||
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
|
||||
if (sigversion == SigVersion::WITNESS_V0) {
|
||||
uint256 hashPrevouts;
|
||||
uint256 hashSequence;
|
||||
uint256 hashIssuance;
|
||||
uint256 hashOutputs;
|
||||
const bool cacheready = cache && cache->ready;
|
||||
|
||||
|
|
@ -1618,6 +1638,9 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
|
||||
}
|
||||
|
||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||
hashIssuance = cacheready ? cache->hashIssuance : GetIssuanceHash(txTo);
|
||||
}
|
||||
|
||||
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
||||
hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
|
||||
|
|
@ -1633,13 +1656,24 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
// Input prevouts/nSequence (none/all, depending on flags)
|
||||
ss << hashPrevouts;
|
||||
ss << hashSequence;
|
||||
if (g_con_elementsmode) {
|
||||
ss << hashIssuance;
|
||||
}
|
||||
// The input being signed (replacing the scriptSig with scriptCode + amount)
|
||||
// The prevout may already be contained in hashPrevout, and the nSequence
|
||||
// may already be contain in hashSequence.
|
||||
ss << txTo.vin[nIn].prevout;
|
||||
ss << scriptCode;
|
||||
ss << amount;
|
||||
if (g_con_elementsmode) {
|
||||
ss << amount;
|
||||
} else {
|
||||
ss << amount.GetAmount();
|
||||
}
|
||||
ss << txTo.vin[nIn].nSequence;
|
||||
if (!txTo.vin[nIn].assetIssuance.IsNull()) {
|
||||
assert(g_con_elementsmode);
|
||||
ss << txTo.vin[nIn].assetIssuance;
|
||||
}
|
||||
// Outputs (none/one/all, depending on flags)
|
||||
ss << hashOutputs;
|
||||
// Locktime
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
|||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs;
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance;
|
||||
bool ready = false;
|
||||
|
||||
template <class T>
|
||||
|
|
@ -143,7 +143,7 @@ static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
|
|||
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
|
||||
class BaseSignatureChecker
|
||||
{
|
||||
|
|
@ -172,15 +172,15 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
|
|||
private:
|
||||
const T* txTo;
|
||||
unsigned int nIn;
|
||||
const CAmount amount;
|
||||
const CConfidentialValue amount;
|
||||
const PrecomputedTransactionData* txdata;
|
||||
|
||||
protected:
|
||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
|
||||
public:
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
|
|||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
case TX_WITNESS_UNKNOWN:
|
||||
case TX_FEE:
|
||||
break;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
#include <uint256.h>
|
||||
#include <vector>
|
||||
|
||||
// IsUnspendable() compatibility
|
||||
extern bool g_con_elementsmode;
|
||||
|
||||
// Maximum number of bytes pushable to the stack
|
||||
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
|
||||
|
||||
|
|
@ -570,7 +573,8 @@ public:
|
|||
*/
|
||||
bool IsUnspendable() const
|
||||
{
|
||||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
|
||||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE) ||
|
||||
(g_con_elementsmode && size() == 0 /* Elements rule for fee outputs */);
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
|
|
|||
|
|
@ -68,7 +68,11 @@ typedef enum ScriptError_t
|
|||
SCRIPT_ERR_OP_CODESEPARATOR,
|
||||
SCRIPT_ERR_SIG_FINDANDDELETE,
|
||||
|
||||
SCRIPT_ERR_ERROR_COUNT
|
||||
SCRIPT_ERR_ERROR_COUNT,
|
||||
|
||||
// ELEMENTS:
|
||||
SCRIPT_ERR_RANGEPROOF,
|
||||
SCRIPT_ERR_PEDERSEN_TALLY
|
||||
} ScriptError;
|
||||
|
||||
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
|
||||
|
|
|
|||
|
|
@ -41,6 +41,14 @@ public:
|
|||
CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
void ComputeEntry(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256().Write(nonce.begin(), nonce.size()).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
void ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256().Write(nonce.begin(), nonce.size()).Write(hash.begin(), 32).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
bool
|
||||
Get(const uint256& entry, const bool erase)
|
||||
{
|
||||
|
|
@ -66,6 +74,11 @@ public:
|
|||
* signatureCache could be made local to VerifySignature.
|
||||
*/
|
||||
static CSignatureCache signatureCache;
|
||||
|
||||
// ELEMENTS:
|
||||
static CSignatureCache rangeProofCache;
|
||||
static CSignatureCache surjectionProofCache;
|
||||
|
||||
} // namespace
|
||||
|
||||
// To be called once in AppInitMain/BasicTestingSetup to initialize the
|
||||
|
|
@ -92,3 +105,102 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsig
|
|||
signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS CACHES
|
||||
|
||||
// To be called once in AppInit2/TestingSetup to initialize the rangeproof cache
|
||||
void InitRangeproofCache()
|
||||
{
|
||||
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
|
||||
// setup_bytes creates the minimum possible cache (2 elements).
|
||||
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
|
||||
size_t nElems = rangeProofCache.setup_bytes(nMaxCacheSize);
|
||||
LogPrintf("Using %zu MiB out of %zu requested for rangeproof cache, able to store %zu elements\n",
|
||||
(nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
|
||||
}
|
||||
|
||||
// To be called once in AppInit2/TestingSetup to initialize the surjectionrproof cache
|
||||
void InitSurjectionproofCache()
|
||||
{
|
||||
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
|
||||
// setup_bytes creates the minimum possible cache (2 elements).
|
||||
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
|
||||
size_t nElems = surjectionProofCache.setup_bytes(nMaxCacheSize);
|
||||
LogPrintf("Using %zu MiB out of %zu requested for surjectionproof cache, able to store %zu elements\n",
|
||||
(nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
|
||||
}
|
||||
|
||||
bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* secp256k1_ctx_verify_amounts) const
|
||||
{
|
||||
uint256 entry;
|
||||
rangeProofCache.ComputeEntry(entry, vchRangeProof, vchValueCommitment);
|
||||
|
||||
if (rangeProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vchRangeProof.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t min_value, max_value;
|
||||
secp256k1_pedersen_commitment commit;
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &vchValueCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
secp256k1_generator tag;
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &tag, &vchAssetCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &commit, vchRangeProof.data(), vchRangeProof.size(), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// An rangeproof is not valid if the output is spendable but the minimum number
|
||||
// is 0. This is to prevent people passing 0-value tokens around, or conjuring
|
||||
// reissuance tokens from nothing then attempting to reissue an asset.
|
||||
// ie reissuance doesn't require revealing value of reissuance output
|
||||
// Issuances proofs are always "unspendable" as they commit to an empty script.
|
||||
if (min_value == 0 && !scriptPubKey.IsUnspendable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (store) {
|
||||
rangeProofCache.Set(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector<secp256k1_generator>& vTags, secp256k1_generator& gen, const secp256k1_context* secp256k1_ctx_verify_amounts, const uint256& wtxid) const
|
||||
{
|
||||
|
||||
// Serialize proof
|
||||
std::vector<unsigned char> vchproof;
|
||||
size_t proof_len = secp256k1_surjectionproof_serialized_size(secp256k1_ctx_verify_amounts, &proof);
|
||||
vchproof.resize(proof_len);
|
||||
assert(secp256k1_surjectionproof_serialize(secp256k1_ctx_verify_amounts, vchproof.data(), &proof_len, &proof) == 1);
|
||||
|
||||
// wtxid commits to all data including surj targets
|
||||
// we need to specify the proof and output asset point to be unique
|
||||
uint256 entry;
|
||||
surjectionProofCache.ComputeEntry(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
|
||||
if (surjectionProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (secp256k1_surjectionproof_verify(secp256k1_ctx_verify_amounts, &proof, vTags.data(), vTags.size(), &gen) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (store) {
|
||||
surjectionProofCache.Set(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// END ELEMENTS
|
||||
//
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include <script/interpreter.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
#include <secp256k1_surjectionproof.h>
|
||||
#include <vector>
|
||||
|
||||
// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit
|
||||
|
|
@ -46,11 +49,46 @@ private:
|
|||
bool store;
|
||||
|
||||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
};
|
||||
|
||||
void InitSignatureCache();
|
||||
|
||||
//
|
||||
// ELEMENTS:
|
||||
|
||||
class CachingRangeProofChecker
|
||||
{
|
||||
private:
|
||||
bool store;
|
||||
public:
|
||||
CachingRangeProofChecker(bool storeIn){
|
||||
store = storeIn;
|
||||
};
|
||||
|
||||
bool VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* ctx) const;
|
||||
|
||||
};
|
||||
|
||||
class CachingSurjectionProofChecker
|
||||
{
|
||||
private:
|
||||
bool store;
|
||||
public:
|
||||
CachingSurjectionProofChecker(bool storeIn){
|
||||
store = storeIn;
|
||||
};
|
||||
|
||||
bool VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector<secp256k1_generator>& vTags, secp256k1_generator& gen, const secp256k1_context* ctx, const uint256& wtxid) const;
|
||||
|
||||
};
|
||||
|
||||
void InitRangeproofCache();
|
||||
void InitSurjectionproofCache();
|
||||
|
||||
// END ELEMENTS
|
||||
//
|
||||
|
||||
#endif // BITCOIN_SCRIPT_SIGCACHE_H
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
||||
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
|
||||
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
|
||||
{
|
||||
|
|
@ -419,7 +419,7 @@ void SignatureData::MergeSignatureData(SignatureData sigdata)
|
|||
signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
|
||||
}
|
||||
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CConfidentialValue& amount, int nHashType)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
txTo.witness.vtxinwit.resize(txTo.vin.size());
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
|
|||
const CMutableTransaction* txTo;
|
||||
unsigned int nIn;
|
||||
int nHashType;
|
||||
CAmount amount;
|
||||
CConfidentialValue amount;
|
||||
const MutableTransactionSignatureChecker checker;
|
||||
|
||||
public:
|
||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
||||
const BaseSignatureChecker& Checker() const override { return checker; }
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
};
|
||||
|
|
@ -703,7 +703,7 @@ struct PartiallySignedTransaction
|
|||
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata, unsigned int additional_flags=0);
|
||||
|
||||
/** Produce a script signature for a transaction. */
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CConfidentialValue& amount, int nHashType);
|
||||
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
||||
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ const char* GetTxnOutputType(txnouttype t)
|
|||
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TX_WITNESS_UNKNOWN: return "witness_unknown";
|
||||
case TX_TRUE: return "true";
|
||||
case TX_FEE: return "fee";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -113,6 +114,11 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
|
|||
return TX_TRUE;
|
||||
}
|
||||
|
||||
// Fee outputs are for elements-style transactions only
|
||||
if (g_con_elementsmode && scriptPubKey == CScript()) {
|
||||
return TX_FEE;
|
||||
}
|
||||
|
||||
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
|
||||
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
|
||||
if (scriptPubKey.IsPayToScriptHash())
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ enum txnouttype
|
|||
TX_WITNESS_V0_KEYHASH,
|
||||
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||
TX_TRUE, // For testing purposes only
|
||||
// ELEMENTS:
|
||||
TX_FEE,
|
||||
};
|
||||
|
||||
class CNoDestination {
|
||||
|
|
|
|||
|
|
@ -404,6 +404,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
|
|||
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
||||
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
||||
|
||||
// ELEMENTS:
|
||||
typedef std::pair<uint256, COutPoint> PeginPair;
|
||||
for(const PeginPair& it : entry.setPeginsSpent) {
|
||||
std::pair<std::map<std::pair<uint256, COutPoint>, uint256>::iterator, bool> ret = mapPeginsSpentToTxid.insert(std::make_pair(it, tx.GetHash()));
|
||||
|
|
@ -651,9 +652,9 @@ static void CheckInputsAndUpdateCoins(const CTxMemPoolEntry& entry, CCoinsViewCa
|
|||
{
|
||||
CTransaction tx = entry.GetTx();
|
||||
CValidationState state;
|
||||
CAmount txfee = 0;
|
||||
CAmountMap fee_map;
|
||||
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
|
||||
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee, setPeginsSpent);
|
||||
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, fee_map, setPeginsSpent, NULL, false, true);
|
||||
assert(fCheckResult);
|
||||
UpdateCoins(tx, mempoolDuplicate, 1000000);
|
||||
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
|
|||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs,
|
||||
bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore,
|
||||
PrecomputedTransactionData& txdata,
|
||||
std::vector<CScriptCheck> *pvChecks = nullptr);
|
||||
std::vector<CCheck*> *pvChecks = nullptr);
|
||||
static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
|
||||
|
||||
bool CheckFinalTx(const CTransaction &tx, int flags)
|
||||
|
|
@ -709,8 +709,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
|||
if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||
|
||||
CAmount nFees = 0;
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees, setPeginsSpent)) {
|
||||
CAmountMap fee_map;
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), fee_map, setPeginsSpent, NULL, true, true)) {
|
||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
||||
}
|
||||
|
||||
|
|
@ -724,6 +724,9 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
|||
|
||||
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||
|
||||
// We only consider policyAsset
|
||||
CAmount nFees = fee_map[policyAsset];
|
||||
|
||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction
|
||||
CAmount nModifiedFees = nFees;
|
||||
pool.ApplyDelta(hash, nModifiedFees);
|
||||
|
|
@ -1412,7 +1415,7 @@ void InitScriptExecutionCache() {
|
|||
*
|
||||
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
|
||||
*/
|
||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CCheck*> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
|
|
@ -1465,11 +1468,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||
// spent being checked as a part of CScriptCheck.
|
||||
|
||||
// Verify signature
|
||||
CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
|
||||
if (pvChecks) {
|
||||
pvChecks->push_back(CScriptCheck());
|
||||
check.swap(pvChecks->back());
|
||||
} else if (!check()) {
|
||||
CCheck* check = new CScriptCheck(coin.out, tx, i, flags, cacheSigStore, &txdata);
|
||||
ScriptError serror = QueueCheck(pvChecks, check);
|
||||
if (serror != SCRIPT_ERR_OK) {
|
||||
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
|
||||
// Check whether the failure was caused by a
|
||||
// non-mandatory script verification check, such as
|
||||
|
|
@ -1480,7 +1481,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||
CScriptCheck check2(coin.out, tx, i,
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
||||
if (check2()) {
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(serror)));
|
||||
}
|
||||
}
|
||||
// Failures of other flags indicate a transaction that is
|
||||
|
|
@ -1490,7 +1491,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||
// as to the correct behavior - we may want to continue
|
||||
// peering with non-upgraded nodes even after soft-fork
|
||||
// super-majority signaling has occurred.
|
||||
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(serror)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1608,7 +1609,13 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out, cons
|
|||
undo.nHeight = alternate.nHeight;
|
||||
undo.fCoinBase = alternate.fCoinBase;
|
||||
} else {
|
||||
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
||||
// ELEMENTS:
|
||||
// If we're connecting genesis outputs, it's probably actually just
|
||||
// a genesis output, let it through. N.B. The case where it's a corrupted
|
||||
// txundo from per-tx db will not be caught!
|
||||
if (!Params().GetConsensus().connect_genesis_outputs) {
|
||||
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
// The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
|
||||
|
|
@ -1634,6 +1641,15 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out, cons
|
|||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||
}
|
||||
|
||||
// We don't want to compare things that are not stored in utxo db, specifically
|
||||
// the nonce commitment which has no consensus meaning for spending conditions
|
||||
static bool TxOutDBEntryIsSame(const CTxOut& block_txout, const CTxOut& txdb_txout)
|
||||
{
|
||||
return txdb_txout.nValue == block_txout.nValue &&
|
||||
txdb_txout.nAsset == block_txout.nAsset &&
|
||||
txdb_txout.scriptPubKey == block_txout.scriptPubKey;
|
||||
}
|
||||
|
||||
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
|
||||
* When FAILED is returned, view is left in an indeterminate state. */
|
||||
DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
|
||||
|
|
@ -1664,7 +1680,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
|
|||
COutPoint out(hash, o);
|
||||
Coin coin;
|
||||
bool is_spent = view.SpendCoin(out, &coin);
|
||||
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
|
||||
if (!is_spent || !TxOutDBEntryIsSame(tx.vout[o], coin.out) || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
|
||||
fClean = false; // transaction output mismatch
|
||||
}
|
||||
}
|
||||
|
|
@ -1743,7 +1759,7 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
|
|||
return true;
|
||||
}
|
||||
|
||||
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
||||
static CCheckQueue<CCheck> scriptcheckqueue(128);
|
||||
|
||||
void ThreadScriptCheck() {
|
||||
RenameThread("bitcoin-scriptch");
|
||||
|
|
@ -1920,7 +1936,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
const CScript& mandatory_coinbase_destination = chainparams.GetConsensus().mandatory_coinbase_destination;
|
||||
if (mandatory_coinbase_destination != CScript()) {
|
||||
for (auto& txout : block.vtx[0]->vout) {
|
||||
if (txout.scriptPubKey != mandatory_coinbase_destination && txout.nValue != 0) {
|
||||
bool mustPay = !txout.nValue.IsExplicit() || txout.nValue.GetAmount() != 0;
|
||||
if (mustPay && txout.scriptPubKey != mandatory_coinbase_destination) {
|
||||
return state.DoS(100, error("ConnectBlock(): Coinbase outputs didn't match required scriptPubKey"),
|
||||
REJECT_INVALID, "bad-coinbase-txos");
|
||||
}
|
||||
|
|
@ -2061,10 +2078,10 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
|
||||
CBlockUndo blockundo;
|
||||
|
||||
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr);
|
||||
CCheckQueueControl<CCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr);
|
||||
|
||||
std::vector<int> prevheights;
|
||||
CAmount nFees = 0;
|
||||
CAmountMap fee_map;
|
||||
int nInputs = 0;
|
||||
int64_t nSigOpsCost = 0;
|
||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||
|
|
@ -2083,16 +2100,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
CAmount txfee = 0;
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee,
|
||||
setPeginsSpent == NULL ? setPeginsSpentDummy : *setPeginsSpent)) {
|
||||
std::vector<CCheck*> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, fee_map,
|
||||
setPeginsSpent == NULL ? setPeginsSpentDummy : *setPeginsSpent,
|
||||
nScriptCheckThreads ? &vChecks : NULL, fCacheResults, fScriptChecks)) {
|
||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
||||
}
|
||||
nFees += txfee;
|
||||
if (!MoneyRange(nFees)) {
|
||||
return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__),
|
||||
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
|
||||
}
|
||||
control.Add(vChecks);
|
||||
|
||||
if (!MoneyRange(fee_map))
|
||||
return state.DoS(100, error("ConnectBlock(): total block reward overflowed"), REJECT_INVALID, "bad-blockreward-outofrange");
|
||||
|
||||
// Check that transaction is BIP68 final
|
||||
// BIP68 lock checks (as opposed to nLockTime checks) must
|
||||
|
|
@ -2124,7 +2142,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
txdata.emplace_back(tx);
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
std::vector<CCheck*> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr))
|
||||
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
||||
|
|
@ -2137,16 +2155,19 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
blockundo.vtxundo.push_back(CTxUndo());
|
||||
}
|
||||
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
||||
|
||||
}
|
||||
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
|
||||
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
|
||||
|
||||
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
|
||||
if (block.vtx[0]->GetValueOut() > blockReward)
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
|
||||
block.vtx[0]->GetValueOut(), blockReward),
|
||||
REJECT_INVALID, "bad-cb-amount");
|
||||
CAmountMap block_reward = fee_map;
|
||||
block_reward[consensusParams.subsidy_asset] += GetBlockSubsidy(pindex->nHeight, consensusParams);
|
||||
if (!MoneyRange(block_reward))
|
||||
return state.DoS(100, error("ConnectBlock(): total block reward overflowed"), REJECT_INVALID, "bad-blockreward-outofrange");
|
||||
if (!VerifyCoinbaseAmount(*(block.vtx[0]), block_reward)) {
|
||||
return state.DoS(100, error("ConnectBlock(): coinbase pays too much (limit=%d)",
|
||||
block_reward[consensusParams.subsidy_asset]), REJECT_INVALID, "bad-cb-amount");
|
||||
}
|
||||
|
||||
if (!control.Wait())
|
||||
return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
|
||||
|
|
@ -3332,10 +3353,21 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
|
|||
std::vector<unsigned char> ret(32, 0x00);
|
||||
if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
|
||||
if (commitpos == -1) {
|
||||
// ELEMENTS: Shim in blank coinbase output for witness output hash
|
||||
// Previous iterations of CA could have allowed witness data
|
||||
// in coinbase transactions, and this witness data must be committed
|
||||
// to here.
|
||||
//
|
||||
// Is No-op in Bitcoin
|
||||
CMutableTransaction tx0(*block.vtx[0]);
|
||||
tx0.vout.push_back(CTxOut());
|
||||
block.vtx[0] = MakeTransactionRef(std::move(tx0));
|
||||
// END
|
||||
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
|
||||
CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
|
||||
CTxOut out;
|
||||
out.nValue = 0;
|
||||
out.nAsset = policyAsset;
|
||||
out.scriptPubKey.resize(38);
|
||||
out.scriptPubKey[0] = OP_RETURN;
|
||||
out.scriptPubKey[1] = 0x24;
|
||||
|
|
@ -3346,7 +3378,9 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
|
|||
memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
|
||||
commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
|
||||
CMutableTransaction tx(*block.vtx[0]);
|
||||
tx.vout.push_back(out);
|
||||
// Elements: replace shimmed output with real coinbase rather than push
|
||||
tx.vout.back() = out;
|
||||
// END
|
||||
block.vtx[0] = MakeTransactionRef(std::move(tx));
|
||||
}
|
||||
}
|
||||
|
|
@ -3536,8 +3570,9 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
|||
return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness reserved value size", __func__));
|
||||
}
|
||||
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->witness.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
|
||||
uint256 committedWitness(std::vector<unsigned char>(&block.vtx[0]->vout[commitpos].scriptPubKey[6], &block.vtx[0]->vout[commitpos].scriptPubKey[6+32]));
|
||||
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch: calculated: %s found commitment: %s", __func__, hashWitness.GetHex(), committedWitness.GetHex()));
|
||||
}
|
||||
fHaveWitness = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <amount.h>
|
||||
#include <coins.h>
|
||||
#include <confidential_validation.h>
|
||||
#include <fs.h>
|
||||
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
||||
#include <policy/feerate.h>
|
||||
|
|
@ -365,7 +366,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null
|
|||
* Closure representing one script verification
|
||||
* Note that this stores references to the spending transaction
|
||||
*/
|
||||
class CScriptCheck
|
||||
class CScriptCheck : public CCheck
|
||||
{
|
||||
private:
|
||||
CTxOut m_tx_out;
|
||||
|
|
@ -373,27 +374,14 @@ private:
|
|||
unsigned int nIn;
|
||||
unsigned int nFlags;
|
||||
bool cacheStore;
|
||||
ScriptError error;
|
||||
PrecomputedTransactionData *txdata;
|
||||
|
||||
public:
|
||||
CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
|
||||
CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false) {}
|
||||
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn) { }
|
||||
|
||||
bool operator()();
|
||||
|
||||
void swap(CScriptCheck &check) {
|
||||
std::swap(ptxTo, check.ptxTo);
|
||||
std::swap(m_tx_out, check.m_tx_out);
|
||||
std::swap(nIn, check.nIn);
|
||||
std::swap(nFlags, check.nFlags);
|
||||
std::swap(cacheStore, check.cacheStore);
|
||||
std::swap(error, check.error);
|
||||
std::swap(txdata, check.txdata);
|
||||
}
|
||||
|
||||
ScriptError GetScriptError() const { return error; }
|
||||
};
|
||||
|
||||
/** Initializes the script-execution cache */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue