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

Block ancient dust spam #140

Open
wants to merge 3 commits into
base: namecoinq
Choose a base branch
from
Open
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
20 changes: 19 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ CTxIndex::GetContainingBlock (const CDiskTxPos& pos)

// Read block header
CBlock block;
if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false))
if (!block.ReadFromDisk (pos.nFile, pos.nBlockPos, false))
return NULL;

// Find the block in the index
Expand Down Expand Up @@ -1164,6 +1164,15 @@ CTransaction::ConnectInputs (DatabaseSet& dbset,
assert (vin[i].fHasPrevInfo);
assert (txPrev);

/* Calculate the previous output's height. Note that the value
here should be ensured to be accurate, since IsUnspendable
below depends on it for block validation! Thus we do not
use the cache introduced for priority calculation. */
int prevHeight = txindex.GetHeight ();
if (prevHeight == -1)
prevHeight = pindexBlock->nHeight;
assert (prevHeight >= 0);

if (prevout.n >= txPrev->vout.size ())
return error ("ConnectInputs: %s prevout.n out of range"
" %d %d prev tx %s\n%s",
Expand All @@ -1180,6 +1189,15 @@ CTransaction::ConnectInputs (DatabaseSet& dbset,
return error ("ConnectInputs: %s VerifySignature failed",
GetHash ().ToString ().substr (0, 10).c_str ());

/* Check for unspendable outputs. This is redundant for some
of the checks done in IsUnspendable, but it also takes care
of blocking dust spam. If not validating blocks (i. e., miner
or mempool), we are strict and block dust even before
the hardfork point. */
if (IsUnspendable (txPrev->vout[prevout.n], prevHeight,
pindexBlock->nHeight, !fBlock))
return error ("ConnectInputs: previous txo is unspendable");

// If prev is coinbase, check that it's matured
if (txPrev->IsCoinBase ())
for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
Expand Down
13 changes: 13 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ extern CHooks* hooks;
class CReserveKey;
class CTxDB;
class CTxIndex;
class CTxOut;

void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
Expand Down Expand Up @@ -141,6 +142,14 @@ std::string GetWarnings(std::string strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock /*, bool fAllowSlow = false*/ );

/* Check if a transaction is unspendable. Either because it is provably
prunable, or because it is explicitly blocked due to some rule. nHeight
is the height at which (or later) it will be spent, and nPrevHeight
is the one where it is included in the block chain. The strict parameter
can be turned on for mempool validation (as opposed to blocks) to
reject transactions from the mempool even before a hardfork. */
bool IsUnspendable (const CTxOut& txo, int nPrevHeight,
int nHeight, bool strict);



Expand Down Expand Up @@ -942,6 +951,10 @@ class CTxIndex
static int GetHeight (const CDiskTxPos& pos);
static int GetDepthInMainChain (const CDiskTxPos& pos);

inline int GetHeight () const
{
return GetHeight (pos);
}
inline int GetDepthInMainChain () const
{
return GetDepthInMainChain (pos);
Expand Down
41 changes: 41 additions & 0 deletions src/namecoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ template<typename T> void ConvertTo(Value& value, bool fAllowNull=false);
static const int BUG_WORKAROUND_BLOCK_START = 139750; // Bug was not exploited before block 139872, so skip checking earlier blocks
static const int BUG_WORKAROUND_BLOCK = 150000; // Point of hard fork

/* Softfork point where the dustspam block was introduced. */
static const int FORK_HEIGHT_DUSTSPAM = 300000;

map<vector<unsigned char>, uint256> mapMyNames;
map<vector<unsigned char>, set<uint256> > mapNamePending;

Expand Down Expand Up @@ -1933,6 +1936,44 @@ int64 GetNameNetFee(const CTransaction& tx)
return nFee;
}

bool
IsUnspendable (const CTxOut& txo, int nPrevHeight, int nHeight, bool strict)
{
assert (nPrevHeight <= nHeight);

/* An OP_RETURN script as per network fee payments is always
unspendable. Check this. */
const CScript& script = txo.scriptPubKey;
if (script.size () == 1 && script[0] == OP_RETURN)
return true;

/* Block dust spam outputs, so that they can be removed from the UTXO
set. We block all 1-Swartz outputs created between blocks 39k and 41k.
Blocking takes effect at the softfork height. */
if (txo.nValue == 1 && nPrevHeight >= 39000 && nPrevHeight <= 41000
&& (strict || nHeight >= FORK_HEIGHT_DUSTSPAM))
return true;

/* A name_update or name_firstupdate is unspendable if expired. */
if (nHeight >= nPrevHeight + GetExpirationDepth (nHeight))
{
int op;
std::vector<vchType> vvch;
if (DecodeNameScript (script, op, vvch))
switch (op)
{
case OP_NAME_FIRSTUPDATE:
case OP_NAME_UPDATE:
return true;

default:
break;
}
}

return false;
}

bool GetValueOfNameTx(const CTransaction& tx, vector<unsigned char>& value)
{
vector<vector<unsigned char> > vvch;
Expand Down