cleanup transaction replacement return codes
This commit is contained in:
@@ -17,6 +17,7 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
|
|||||||
static const unsigned char REJECT_DUST = 0x41;
|
static const unsigned char REJECT_DUST = 0x41;
|
||||||
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
||||||
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
||||||
|
static const unsigned char REJECT_HAVEBETTER = 0x44;
|
||||||
|
|
||||||
/** Capture information about block/transaction validation */
|
/** Capture information about block/transaction validation */
|
||||||
class CValidationState {
|
class CValidationState {
|
||||||
|
|||||||
23
src/main.cpp
23
src/main.cpp
@@ -1181,7 +1181,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxReplacementPoolResult rpr = RP_NotReplace;
|
CTxReplacementPoolResult rpr = RP_NotReplaceable;
|
||||||
|
|
||||||
{
|
{
|
||||||
CCoinsView dummy;
|
CCoinsView dummy;
|
||||||
@@ -1339,22 +1339,31 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
if ( komodo_is_notarytx(tx) == 0 )
|
if ( komodo_is_notarytx(tx) == 0 )
|
||||||
KOMODO_ON_DEMAND++;
|
KOMODO_ON_DEMAND++;
|
||||||
|
|
||||||
if (fAcceptReplacement) {
|
if (fAcceptReplacement)
|
||||||
|
{
|
||||||
CTxReplacementPoolItem item(tx, GetHeight());
|
CTxReplacementPoolItem item(tx, GetHeight());
|
||||||
if (SetReplacementParams(item)) {
|
if (SetReplacementParams(item)) {
|
||||||
rpr = replacementPool.replace(item);
|
rpr = replacementPool.replace(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rpr == RP_Invalid) {
|
if (rpr == RP_HaveBetter)
|
||||||
|
{
|
||||||
// already have a better one
|
// already have a better one
|
||||||
// TODO: Shouldn't neccesary log this as invalid, not sure if punishing peers is the best idea
|
|
||||||
fprintf(stderr,"accept failure.20\n");
|
fprintf(stderr,"accept failure.20\n");
|
||||||
return error("AcceptToMemoryPool: Replacement is worse %s", hash.ToString());
|
return state.DoS(0, error("AcceptToMemoryPool: Replacement is worse"), REJECT_HAVEBETTER, "replacement-is-worse");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rpr == RP_Invalid)
|
||||||
|
{
|
||||||
|
// Not valid according to replaceability rules
|
||||||
|
fprintf(stderr,"accept failure.21\n");
|
||||||
|
return state.DoS(0, error("AcceptToMemoryPool: Replacement is invalid"), REJECT_INVALID, "replacement-is-worse");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no replacement action happening...
|
// If there is no replacement action happening...
|
||||||
if (rpr == RP_NotReplace) {
|
if (rpr == RP_NotReplaceable)
|
||||||
|
{
|
||||||
// Store transaction in memory
|
// Store transaction in memory
|
||||||
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
||||||
}
|
}
|
||||||
@@ -1362,7 +1371,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
|
|
||||||
// in order for replaceable transactions to sync with wallet, replacementpool should advise
|
// in order for replaceable transactions to sync with wallet, replacementpool should advise
|
||||||
// wallet of transaction eviction
|
// wallet of transaction eviction
|
||||||
if (rpr == RP_NotReplace) {
|
if (rpr == RP_NotReplaceable) {
|
||||||
SyncWithWallets(tx, NULL);
|
SyncWithWallets(tx, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,11 @@
|
|||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "coins.h"
|
|
||||||
#include "replacementpool.h"
|
#include "replacementpool.h"
|
||||||
|
|
||||||
|
|
||||||
CTxReplacementPool replacementPool;
|
CTxReplacementPool replacementPool;
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a transaction to the pool, with a priority.
|
|
||||||
* Return true if valid, false if not valid. */
|
|
||||||
bool ValidateReplacementPoolItem(CTxReplacementPoolItem item)
|
|
||||||
{
|
|
||||||
// Perform some validations.
|
|
||||||
if (item.tx.vin.size() > 1) {
|
|
||||||
// Replaceable transactions with multiple inputs are disabled for now. It's not yet clear
|
|
||||||
// what edge cases may arise. It is speculated that it will "just work", since if
|
|
||||||
// replaceable transactions spend multiple outputs using the replacement protocol,
|
|
||||||
// they will never conflict in the replaceMap data structure. But for now, to be prudent, disable.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A transaction with 0 priority is not valid.
|
|
||||||
if (item.priority == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validate the item
|
* Validate the item
|
||||||
*
|
*
|
||||||
@@ -45,7 +17,17 @@ bool ValidateReplacementPoolItem(CTxReplacementPoolItem item)
|
|||||||
*/
|
*/
|
||||||
CTxReplacementPoolResult CTxReplacementPool::replace(CTxReplacementPoolItem &item)
|
CTxReplacementPoolResult CTxReplacementPool::replace(CTxReplacementPoolItem &item)
|
||||||
{
|
{
|
||||||
if (!ValidateReplacementPoolItem(item)) {
|
AssertLockHeld(cs_main);
|
||||||
|
|
||||||
|
// Perform some validations.
|
||||||
|
if (item.tx.vin.size() > 1) {
|
||||||
|
// Replaceable transactions with multiple inputs are disabled.
|
||||||
|
// It seems like quite alot of additional complexity.
|
||||||
|
return RP_Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A transaction with 0 priority is not valid.
|
||||||
|
if (item.priority == 0) {
|
||||||
return RP_Invalid;
|
return RP_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,22 +35,25 @@ CTxReplacementPoolResult CTxReplacementPool::replace(CTxReplacementPoolItem &ite
|
|||||||
|
|
||||||
if (it != replaceMap.end()) {
|
if (it != replaceMap.end()) {
|
||||||
if (it->second.priority >= item.priority) {
|
if (it->second.priority >= item.priority) {
|
||||||
// Already have a transaction with equal or greater priority; this is not valid
|
return RP_HaveBetter; // (ThanksThough)
|
||||||
return RP_Invalid;
|
|
||||||
}
|
}
|
||||||
// copy the previous starting block over
|
|
||||||
item.startBlock = it->second.startBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This transaction has higher priority
|
// This transaction has higher priority
|
||||||
replaceMap[item.tx.vin[0].prevout] = item;
|
replaceMap[item.tx.vin[0].prevout] = item;
|
||||||
|
replaceMap[item.tx.vin[0].prevout].startBlock = it->second.startBlock;
|
||||||
|
|
||||||
return RP_Valid;
|
return RP_Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove and return any spends that have matured
|
||||||
|
*/
|
||||||
void CTxReplacementPool::removePending(int height, std::vector<CTransaction> &txs)
|
void CTxReplacementPool::removePending(int height, std::vector<CTransaction> &txs)
|
||||||
{
|
{
|
||||||
|
AssertLockHeld(cs_main);
|
||||||
|
|
||||||
for (auto it = replaceMap.begin(); it != replaceMap.end(); /**/) {
|
for (auto it = replaceMap.begin(); it != replaceMap.end(); /**/) {
|
||||||
CTxReplacementPoolItem &rep = it->second;
|
CTxReplacementPoolItem &rep = it->second;
|
||||||
if (rep.GetTargetBlock() <= height) {
|
if (rep.GetTargetBlock() <= height) {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
#ifndef KOMODO_REPLACEMENTCACHE_H
|
#ifndef KOMODO_REPLACEMENTCACHE_H
|
||||||
#define KOMODO_REPLACEMENTCACHE_H
|
#define KOMODO_REPLACEMENTCACHE_H
|
||||||
|
|
||||||
#include "coins.h"
|
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
|
||||||
|
|
||||||
enum CTxReplacementPoolResult { RP_NotReplace, RP_Valid, RP_Invalid };
|
enum CTxReplacementPoolResult { RP_Accepted, RP_HaveBetter, RP_Invalid, RP_NotReplaceable };
|
||||||
|
|
||||||
|
|
||||||
class CTxReplacementPoolItem
|
class CTxReplacementPoolItem
|
||||||
{
|
{
|
||||||
@@ -28,41 +28,43 @@ public:
|
|||||||
replacementWindow = 0;
|
replacementWindow = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetTargetBlock()
|
int GetTargetBlock() { return startBlock + replacementWindow; }
|
||||||
{
|
|
||||||
return startBlock + replacementWindow;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CTxReplacementPool stores valid-according-to-the-current-best-chain (??? do we need to do this?)
|
* CTxReplacementPool stores transactions that are valid but held for
|
||||||
* transactions that are valid but held for period of time during which
|
* period of time during which they may be replaced.
|
||||||
* they may be replaced.
|
|
||||||
*
|
*
|
||||||
* Transactions are added when they are found to be valid but not added
|
* Transactions are added when they are found to be valid but not added
|
||||||
* to the mempool until a timeout.
|
* to the mempool until a timeout.
|
||||||
*
|
*
|
||||||
* Replacement pool is like another mempool before the main mempool.
|
* Replacement pool is like another mempool before the main mempool.
|
||||||
|
*
|
||||||
|
* Transactions in the replacement pool are indexed by the output
|
||||||
|
* that they are spending. Once a replaceable transaction tries to
|
||||||
|
* spend an output, a countdown of blocks begins at the current block
|
||||||
|
* plus a window that is set by "userland" code. If another, better
|
||||||
|
* transaction replaces the spend that's already pending, the countdown
|
||||||
|
* start block remains the same.
|
||||||
*/
|
*/
|
||||||
class CTxReplacementPool
|
class CTxReplacementPool
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// A potential replacement is first stored here, not in the replaceMap.
|
|
||||||
// This is in case some other checks fail, during AcceptToMemoryPool.
|
|
||||||
// Later on, if all checks pass, processReplacement() is called.
|
|
||||||
|
|
||||||
/* Index of spends that may be replaced */
|
/* Index of spends that may be replaced */
|
||||||
std::map<COutPoint, CTxReplacementPoolItem> replaceMap;
|
std::map<COutPoint, CTxReplacementPoolItem> replaceMap;
|
||||||
public:
|
public:
|
||||||
|
/* Try to replace a transaction in the index */
|
||||||
CTxReplacementPoolResult replace(CTxReplacementPoolItem &item);
|
CTxReplacementPoolResult replace(CTxReplacementPoolItem &item);
|
||||||
|
|
||||||
// Remove and return all transactions up to a given block height.
|
/* Remove and return all transactions up to a given block height */
|
||||||
void removePending(int height, std::vector<CTransaction> &txs);
|
void removePending(int height, std::vector<CTransaction> &txs);
|
||||||
|
|
||||||
|
/* Find a transaction in the index by it's hash. */
|
||||||
bool lookup(uint256 txHash, CTransaction &tx);
|
bool lookup(uint256 txHash, CTransaction &tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Global instance */
|
||||||
extern CTxReplacementPool replacementPool;
|
extern CTxReplacementPool replacementPool;
|
||||||
|
|
||||||
#endif // KOMODO_REPLACEMENTCACHE_H
|
#endif // KOMODO_REPLACEMENTCACHE_H
|
||||||
|
|||||||
Reference in New Issue
Block a user