Skip to content

Commit

Permalink
Port coins
Browse files Browse the repository at this point in the history
  • Loading branch information
timemarkovqtum committed Feb 15, 2024
1 parent a6d50f1 commit 6adcfa8
Show file tree
Hide file tree
Showing 25 changed files with 414 additions and 74 deletions.
38 changes: 30 additions & 8 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,13 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
it->second.coin = std::move(coin);
it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
TRACE5(utxocache, add,
TRACE6(utxocache, add,
outpoint.hash.data(),
(uint32_t)outpoint.n,
(uint32_t)it->second.coin.nHeight,
(int64_t)it->second.coin.out.nValue,
(bool)it->second.coin.IsCoinBase());
(bool)it->second.coin.IsCoinBase(),
(bool)it->second.coin.IsCoinStake());
}

void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
Expand All @@ -117,25 +118,27 @@ void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coi

void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
bool fCoinbase = tx.IsCoinBase();
bool fCoinstake = tx.IsCoinStake();
const uint256& txid = tx.GetHash();
for (size_t i = 0; i < tx.vout.size(); ++i) {
bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
// Coinbase transactions can always be overwritten, in order to correctly
// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinstake), overwrite);
}
}

bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
CCoinsMap::iterator it = FetchCoin(outpoint);
if (it == cacheCoins.end()) return false;
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
TRACE5(utxocache, spent,
TRACE6(utxocache, spent,
outpoint.hash.data(),
(uint32_t)outpoint.n,
(uint32_t)it->second.coin.nHeight,
(int64_t)it->second.coin.out.nValue,
(bool)it->second.coin.IsCoinBase());
(bool)it->second.coin.IsCoinBase(),
(bool)it->second.coin.IsCoinStake());
if (moveout) {
*moveout = std::move(it->second.coin);
}
Expand Down Expand Up @@ -286,12 +289,13 @@ void CCoinsViewCache::Uncache(const COutPoint& hash)
CCoinsMap::iterator it = cacheCoins.find(hash);
if (it != cacheCoins.end() && it->second.flags == 0) {
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
TRACE5(utxocache, uncache,
TRACE6(utxocache, uncache,
hash.hash.data(),
(uint32_t)hash.n,
(uint32_t)it->second.coin.nHeight,
(int64_t)it->second.coin.out.nValue,
(bool)it->second.coin.IsCoinBase());
(bool)it->second.coin.IsCoinBase(),
(bool)it->second.coin.IsCoinStake());
cacheCoins.erase(it);
}
}
Expand All @@ -300,6 +304,18 @@ unsigned int CCoinsViewCache::GetCacheSize() const {
return cacheCoins.size();
}

CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
{
if (tx.IsCoinBase())
return 0;

CAmount nResult = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
nResult += AccessCoin(tx.vin[i].prevout).out.nValue;

return nResult;
}

bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsCoinBase()) {
Expand Down Expand Up @@ -340,7 +356,7 @@ void CCoinsViewCache::SanityCheck() const
}

static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT;
static const size_t MAX_OUTPUTS_PER_BLOCK = dgpMaxBlockWeight / MIN_TRANSACTION_OUTPUT_WEIGHT;

const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
{
Expand Down Expand Up @@ -378,3 +394,9 @@ bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) cons
bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint &outpoint) const {
return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks);
}

const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
{
const Coin& coins = AccessCoin(input.prevout);
return coins.out;
}
92 changes: 85 additions & 7 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,72 @@
#include <functional>
#include <unordered_map>

////////////////////////////////////////////////////////////////// // qtum
struct CSpentIndexKey {
uint256 txid;
unsigned int outputIndex;

SERIALIZE_METHODS(CSpentIndexKey, obj) { READWRITE(obj.txid, obj.outputIndex); }

CSpentIndexKey(uint256 t, unsigned int i) {
txid = t;
outputIndex = i;
}

CSpentIndexKey() {
SetNull();
}

void SetNull() {
txid.SetNull();
outputIndex = 0;
}
};

struct CSpentIndexValue {
uint256 txid;
unsigned int inputIndex;
int blockHeight;
CAmount satoshis;
int addressType;
uint256 addressHash;

SERIALIZE_METHODS(CSpentIndexValue, obj) { READWRITE(obj.txid, obj.inputIndex, obj.blockHeight, obj.satoshis, obj.addressType, obj.addressHash); }

CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint256 a) {
txid = t;
inputIndex = i;
blockHeight = h;
satoshis = s;
addressType = type;
addressHash = a;
}

CSpentIndexValue() {
SetNull();
}

void SetNull() {
txid.SetNull();
inputIndex = 0;
blockHeight = 0;
satoshis = 0;
addressType = 0;
addressHash.SetNull();
}

bool IsNull() const {
return txid.IsNull();
}
};
//////////////////////////////////////////////////////////////////

