Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fork out NotAllowedToFail #223

Merged
merged 1 commit into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
std::vector<unsigned char> dummy;
const auto txType = GuessCustomTxType(tx, dummy);

if (NotAllowedToFail(txType)) {
if (NotAllowedToFail(txType, nSpendHeight)) {
auto res = ApplyCustomTx(const_cast<CCustomCSView&>(*mnview), inputs, tx, chainparams.GetConsensus(), nSpendHeight, 0, true); // note for 'isCheck == true' here; 'zero' for txn is dummy value
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-customtx", res.msg);
Expand Down
5 changes: 3 additions & 2 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CT
default:
return Res::Ok(); // not "custom" tx
}
// list of transactions which aren't allowed to fail:
if (!res.ok && NotAllowedToFail(guess)) {
// list of transactions which aren't allowed to fail,
// post Dakota height all failed txs are marked as fatal
if (!res.ok && (NotAllowedToFail(guess, height) || height >= consensusParams.DakotaHeight)) {
res.code |= CustomTxErrCodes::Fatal;
}
} catch (std::exception& e) {
Expand Down
6 changes: 4 additions & 2 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ inline std::string ToString(CustomTxType type) {
}
}

inline bool NotAllowedToFail(CustomTxType txType) {
return txType == CustomTxType::MintToken || txType == CustomTxType::AccountToUtxos;
// it's disabled after Dakota height
inline bool NotAllowedToFail(CustomTxType txType, int height) {
return (height < Params().GetConsensus().DakotaHeight
&& (txType == CustomTxType::MintToken || txType == CustomTxType::AccountToUtxos));
}

template<typename Stream>
Expand Down
2 changes: 1 addition & 1 deletion src/test/mempool_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include <boost/test/unit_test.hpp>
#include <vector>

BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
BOOST_FIXTURE_TEST_SUITE(mempool_tests, BasicTestingSetup)

static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::REPLACED;

Expand Down
91 changes: 40 additions & 51 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,69 +556,58 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight)
{
AssertLockHeld(cs);
std::vector<const CTxMemPoolEntry*> entries;
for (const auto& tx : vtx)
{
uint256 hash = tx->GetHash();

indexed_transaction_set::iterator i = mapTx.find(hash);
if (i != mapTx.end())
entries.push_back(&*i);
}
// Before the txs in the new block have been removed from the mempool, update policy estimates
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
for (const auto& tx : vtx)
{
txiter it = mapTx.find(tx->GetHash());
setEntries staged;
std::vector<const CTxMemPoolEntry*> entries;
for (const auto& tx : vtx) {
auto it = mapTx.find(tx->GetHash());
if (it != mapTx.end()) {
setEntries stage;
stage.insert(it);
RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK);
staged.insert(it);
entries.push_back(&*it);
}
removeConflicts(*tx);
ClearPrioritisation(tx->GetHash());
}
// Before the txs in the new block have been removed from the mempool, update policy estimates
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}

bool accountConflict{false};

// Check if any custom TXs are in mempool with conflict
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); ++it) {
std::vector<unsigned char> metadata;
CustomTxType txType = GuessCustomTxType(it->GetTx(), metadata);
if (NotAllowedToFail(txType)) {
auto res = ApplyCustomTx(*pcustomcsview, g_chainstate->CoinsTip(), it->GetTx(), Params().GetConsensus(), nBlockHeight, 0, true);
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
accountConflict = true;
break;
}
}
for (auto& it : staged) {
auto& tx = it->GetTx();
removeConflicts(tx);
ClearPrioritisation(tx.GetHash());
}

// Account conflict, check entire mempool
if (accountConflict) {
std::set<CTransactionRef> txsToRemove;
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&::ChainstateActive().CoinsTip()));
RemoveStaged(staged, true, MemPoolRemovalReason::BLOCK);

if (pcustomcsview) { // can happen in tests
// check entire mempool
CAmount txfee = 0;
CCustomCSView viewDuplicate(*pcustomcsview);
CCoinsViewCache mempoolDuplicate(&::ChainstateActive().CoinsTip());

setEntries staged;
// Check custom TX consensus types are now not in conflict with account layer
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); ++it) {
auto& txsByEntryTime = mapTx.get<entry_time>();
for (auto it = txsByEntryTime.begin(); it != txsByEntryTime.end(); ++it) {
CValidationState state;
if (!Consensus::CheckTxInputs(it->GetTx(), state, mempoolDuplicate, pcustomcsview.get(), nBlockHeight, txfee, Params())) {
LogPrintf("%s: Remove conflicting TX: %s\n", __func__, it->GetTx().GetHash().GetHex());
txsToRemove.insert(it->GetSharedTx());
const auto& tx = it->GetTx();
if (!Consensus::CheckTxInputs(tx, state, mempoolDuplicate, &viewDuplicate, nBlockHeight, txfee, Params())) {
LogPrintf("%s: Remove conflicting TX: %s\n", __func__, tx.GetHash().GetHex());
staged.insert(mapTx.project<0>(it));
continue;
}
auto res = ApplyCustomTx(viewDuplicate, mempoolDuplicate, tx, Params().GetConsensus(), nBlockHeight, 0, false);
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
LogPrintf("%s: Remove conflicting custom TX: %s\n", __func__, tx.GetHash().GetHex());
staged.insert(mapTx.project<0>(it));
}
}

for (auto& tx : txsToRemove) {
txiter it = mapTx.find(tx->GetHash());
if (it != mapTx.end()) {
setEntries stage;
stage.insert(it);
RemoveStaged(stage, true, MemPoolRemovalReason::CONFLICT);
}
removeConflicts(*tx);
ClearPrioritisation(tx->GetHash());
for (auto& it : staged) {
auto& tx = it->GetTx();
removeConflicts(tx);
ClearPrioritisation(tx.GetHash());
}

RemoveStaged(staged, true, MemPoolRemovalReason::BLOCK);
}

lastRollingFeeUpdate = GetTime();
Expand Down Expand Up @@ -893,7 +882,7 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
}

