Move amount verification to main

This commit is contained in:
instagibbs 2016-07-06 10:30:43 +02:00 committed by Gregory Sanders
parent e0b854185a
commit e877750729
6 changed files with 125 additions and 115 deletions

View file

@ -10,6 +10,7 @@
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
#include "coins.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
@ -46,6 +47,8 @@
#include <boost/filesystem/fstream.hpp>
#include <boost/math/distributions/poisson.hpp>
#include <boost/thread.hpp>
#include <secp256k1.h>
#include <secp256k1_rangeproof.h>
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
@ -542,6 +545,106 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
return true;
}
//static Secp256k1Ctx init_context_on_load;
//extern secp256k1_context* secp256k1_bitcoin_verify_context;
static secp256k1_context* secp256k1_ctx_verify_amounts = NULL;
class Secp256k1Ctx
{
public:
Secp256k1Ctx() {
assert(secp256k1_ctx_verify_amounts == NULL);
secp256k1_ctx_verify_amounts = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_pedersen_context_initialize(secp256k1_ctx_verify_amounts);
secp256k1_rangeproof_context_initialize(secp256k1_ctx_verify_amounts);
assert(secp256k1_ctx_verify_amounts != NULL);
}
~Secp256k1Ctx() {
assert(secp256k1_ctx_verify_amounts != NULL);
secp256k1_context_destroy(secp256k1_ctx_verify_amounts);
secp256k1_ctx_verify_amounts = NULL;
}
};
bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess)
{
CAmount nPlainAmount = excess;
std::vector<unsigned char> vchData;
std::vector<unsigned char *> vpchCommitsIn, vpchCommitsOut;
bool fNullRangeproof = false;
vchData.resize(CTxOutValue::nCommitmentSize * (tx.vin.size() + tx.vout.size()));
unsigned char *p = vchData.data();
if (!tx.IsCoinBase())
{
for (size_t i = 0; i < tx.vin.size(); ++i)
{
const CTxOutValue& val = cache.GetOutputFor(tx.vin[i]).nValue;
if (val.IsAmount()) {
nPlainAmount -= val.GetAmount();
if (!MoneyRange(val.GetAmount()) || (!MoneyRange(nPlainAmount) && !MoneyRange(-nPlainAmount)))
return false;
}
else
{
assert(val.vchCommitment.size() == CTxOutValue::nCommitmentSize);
memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize);
vpchCommitsIn.push_back(p);
p += CTxOutValue::nCommitmentSize;
}
}
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
const CTxOutValue& val = tx.vout[i].nValue;
assert(val.vchCommitment.size() == CTxOutValue::nCommitmentSize);
if (val.vchNonceCommitment.size() > CTxOutValue::nCommitmentSize || val.vchRangeproof.size() > 5000)
return false;
if (val.IsAmount()) {
nPlainAmount += val.GetAmount();
if (!MoneyRange(val.GetAmount()) || (!MoneyRange(nPlainAmount) && !MoneyRange(-nPlainAmount)))
return false;
}
else
{
memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize);
vpchCommitsOut.push_back(p);
p += CTxOutValue::nCommitmentSize;
if (val.vchRangeproof.empty())
fNullRangeproof = true;
}
}
// If there are no encrypted input or output values, we can do simple math
if (vpchCommitsIn.size() + vpchCommitsOut.size() == 0)
return (nPlainAmount == 0);
if (!secp256k1_pedersen_verify_tally(secp256k1_ctx_verify_amounts, vpchCommitsIn.data(), vpchCommitsIn.size(), vpchCommitsOut.data(), vpchCommitsOut.size(), nPlainAmount))
return false;
// Rangeproof is optional in this case
if ((!vpchCommitsIn.empty()) && vpchCommitsOut.size() == 1 && nPlainAmount <= 0 && fNullRangeproof)
return true;
uint64_t min_value, max_value;
for (size_t i = 0; i < tx.vout.size(); ++i)
{
const CTxOutValue& val = tx.vout[i].nValue;
if (val.IsAmount())
continue;
if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &val.vchCommitment[0], val.vchRangeproof.data(), val.vchRangeproof.size()))
return false;
}
return true;
}
void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
int expired = pool.Expire(GetTime() - age);
if (expired != 0)
@ -1583,7 +1686,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
if (!MoneyRange(nFees))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
if (!inputs.VerifyAmounts(tx, nTxFee))
if (!VerifyAmounts(inputs, tx, nTxFee))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
strprintf("value in (%s) < value out", FormatMoney(nValueIn)));
@ -2261,7 +2364,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
if (!MoneyRange(blockReward))
return state.DoS(100, error("ConnectBlock(): total block reward overflowed"), REJECT_INVALID, "bad-blockreward-outofrange");
if (!view.VerifyAmounts(*(block.vtx[0]), -blockReward))
if (!VerifyAmounts(view, *(block.vtx[0]), -blockReward))
return state.DoS(100,
error("ConnectBlock(): coinbase pays too much (limit=%d)",
blockReward),