/**
* A UTXO entry.
*
* Serialized format:
* - VARINT((coinbase ? 1 : 0) | (height << 1))
* - VARINT((coinbase ? 1 : 0) | (height << 2))
* - VARINT((coinstake ? 2 : 0) | (height << 2))
* - the non-spent CTxOut (via TxOutCompression)
*/
class Coin
Expand All @@ -39,29 +100,33 @@ class Coin
unsigned int fCoinStake : 1;

//! at which height this containing transaction was included in the active block chain
uint32_t nHeight : 31;
uint32_t nHeight : 30;

//! construct a Coin from a CTxOut and height/coinbase information.
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) {}
Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn) : out(outIn), fCoinBase(fCoinBaseIn),nHeight(nHeightIn) {}
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn) {}
Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn) : out(outIn), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn) {}

void Clear() {
out.SetNull();
fCoinBase = false;
fCoinStake = false;
nHeight = 0;
}

//! empty constructor
Coin() : fCoinBase(false), nHeight(0) { }
Coin() : fCoinBase(false), fCoinStake(false), nHeight(0) { }

bool IsCoinBase() const {
return fCoinBase;
}
bool IsCoinStake() const {
return fCoinStake;
}

template<typename Stream>
void Serialize(Stream &s) const {
assert(!IsSpent());
uint32_t code = nHeight * uint32_t{2} + fCoinBase;
uint32_t code = (nHeight << 2) + (fCoinBase ? 1 : 0) + (fCoinStake ? 2 : 0);
::Serialize(s, VARINT(code));
::Serialize(s, Using<TxOutCompression>(out));
}
Expand All @@ -70,8 +135,9 @@ class Coin
void Unserialize(Stream &s) {
uint32_t code = 0;
::Unserialize(s, VARINT(code));
nHeight = code >> 1;
nHeight = code >> 2;
fCoinBase = code & 1;
fCoinStake = (code >> 1) & 1;
::Unserialize(s, Using<TxOutCompression>(out));
}

Expand Down Expand Up @@ -331,6 +397,16 @@ class CCoinsViewCache : public CCoinsViewBacked
//! Calculate the size of the cache (in bytes)
size_t DynamicMemoryUsage() const;

/**
* Amount of bitcoins coming in to a transaction
* Note that lightweight clients may not know anything besides the hash of previous transactions,
* so may not be able to calculate this.
*
* @param[in] tx transaction for which we are checking input total
* @return Sum of value of all inputs (scriptSigs)
*/
CAmount GetValueIn(const CTransaction& tx) const;

//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx) const;

Expand All @@ -344,6 +420,8 @@ class CCoinsViewCache : public CCoinsViewBacked
//! Run an internal sanity check on the cache data structure. */
void SanityCheck() const;

const CTxOut &GetOutputFor(const CTxIn& input) const;

private:
/**
* @note this is marked const, but may actually append to `cacheCoins`, increasing
Expand Down
20 changes: 19 additions & 1 deletion src/consensus/tx_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <consensus/validation.h>
#ifndef BUILD_BITCOIN_INTERNAL
#include <script/solver.h>
#endif

bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
{
Expand All @@ -16,20 +19,35 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
if (tx.vout.empty())
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_TRANSACTION_BASE_SIZE ||
::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > dgpMaxBlockWeight)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize");

// Check for negative or overflow output values (see CVE-2010-5139)
CAmount nValueOut = 0;
for (const auto& txout : tx.vout)
{
if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake())
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");
if (txout.nValue < 0)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");

#ifndef BUILD_BITCOIN_INTERNAL
/////////////////////////////////////////////////////////// // qtum
if (txout.scriptPubKey.HasOpCall() || txout.scriptPubKey.HasOpCreate() || txout.scriptPubKey.HasOpSender()) {
std::vector<std::vector<unsigned char>> vSolutions;
TxoutType whichType = Solver(txout.scriptPubKey, vSolutions, true);
if (whichType == TxoutType::NONSTANDARD) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-contract-nonstandard");
}
}
///////////////////////////////////////////////////////////
#endif
}

// Check for duplicate inputs (see CVE-2018-17144)
Expand Down
28 changes: 16 additions & 12 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <script/interpreter.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <chainparams.h>

bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
Expand Down Expand Up @@ -180,7 +181,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
assert(!coin.IsSpent());

// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
if ((coin.IsCoinBase() || coin.IsCoinStake()) && nSpendHeight - coin.nHeight < ::Params().GetConsensus().CoinbaseMaturity(nSpendHeight)) {
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
}
Expand All @@ -192,18 +193,21 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
}
}

const CAmount value_out = tx.GetValueOut();
if (nValueIn < value_out) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
}
if (!tx.IsCoinStake())
{
const CAmount value_out = tx.GetValueOut();
if (nValueIn < value_out) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
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.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
}
// Tally transaction fees
const CAmount txfee_aux = nValueIn - value_out;
if (!MoneyRange(txfee_aux)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
}

txfee = txfee_aux;
txfee = txfee_aux;
}
return true;
}
Loading

0 comments on commit 6adcfa8

Please sign in to comment.