void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
{
LOCK(cs);
std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash);
Expand All @@ -903,7 +892,7 @@ void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
nFeeDelta += delta;
}

void CTxMemPool::ClearPrioritisation(const uint256 hash)
void CTxMemPool::ClearPrioritisation(const uint256& hash)
{
LOCK(cs);
mapDeltas.erase(hash);
Expand Down Expand Up @@ -964,7 +953,7 @@ size_t CTxMemPool::DynamicMemoryUsage() const {
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}

void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
void CTxMemPool::RemoveStaged(const setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
AssertLockHeld(cs);
UpdateForRemoveFromMempool(stage, updateDescendants);
for (txiter it : stage) {
Expand Down
6 changes: 3 additions & 3 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,8 @@ class CTxMemPool

/** Affect CreateNewBlock prioritisation of transactions */
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const;
void ClearPrioritisation(const uint256 hash);
void ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const;
void ClearPrioritisation(const uint256& hash);

/** Get the transaction in the pool that spends the same prevout */
const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs);
Expand All @@ -614,7 +614,7 @@ class CTxMemPool
* Set updateDescendants to true when removing a tx that was in a block, so
* that any in-mempool descendants have their ancestor state updated.
*/
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
void RemoveStaged(const setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);

/** When adding transactions from a disconnected block back to the mempool,
* new mempool entries may have children in the mempool (which is generally
Expand Down
29 changes: 15 additions & 14 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,23 +609,29 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
for (const auto& e : mempool.mapTx.get<entry_time>()) {
const auto& tx = e.GetTx();
auto res = ApplyCustomTx(mnview, view, tx, chainparams.GetConsensus(), height, 0, false);
assert(res.ok || !(res.code & CustomTxErrCodes::Fatal)); // inconsistent mempool
// we don't need contract anynore furthermore transition to new hardfork will broke it
if (height < chainparams.GetConsensus().DakotaHeight) {
assert(res.ok || !(res.code & CustomTxErrCodes::Fatal));
}
}

CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, view, &mnview, height, nFees, chainparams)) {
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
}

// NOTE Consensus::CheckTxInputs will check for NotAllowToFail
std::vector<unsigned char> metadata;
const auto txType = GuessCustomTxType(tx, metadata);
if (nAbsurdFee && nFees > nAbsurdFee) {
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee));
}

if (!NotAllowedToFail(txType)) {
auto res = ApplyCustomTx(mnview, view, tx, chainparams.GetConsensus(), height, 0, false);
if (!res.ok || (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INVALID, res.msg);
}
// let make sure we have needed coins
if (view.GetValueIn(tx) < nFees) {
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INVALID, "bad-txns-inputs-below-tx-fee");
}

auto res = ApplyCustomTx(mnview, view, tx, chainparams.GetConsensus(), height, 0, false);
if (!res.ok || (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INVALID, res.msg);
}

// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
Expand Down Expand Up @@ -688,11 +694,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
}

if (nAbsurdFee && nFees > nAbsurdFee)
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
REJECT_HIGHFEE, "absurdly-high-fee",
strprintf("%d > %d", nFees, nAbsurdFee));

// Calculate in-mempool ancestors, up to a limit.
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
Expand Down
53 changes: 53 additions & 0 deletions test/functional/feature_mempool_dakota.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2019 The Bitcoin Core developers
# Copyright (c) DeFi Blockchain Developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test account mining behaviour"""

from test_framework.test_framework import DefiTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)

class MempoolDakotaTest(DefiTestFramework):
def set_test_params(self):
self.num_nodes = 3
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-dakotaheight=100'],
['-txnotokens=0', '-amkheight=50', '-dakotaheight=100'],
['-txnotokens=0', '-amkheight=50', '-dakotaheight=100'],
]

def run_test(self):
node = self.nodes[0]
node1 = self.nodes[1]
node.generate(101)
self.sync_all()

assert_equal(node.getblockcount(), 101) # Dakota height

# Get addresses and set up account
wallet1_addr = node1.getnewaddress("", "legacy")
node.sendtoaddress(wallet1_addr, "3.1")
node.generate(1)
self.sync_all()
collateral = node1.createmasternode(wallet1_addr)
node.generate(1)
self.sync_all()
assert_raises_rpc_error(-26, "collateral-locked", node1.utxostoaccount, {wallet1_addr: "0.09@0"})
node.generate(1)
self.sync_all()
assert_equal(node1.listmasternodes({}, False)[collateral], "PRE_ENABLED")
assert_equal(node1.getmasternode(collateral)[collateral]["state"], "PRE_ENABLED")
node.generate(10)
assert_equal(node.listmasternodes({}, False)[collateral], "ENABLED")

node1.utxostoaccount({wallet1_addr: "0.09@0"})
node.generate(1)
self.sync_all()

if __name__ == '__main__':
MempoolDakotaTest().main ()
27 changes: 12 additions & 15 deletions test/functional/feature_mine_cached.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,23 @@ def set_test_params(self):
def run_test(self):
assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI

print("Generating initial chain...")
tokens = [
{
"wallet": self.nodes[0],
"symbol": "GOLD",
"name": "shiny gold",
"collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress,
"amount": 30
},
]
# inside this function "tokenId" and "symbolId" will be assigned for each token obj
self.setup_tokens(tokens)
self.nodes[0].generate(101)
self.sync_all()

token0_symbol = tokens[0]["symbolId"]
wallet0_addr = self.nodes[0].getnewaddress("", "legacy")
self.nodes[0].utxostoaccount({wallet0_addr: "10@0"})
self.nodes[0].generate(1)
self.sync_all()

to = {}
wallet1_addr1 = self.nodes[1].getnewaddress("", "legacy")
to[wallet1_addr1] = ["10@" + token0_symbol, "10@0"]
wallet1_addr = self.nodes[1].getnewaddress("", "legacy")
to[wallet1_addr] = ["10@0"]

assert_raises_rpc_error(-5, None, self.nodes[0].sendtokenstoaddress, {}, to)

self.nodes[0].importprivkey(self.nodes[1].dumpprivkey(wallet1_addr))

self.nodes[0].sendtokenstoaddress({}, to)

if __name__ == '__main__':
IsMineCachedTest().main()
2 changes: 1 addition & 1 deletion test/functional/feature_poolpair_liquidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def run_test(self):
assert_equal(self.nodes[0].getaccount(accountGold, {}, True)[idGold], initialGold)
assert_equal(self.nodes[0].getaccount(accountSilver, {}, True)[idSilver], initialSilver)

assert_equal(len(self.nodes[0].getrawmempool()), 13) # 13 txs
# assert_equal(len(self.nodes[0].getrawmempool()), 13) # removed txs for block


if __name__ == '__main__':
Expand Down
9 changes: 6 additions & 3 deletions test/functional/rpc_mn_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def run_test(self):
# Funding auth address and successful resign
fundingTx = self.nodes[0].sendtoaddress(collateral0, 1)
self.nodes[0].generate(1)
resignTx = self.nodes[0].resignmasternode(idnode0)
# resignTx
self.nodes[0].resignmasternode(idnode0)
self.nodes[0].generate(1)
assert_equal(self.nodes[0].listmasternodes()[idnode0]['state'], "PRE_RESIGNED")
self.nodes[0].generate(10)
Expand Down Expand Up @@ -129,7 +130,8 @@ def run_test(self):
# print ("ResignTx", resignTx)
# print ("FundingTx", fundingTx)
# print ("SpendTx", sendedTxHash)
assert_equal(self.nodes[0].getrawmempool(), [fundingTx, resignTx])
# resignTx is removed for a block
assert_equal(self.nodes[0].getrawmempool(), [fundingTx])
assert_equal(self.nodes[0].listmasternodes()[idnode0]['state'], "ENABLED")

# Revert creation!
Expand All @@ -139,7 +141,8 @@ def run_test(self):
connect_nodes_bi(self.nodes, 0, 2)
self.sync_blocks(self.nodes[0:3])
assert_equal(len(self.nodes[0].listmasternodes()), 8)
assert_equal(self.nodes[0].getrawmempool(), [idnode0, fundingTx, resignTx])
# fundingTx is removed for a block
assert_equal(self.nodes[0].getrawmempool(), [idnode0])

collateral0 = self.nodes[0].getnewaddress("", "legacy")
self.nodes[0].createmasternode(collateral0)
Expand Down
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
'feature_prevent_bad_tx_propagation.py',
'feature_masternode_operator.py',
'feature_mine_cached.py',
'feature_mempool_dakota.py',
'interface_http.py',
'interface_rpc.py',
'rpc_psbt.py',
Expand Down
5 changes: 4 additions & 1 deletion test/functional/wallet_bumpfee.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,10 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):

# Call abandon to make sure the wallet doesn't attempt to resubmit
# the bump tx and hope the wallet does not rebroadcast before we call.
rbf_node.abandontransaction(bumpid)
try:
rbf_node.abandontransaction(bumpid)
except:
pass # make sure test not fail due to missing tx
assert bumpid not in rbf_node.getrawmempool()
assert rbfid in rbf_node.getrawmempool()

Expand Down