// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "txmempool.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
+#include "validationinterface.h"
#include "version.h"
+#include "cc/CCinclude.h"
#include "pbaas/pbaas.h"
+#include "pbaas/identity.h"
#define _COINBASE_MATURITY 100
using namespace std;
{
CAmount nValueIn = tx.GetValueOut()+nFee;
CCurrencyState currencyState;
- if (hasReserve)
+ unsigned int lastHeight = currentHeight < 1 ? 0 : currentHeight - 1;
+ AssertLockHeld(cs_main);
+ if (hasReserve && (currencyState = ConnectedChains.GetCurrencyState(currentHeight - 1)).IsValid())
{
- nValueIn += currencyState.ReserveToNative(tx.GetReserveValueOut(currencyState));
+ nValueIn += currencyState.ReserveToNative(tx.GetReserveValueOut());
}
double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nModSize;
double dResult = dPriority + deltaPriority;
LOCK(cs);
mapTx.insert(entry);
const CTransaction& tx = mapTx.find(hash)->GetTx();
+ mapRecentlyAddedTx[tx.GetHash()] = &tx;
+ nRecentlyAddedSequence += 1;
if (!tx.IsCoinImport()) {
for (unsigned int i = 0; i < tx.vin.size(); i++)
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
}
- BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
+ BOOST_FOREACH(const JSDescription &joinsplit, tx.vJoinSplit) {
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
mapSproutNullifiers[nf] = &tx;
}
std::vector<CMempoolAddressDeltaKey> inserted;
uint256 txhash = tx.GetHash();
-
if (!tx.IsCoinBase())
{
for (unsigned int j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(input);
- if (prevout.scriptPubKey.IsPayToScriptHash()) {
- vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
- CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1);
- CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
- mapAddress.insert(make_pair(key, delta));
- inserted.push_back(key);
- }
- else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
- vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
- CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
- CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
- mapAddress.insert(make_pair(key, delta));
- inserted.push_back(key);
+ COptCCParams p;
+ if (prevout.scriptPubKey.IsPayToCryptoCondition(p))
+ {
+ std::vector<CTxDestination> dests;
+ if (p.IsValid())
+ {
+ dests = p.GetDestinations();
+ }
+ else
+ {
+ dests = prevout.scriptPubKey.GetDestinations();
+ }
+
+ uint32_t nHeight = chainActive.Height();
+ std::map<uint160, uint32_t> heightOffsets = p.GetIndexHeightOffsets(chainActive.Height());
+
+ for (auto dest : dests)
+ {
+ if (dest.which() != COptCCParams::ADDRTYPE_INVALID)
+ {
+ uint160 destID = GetDestinationID(dest);
+ if (!(dest.which() == COptCCParams::ADDRTYPE_INDEX && heightOffsets.count(destID) && heightOffsets[destID] != nHeight))
+ {
+ CMempoolAddressDeltaKey key(AddressTypeFromDest(dest), destID, txhash, j, 1);
+ CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
+ mapAddress.insert(make_pair(key, delta));
+ inserted.push_back(key);
+ }
+ }
+ }
}
- else if (prevout.scriptPubKey.IsPayToPublicKey()) {
- vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34);
- CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
+ else
+ {
+ CScript::ScriptType type = prevout.scriptPubKey.GetType();
+ if (type == CScript::UNKNOWN)
+ continue;
+
+ CMempoolAddressDeltaKey key(type, prevout.scriptPubKey.AddressHash(), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
- else if (prevout.scriptPubKey.IsPayToCryptoCondition()) {
- vector<unsigned char> hashBytes(prevout.scriptPubKey.begin(), prevout.scriptPubKey.end());
- CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
- CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
- mapAddress.insert(make_pair(key, delta));
- inserted.push_back(key);
- } }
+ }
}
- for (unsigned int k = 0; k < tx.vout.size(); k++) {
- const CTxOut &out = tx.vout[k];
- if (out.scriptPubKey.IsPayToScriptHash()) {
- vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
- CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0);
- mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
- inserted.push_back(key);
- }
- else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
- vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
- std::pair<addressDeltaMap::iterator,bool> ret;
- CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0);
- mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
- inserted.push_back(key);
- }
- else if (out.scriptPubKey.IsPayToPublicKey()) {
- vector<unsigned char> hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34);
- std::pair<addressDeltaMap::iterator,bool> ret;
- CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, k, 0);
- mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
- inserted.push_back(key);
+ for (unsigned int j = 0; j < tx.vout.size(); j++) {
+ const CTxOut &out = tx.vout[j];
+
+ COptCCParams p;
+ if (out.scriptPubKey.IsPayToCryptoCondition(p))
+ {
+ std::vector<CTxDestination> dests;
+ if (p.IsValid())
+ {
+ dests = p.GetDestinations();
+ }
+ else
+ {
+ dests = out.scriptPubKey.GetDestinations();
+ }
+
+ uint32_t nHeight = chainActive.Height();
+ std::map<uint160, uint32_t> heightOffsets = p.GetIndexHeightOffsets(nHeight);
+
+ for (auto dest : dests)
+ {
+ if (dest.which() != COptCCParams::ADDRTYPE_INVALID)
+ {
+ uint160 destID = GetDestinationID(dest);
+ if (!(dest.which() == COptCCParams::ADDRTYPE_INDEX && heightOffsets.count(destID) && heightOffsets[destID] > nHeight))
+ {
+ CMempoolAddressDeltaKey key(AddressTypeFromDest(dest), GetDestinationID(dest), txhash, j, 0);
+ mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
+ inserted.push_back(key);
+ }
+ }
+ }
}
- else if (out.scriptPubKey.IsPayToCryptoCondition()) {
- vector<unsigned char> hashBytes(out.scriptPubKey.begin(), out.scriptPubKey.end());
- std::pair<addressDeltaMap::iterator,bool> ret;
- CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, k, 0);
+ else
+ {
+ CScript::ScriptType type = out.scriptPubKey.GetType();
+ if (type == CScript::UNKNOWN)
+ continue;
+
+ CMempoolAddressDeltaKey key(type, out.scriptPubKey.AddressHash(), txhash, j, 0);
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
inserted.push_back(key);
}
}
-
mapAddressInserted.insert(make_pair(txhash, inserted));
}
-bool CTxMemPool::getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
- std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results)
+bool CTxMemPool::getAddressIndex(const std::vector<std::pair<uint160, int> > &addresses, std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results)
{
LOCK(cs);
- for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
- addressDeltaMap::iterator ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first));
+ for (std::vector<std::pair<uint160, int> >::const_iterator it = addresses.begin(); it != addresses.end(); it++) {
+ auto ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first));
while (ait != mapAddress.end() && (*ait).first.addressBytes == (*it).first && (*ait).first.type == (*it).second) {
results.push_back(*ait);
ait++;
bool CTxMemPool::removeAddressIndex(const uint256 txhash)
{
LOCK(cs);
- addressDeltaMapInserted::iterator it = mapAddressInserted.find(txhash);
+ auto it = mapAddressInserted.find(txhash);
if (it != mapAddressInserted.end()) {
std::vector<CMempoolAddressDeltaKey> keys = (*it).second;
void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view)
{
LOCK(cs);
-
const CTransaction& tx = entry.GetTx();
+ uint256 txhash = tx.GetHash();
std::vector<CSpentIndexKey> inserted;
- uint256 txhash = tx.GetHash();
for (unsigned int j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(input);
- uint160 addressHash;
- int addressType;
-
- if (prevout.scriptPubKey.IsPayToScriptHash()) {
- addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
- addressType = 2;
- }
- else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
- addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
- addressType = 1;
- }
- else if (prevout.scriptPubKey.IsPayToPublicKey()) {
- addressHash = Hash160(vector<unsigned char> (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34));
- addressType = 1;
- }
- else if (prevout.scriptPubKey.IsPayToCryptoCondition()) {
- addressHash = Hash160(vector<unsigned char> (prevout.scriptPubKey.begin(), prevout.scriptPubKey.end()));
- addressType = 1;
- }
- else {
- addressHash.SetNull();
- addressType = 0;
- }
-
CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n);
- CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, addressType, addressHash);
-
+ CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue,
+ prevout.scriptPubKey.GetType(),
+ prevout.scriptPubKey.AddressHash());
mapSpent.insert(make_pair(key, value));
inserted.push_back(key);
-
}
-
mapSpentInserted.insert(make_pair(txhash, inserted));
}
-bool CTxMemPool::getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value)
+bool CTxMemPool::getSpentIndex(const CSpentIndexKey &key, CSpentIndexValue &value)
{
LOCK(cs);
- mapSpentIndex::iterator it;
-
- it = mapSpent.find(key);
+ std::map<CSpentIndexKey, CSpentIndexValue, CSpentIndexKeyCompare>::iterator it = mapSpent.find(key);
if (it != mapSpent.end()) {
value = it->second;
return true;
bool CTxMemPool::removeSpentIndex(const uint256 txhash)
{
LOCK(cs);
- mapSpentIndexInserted::iterator it = mapSpentInserted.find(txhash);
+ auto it = mapSpentInserted.find(txhash);
if (it != mapSpentInserted.end()) {
std::vector<CSpentIndexKey> keys = (*it).second;
txToRemove.push_back(it->second.ptx->GetHash());
}
}
+ mapRecentlyAddedTx.erase(hash);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
- BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) {
+ BOOST_FOREACH(const JSDescription& joinsplit, tx.vJoinSplit) {
BOOST_FOREACH(const uint256& nf, joinsplit.nullifiers) {
mapSproutNullifiers.erase(nf);
}
mapTx.erase(hash);
nTransactionsUpdated++;
minerPolicyEstimator->removeTx(hash);
- removeAddressIndex(hash);
- removeSpentIndex(hash);
+ if (fAddressIndex)
+ removeAddressIndex(hash);
+ if (fSpentIndex)
+ removeSpentIndex(hash);
ClearPrioritisation(tx.GetHash());
}
}
const CTransaction& tx = it->GetTx();
switch (type) {
case SPROUT:
- BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) {
+ BOOST_FOREACH(const JSDescription& joinsplit, tx.vJoinSplit) {
if (joinsplit.anchor == invalidRoot) {
transactionsToRemove.push_back(tx);
break;
}
}
+bool CTxMemPool::checkNameConflicts(const CTransaction &tx, std::list<CTransaction> &conflicting)
+{
+ LOCK(cs);
+
+ // easy way to check if there are any transactions in the memory pool that define the name specified but are not the same as tx
+ conflicting.clear();
+
+ // first, be sure that this is a name definition. if so, it will have both a definition and reservation output. if it is a name definition,
+ // our only concern is whether or not there is a conflicting definition in the mempool. we assume that a check for any conflicting definition
+ // in the blockchain has already taken place.
+ CIdentity identity;
+ CNameReservation reservation;
+ for (auto output : tx.vout)
+ {
+ COptCCParams p;
+ if (output.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3)
+ {
+ if (p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() > 1)
+ {
+ if (identity.IsValid())
+ {
+ identity = CIdentity();
+ break;
+ }
+ else
+ {
+ identity = CIdentity(p.vData[0]);
+ }
+ }
+ else if (p.evalCode == EVAL_IDENTITY_RESERVATION && p.vData.size() > 1)
+ {
+ if (reservation.IsValid())
+ {
+ reservation = CNameReservation();
+ break;
+ }
+ else
+ {
+ reservation = CNameReservation(p.vData[0]);
+ }
+ }
+ }
+ }
+
+ // it can't conflict if it's not a definition
+ if (!(identity.IsValid() && reservation.IsValid()))
+ {
+ return false;
+ }
+
+ std::vector<std::pair<uint160, int>> addresses = std::vector<std::pair<uint160, int>>({{identity.GetID(), CScript::P2ID}});
+ std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> results;
+ if (mempool.getAddressIndex(addresses, results) && results.size())
+ {
+ std::map<uint256, std::pair<CTransaction, int>> txesAndSources; // first hash is transaction of input of prior identity or commitment output in the mempool, second pair is tx and ID output num if identity
+
+ uint256 txHash = tx.GetHash();
+ CNameReservation conflictingRes;
+ for (auto r : results)
+ {
+ if (r.first.txhash == txHash)
+ {
+ continue;
+ }
+ CTransaction mpTx;
+ if (lookup(r.first.txhash, mpTx))
+ {
+ COptCCParams p;
+ if (mpTx.vout[r.first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
+ p.IsValid() &&
+ p.evalCode == EVAL_IDENTITY_RESERVATION &&
+ p.vData.size() > 1 &&
+ (conflictingRes = CNameReservation(p.vData[0])).IsValid() &&
+ CIdentity(mpTx).IsValid())
+ {
+ conflicting.push_back(mpTx);
+ }
+ }
+ }
+ }
+
+ return conflicting.size();
+}
+
void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
{
+ LOCK(cs);
+
+ // names are enforced as unique without requiring related spends.
+ // if this is a definition that conflicts with an existing, unrelated name definition, remove the
+ // definition that exists in the mempool
+ std::list<CTransaction> conflicting;
+ if (checkNameConflicts(tx, conflicting))
+ {
+ for (auto &remTx : conflicting)
+ {
+ remove(remTx, removed, true);
+ }
+ }
+
// Remove transactions which depend on inputs of tx, recursively
list<CTransaction> result;
- LOCK(cs);
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);
if (it != mapNextTx.end()) {
}
}
- BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
+ BOOST_FOREACH(const JSDescription &joinsplit, tx.vJoinSplit) {
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
std::map<uint256, const CTransaction*>::iterator it = mapSproutNullifiers.find(nf);
if (it != mapSproutNullifiers.end()) {
void CTxMemPool::removeExpired(unsigned int nBlockHeight)
{
CBlockIndex *tipindex;
- // Remove expired txs from the mempool
+ // Remove expired txs and leftover coinbases from the mempool
LOCK(cs);
list<CTransaction> transactionsToRemove;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++)
{
const CTransaction& tx = it->GetTx();
tipindex = chainActive.LastTip();
- if (IsExpiredTx(tx, nBlockHeight) || (ASSETCHAINS_SYMBOL[0] == 0 && tipindex != 0 && komodo_validate_interest(tx,tipindex->GetHeight()+1,tipindex->GetMedianTimePast() + 777,0)) < 0)
+ if (tx.IsCoinBase() || IsExpiredTx(tx, nBlockHeight))
{
transactionsToRemove.push_back(tx);
}
boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
- BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
+ BOOST_FOREACH(const JSDescription &joinsplit, tx.vJoinSplit) {
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
assert(!pcoins->GetNullifier(nf, SPROUT));
}
deltas.first += dPriorityDelta;
deltas.second += nFeeDelta;
}
- LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
+ if (fDebug)
+ {
+ LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
+ }
}
void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta)
if (txDesc.IsValid())
{
mapReserveTransactions[hash] = txDesc;
- PrioritiseTransaction(hash, hash.GetHex().c_str(), currencyState.ReserveToNative(txDesc.ReserveFees()), currencyState.ReserveToNative(txDesc.ReserveFees()));
+ CAmount feeDelta = txDesc.AllFeesAsNative(currencyState);
+ PrioritiseTransaction(hash, hash.GetHex().c_str(), (double)feeDelta * 100.0, feeDelta);
return true;
}
return false;
}
}
+void CTxMemPool::NotifyRecentlyAdded()
+{
+ uint64_t recentlyAddedSequence;
+ std::vector<CTransaction> txs;
+ {
+ LOCK(cs);
+ recentlyAddedSequence = nRecentlyAddedSequence;
+ for (const auto& kv : mapRecentlyAddedTx) {
+ txs.push_back(*(kv.second));
+ }
+ mapRecentlyAddedTx.clear();
+ }
+
+ // A race condition can occur here between these SyncWithWallets calls, and
+ // the ones triggered by block logic (in ConnectTip and DisconnectTip). It
+ // is harmless because calling SyncWithWallets(_, NULL) does not alter the
+ // wallet transaction's block information.
+ for (auto tx : txs) {
+ try {
+ SyncWithWallets(tx, NULL);
+ } catch (const boost::thread_interrupted&) {
+ throw;
+ } catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "CTxMemPool::NotifyRecentlyAdded()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "CTxMemPool::NotifyRecentlyAdded()");
+ }
+ }
+
+ // Update the notified sequence number. We only need this in regtest mode,
+ // and should not lock on cs after calling SyncWithWallets otherwise.
+ if (Params().NetworkIDString() == "regtest") {
+ LOCK(cs);
+ nNotifiedSequence = recentlyAddedSequence;
+ }
+}
+
+bool CTxMemPool::IsFullyNotified() {
+ assert(Params().NetworkIDString() == "regtest");
+ LOCK(cs);
+ return nRecentlyAddedSequence == nNotifiedSequence;
+}
+
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, ShieldedType type) const