Skip to content

Commit

Permalink
Add transaction type to the Coin
Browse files Browse the repository at this point in the history
Signed-off-by: Stanislav Frolov <stanislav@thirdhash.com>
  • Loading branch information
frolosofsky committed Mar 29, 2019
1 parent aa62771 commit c938268
Show file tree
Hide file tree
Showing 38 changed files with 270 additions and 221 deletions.
7 changes: 3 additions & 4 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,12 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
}

void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check) {
bool fCoinbase = tx.IsCoinBase();
const uint256& txid = tx.GetHash();
for (size_t i = 0; i < tx.vout.size(); ++i) {
bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
const bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : tx.IsCoinBase();
// Always set the possible_overwrite flag to AddCoin for coinbase txn, 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, tx.GetType()), overwrite);
}
}

Expand Down Expand Up @@ -262,7 +261,7 @@ bool CCoinsViewCache::ApplySnapshot(std::unique_ptr<snapshot::Indexer> &&indexer
snapshot::UTXOSubset &subset = iter.GetUTXOSubset();
for (auto const &p : subset.outputs) {
COutPoint out(subset.tx_id, p.first);
Coin coin(p.second, subset.height, subset.is_coin_base);
Coin coin(p.second, subset.height, subset.tx_type);
AddCoin(out, std::move(coin), true);
}

Expand Down
32 changes: 16 additions & 16 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,26 @@ class Coin
//! unspent transaction output
CTxOut out;

//! whether containing transaction was a coinbase
unsigned int fCoinBase : 1;
TxType tx_type;

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

//! 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, TxType tx_type) : out(std::move(outIn)), tx_type(tx_type), nHeight(nHeightIn) {}
Coin(const CTxOut& outIn, int nHeightIn, TxType tx_type) : out(outIn), tx_type(tx_type), nHeight(nHeightIn) {}

void Clear() {
out.SetNull();
fCoinBase = false;
tx_type = TxType::REGULAR;
nHeight = 0;
}

//! empty constructor
Coin() : fCoinBase(false), nHeight(0) { }
Coin() : tx_type(TxType::REGULAR), nHeight(0) { }

bool IsCoinBase() const {
return fCoinBase;
return tx_type == +TxType::COINBASE;
}

//! \brief checks if this transaction is a coinbase and the reward is still immature
Expand Down Expand Up @@ -89,17 +88,18 @@ class Coin
template<typename Stream>
void Serialize(Stream &s) const {
assert(!IsSpent());
uint32_t code = nHeight * 2 + fCoinBase;
::Serialize(s, VARINT(code));
uint8_t type = +tx_type;
::Serialize(s, type);
::Serialize(s, nHeight);
::Serialize(s, CTxOutCompressor(REF(out)));
}

template<typename Stream>
void Unserialize(Stream &s) {
uint32_t code = 0;
::Unserialize(s, VARINT(code));
nHeight = code >> 1;
fCoinBase = code & 1;
uint8_t type = 0;
::Unserialize(s, type);
tx_type = TxType::_from_integral(type);
::Unserialize(s, nHeight);
::Unserialize(s, REF(CTxOutCompressor(out)));
}

Expand Down Expand Up @@ -255,7 +255,7 @@ class CCoinsViewCache : public CCoinsViewBacked, public AccessibleCoinsView
protected:
/**
* Make mutable so that we can "fill the cache" even from Get-methods
* declared as "const".
* declared as "const".
*/
mutable uint256 hashBlock;
mutable snapshot::SnapshotHash snapshotHash;
Expand Down Expand Up @@ -341,7 +341,7 @@ class CCoinsViewCache : public CCoinsViewBacked, public AccessibleCoinsView
//! Calculate the size of the cache (in bytes)
size_t DynamicMemoryUsage() const;

/**
/**
* Amount of unites 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.
Expand Down
98 changes: 65 additions & 33 deletions src/esperanza/checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <coins.h>
#include <esperanza/adminparams.h>
#include <esperanza/checks.h>
#include <esperanza/finalizationstate.h>
Expand All @@ -17,21 +18,22 @@ namespace esperanza {
bool ContextualCheckFinalizerCommit(const CTransaction &tx,
CValidationState &err_state,
const Consensus::Params &params,
const FinalizationState &fin_state) {
const FinalizationState &fin_state,
const CCoinsView &view) {
switch (tx.GetType()) {
case +TxType::REGULAR:
case +TxType::COINBASE:
assert(not("Shouldn't be called on non-finalization transaction"));
case +TxType::DEPOSIT:
return ContextualCheckDepositTx(tx, err_state, fin_state);
case +TxType::VOTE:
return ContextualCheckVoteTx(tx, err_state, params, fin_state);
return ContextualCheckVoteTx(tx, err_state, params, fin_state, view);
case +TxType::LOGOUT:
return ContextualCheckLogoutTx(tx, err_state, params, fin_state);
return ContextualCheckLogoutTx(tx, err_state, params, fin_state, view);
case +TxType::SLASH:
return ContextualCheckSlashTx(tx, err_state, params, fin_state);
case +TxType::WITHDRAW:
return ContextualCheckWithdrawTx(tx, err_state, params, fin_state);
return ContextualCheckWithdrawTx(tx, err_state, params, fin_state, view);
case +TxType::ADMIN:
return ContextualCheckAdminTx(tx, err_state, fin_state);
}
Expand Down Expand Up @@ -67,6 +69,40 @@ inline bool CheckValidatorAddress(const CTransaction &tx, uint160 *addr_out) {
}
return ExtractValidatorAddress(tx, *addr_out);
}

bool FindPrevTxData(const CTransaction &tx,
const Consensus::Params &consensus_params,
const CCoinsView &view,
TxType *tx_type,
CScript *script) {
Coin prev_coin;
if (view.GetCoin(tx.vin[0].prevout, prev_coin)) {
if (tx_type != nullptr) {
*tx_type = prev_coin.tx_type;
}
if (script != nullptr) {
*script = prev_coin.out.scriptPubKey;
}
return true;
}
CTransactionRef prev_tx;
uint256 block_hash;
// We have to look into the tx database to find the prev tx, hence the
// use of fAllowSlow = true
LogPrint(BCLog::FINALIZATION, "Cannout find coin=%s in the UTXO set, try find the whole transaction\n", tx.vin[0].prevout.ToString());
if (GetTransaction(tx.vin[0].prevout.hash, prev_tx, consensus_params,
block_hash, true)) {
if (tx_type != nullptr) {
*tx_type = prev_tx->GetType();
}
if (script != nullptr) {
*script = prev_tx->vout[0].scriptPubKey;
}
return true;
}
return false;
}

} // namespace

bool CheckDepositTx(const CTransaction &tx, CValidationState &err_state,
Expand Down Expand Up @@ -160,7 +196,8 @@ bool CheckLogoutTx(const CTransaction &tx, CValidationState &err_state,

bool ContextualCheckLogoutTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_params,
const FinalizationState &fin_state) {
const FinalizationState &fin_state,
const CCoinsView &view) {

uint160 validator_address = uint160();
if (!CheckLogoutTx(tx, err_state, &validator_address)) {
Expand All @@ -176,23 +213,20 @@ bool ContextualCheckLogoutTx(const CTransaction &tx, CValidationState &err_state
// check (potentially goes to disk) and there is a good chance that if the
// vote is not valid (i.e. outdated) then the function will return before
// reaching this point.
CTransactionRef prev_tx;
uint256 block_hash;
TxType prev_tx_type = TxType::REGULAR;
CScript prev_out_script;

// We have to look into the tx database to find the prev tx, hence the
// use of fAllowSlow = true
if (!GetTransaction(tx.vin[0].prevout.hash, prev_tx, consensus_params,
block_hash, true)) {
if (!FindPrevTxData(tx, consensus_params, view, &prev_tx_type, &prev_out_script)) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-logout-no-prev-tx-found");
}

if (!prev_tx->IsDeposit() && !prev_tx->IsVote()) {
if (prev_tx_type != +TxType::DEPOSIT && prev_tx_type != +TxType::VOTE) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-logout-prev-not-deposit-or-vote");
}

if (prev_tx->vout[0].scriptPubKey != tx.vout[0].scriptPubKey) {
if (prev_out_script != tx.vout[0].scriptPubKey) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-logout-not-same-payvoteslash-script");
}
Expand Down Expand Up @@ -231,28 +265,25 @@ bool CheckWithdrawTx(const CTransaction &tx, CValidationState &err_state,

bool ContextualCheckWithdrawTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_params,
const FinalizationState &fin_state) {
const FinalizationState &fin_state,
const CCoinsView &view) {

uint160 validator_address = uint160();
if (!CheckWithdrawTx(tx, err_state, &validator_address)) {
return false;
}

CTransactionRef prev_tx;
uint256 block_hash;

// We have to look into the tx database to find the prev tx, hence the
// use of fAllowSlow = true
if (!GetTransaction(tx.vin[0].prevout.hash, prev_tx, consensus_params,
block_hash, true)) {
TxType prev_tx_type = TxType::REGULAR;
CScript prev_out_script;

if (!FindPrevTxData(tx, consensus_params, view, &prev_tx_type, &prev_out_script)) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-withdraw-no-prev-tx-found");
}

std::vector<std::vector<unsigned char>> prev_solutions;
txnouttype prev_type_ret;
if (!Solver(prev_tx->vout[0].scriptPubKey, prev_type_ret, prev_solutions)) {
if (!Solver(prev_out_script, prev_type_ret, prev_solutions)) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-logout-script-not-solvable");
}
Expand All @@ -264,7 +295,7 @@ bool ContextualCheckWithdrawTx(const CTransaction &tx, CValidationState &err_sta
"bad-withdraw-invalid-state");
}

if (!prev_tx->IsLogout() && !prev_tx->IsVote()) {
if (prev_tx_type != +TxType::LOGOUT && prev_tx_type != +TxType::VOTE) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-withdraw-prev-not-logout-or-vote");
}
Expand Down Expand Up @@ -313,7 +344,8 @@ bool CheckVoteTx(const CTransaction &tx, CValidationState &err_state,

bool ContextualCheckVoteTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_params,
const FinalizationState &fin_state) {
const FinalizationState &fin_state,
const CCoinsView &view) {

Vote vote;
std::vector<unsigned char> vote_sig;
Expand All @@ -333,21 +365,21 @@ bool ContextualCheckVoteTx(const CTransaction &tx, CValidationState &err_state,
// check (potentially goes to disk) and there is a good chance that if the
// vote is not valid (i.e. outdated) then the function will return before
// reaching this point.
CTransactionRef prev_tx;
uint256 block_hash;
// We have to look into the tx database to find the prev tx, hence the
// use of fAllowSlow = true
if (!GetTransaction(tx.vin[0].prevout.hash, prev_tx, consensus_params,
block_hash, true)) {
return err_state.DoS(10, false, REJECT_INVALID, "bad-vote-no-prev-tx-found");

TxType prev_tx_type = TxType::REGULAR;
CScript prev_out_script;

if (!FindPrevTxData(tx, consensus_params, view, &prev_tx_type, &prev_out_script)) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-vote-no-prev-tx-found");
}

if (!prev_tx->IsDeposit() && !prev_tx->IsVote() && !prev_tx->IsLogout()) {
if (prev_tx_type != +TxType::DEPOSIT && prev_tx_type != +TxType::VOTE && prev_tx_type != +TxType::LOGOUT) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-vote-prev-not-deposit-vote-or-logout");
}

if (prev_tx->vout[0].scriptPubKey != tx.vout[0].scriptPubKey) {
if (prev_out_script != tx.vout[0].scriptPubKey) {
return err_state.DoS(10, false, REJECT_INVALID,
"bad-vote-not-same-payvoteslash-script");
}
Expand Down
14 changes: 10 additions & 4 deletions src/esperanza/checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <primitives/block.h>
#include <primitives/transaction.h>

class CCoinsView;

namespace esperanza {

class FinalizationState;
Expand All @@ -36,7 +38,8 @@ bool CheckFinalizerCommit(const CTransaction &tx, CValidationState &err_state);
bool ContextualCheckFinalizerCommit(const CTransaction &tx,
CValidationState &err_state,
const Consensus::Params &params,
const FinalizationState &fin_state);
const FinalizationState &fin_state,
const CCoinsView &view);

bool CheckDepositTx(const CTransaction &tx, CValidationState &err_state,
uint160 *validator_address_out);
Expand All @@ -47,7 +50,8 @@ bool CheckVoteTx(const CTransaction &tx, CValidationState &err_state,
Vote *vote_out, std::vector<unsigned char> *vote_sig_out);
bool ContextualCheckVoteTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_params,
const FinalizationState &fin_state);
const FinalizationState &fin_state,
const CCoinsView &view);

bool CheckSlashTx(const CTransaction &tx, CValidationState &err_state,
Vote *vote1_out, Vote *vote2_out);
Expand All @@ -59,13 +63,15 @@ bool CheckLogoutTx(const CTransaction &tx, CValidationState &err_state,
uint160 *out_validator_address);
bool ContextualCheckLogoutTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_params,
const FinalizationState &fin_state);
const FinalizationState &fin_state,
const CCoinsView &view);

bool CheckWithdrawTx(const CTransaction &tx, CValidationState &err_state,
uint160 *out_validator_address);
bool ContextualCheckWithdrawTx(const CTransaction &tx, CValidationState &err_state,
const Consensus::Params &consensus_arams,
const FinalizationState &fin_state);
const FinalizationState &fin_state,
const CCoinsView &view);

bool CheckAdminTx(const CTransaction &tx, CValidationState &err_state,
std::vector<CPubKey> *keys_out);
Expand Down
3 changes: 2 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,15 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc

void BlockAssembler::AddMandatoryTxs()
{
CCoinsViewCache view(pcoinsTip.get());
const auto &fin_state = *esperanza::FinalizationState::GetState(chainActive.Tip());
auto mi = mempool.mapTx.get<ancestor_score>().begin();
for (;mi != mempool.mapTx.get<ancestor_score>().end(); ++mi) {

if (mi->GetTx().IsVote()) {
CValidationState state;
//Check again in case the vote became invalid in the meanwhile (different target now)
if (esperanza::ContextualCheckVoteTx(mi->GetTx(), state, chainparams.GetConsensus(), fin_state)) {
if (esperanza::ContextualCheckVoteTx(mi->GetTx(), state, chainparams.GetConsensus(), fin_state, view)) {
AddToBlock(mempool.mapTx.project<0>(mi));
LogPrint(BCLog::FINALIZATION,
"%s: Add vote with id %s to a new block.\n",
Expand Down
6 changes: 3 additions & 3 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
{
assert(!outputs.empty());
ss << hash;
ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase);
ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.IsCoinBase());
stats.nTransactions++;
for (const auto output : outputs) {
ss << VARINT(output.first + 1);
Expand Down Expand Up @@ -1022,7 +1022,7 @@ UniValue gettxout(const JSONRPCRequest& request)
" ,...\n"
" ]\n"
" },\n"
" \"coinbase\" : true|false (boolean) Coinbase or not\n"
" \"type\" : (numeric) Transaction type\n"
"}\n"

"\nExamples:\n"
Expand Down Expand Up @@ -1071,7 +1071,7 @@ UniValue gettxout(const JSONRPCRequest& request)
UniValue o(UniValue::VOBJ);
ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("coinbase", (bool)coin.fCoinBase));
ret.push_back(Pair("type", +coin.tx_type));

return ret;
}
Expand Down
Loading

0 comments on commit c938268

Please sign in to comment.