mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 15:50:07 +01:00
Merge branch '0.4.x' into 0.5.0.x
Conflicts: src/main.cpp
This commit is contained in:
294
src/main.cpp
294
src/main.cpp
@@ -381,15 +381,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
||||
if ((int64)nLockTime > INT_MAX)
|
||||
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
|
||||
|
||||
// Safety limits
|
||||
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
||||
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
|
||||
// attacks disallow transactions with more than one SigOp per 34 bytes.
|
||||
// 34 bytes because a TxOut is:
|
||||
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
|
||||
if (GetSigOpCount() > nSize / 34 || nSize < 100)
|
||||
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
|
||||
|
||||
// Rather not work on nonstandard transactions (unless -testnet)
|
||||
if (!fTestNet && !IsStandard())
|
||||
return error("AcceptToMemoryPool() : nonstandard transaction type");
|
||||
@@ -433,17 +424,29 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
||||
|
||||
if (fCheckInputs)
|
||||
{
|
||||
// Check against previous transactions
|
||||
MapPrevTx mapInputs;
|
||||
map<uint256, CTxIndex> mapUnused;
|
||||
int64 nFees = 0;
|
||||
bool fInvalid = false;
|
||||
if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, 0, fInvalid))
|
||||
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
|
||||
{
|
||||
if (fInvalid)
|
||||
return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
|
||||
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||
if (pfMissingInputs)
|
||||
*pfMissingInputs = true;
|
||||
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
|
||||
// Safety limits
|
||||
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
||||
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
|
||||
// attacks disallow transactions with more than one SigOp per 34 bytes.
|
||||
// 34 bytes because a TxOut is:
|
||||
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
|
||||
if (GetSigOpCount() > nSize / 34 || nSize < 100)
|
||||
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
|
||||
|
||||
int64 nFees = GetValueIn(mapInputs)-GetValueOut();
|
||||
|
||||
// Don't accept it if it can't get into a block
|
||||
if (nFees < GetMinFee(1000, true, true))
|
||||
return error("AcceptToMemoryPool() : not enough fees");
|
||||
@@ -472,6 +475,13 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
||||
dFreeCount += nSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Check against previous transactions
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
|
||||
{
|
||||
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Store transaction in memory
|
||||
@@ -875,9 +885,8 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
|
||||
}
|
||||
|
||||
|
||||
bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee,
|
||||
bool& fInvalid)
|
||||
bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
|
||||
bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid)
|
||||
{
|
||||
// FetchInputs can return false either because we just haven't seen some inputs
|
||||
// (in which case the transaction should be stored as an orphan)
|
||||
@@ -885,6 +894,118 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
// be dropped). If tx is definitely invalid, fInvalid will be set to true.
|
||||
fInvalid = false;
|
||||
|
||||
if (IsCoinBase())
|
||||
return true; // Coinbase transactions have no inputs to fetch.
|
||||
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
COutPoint prevout = vin[i].prevout;
|
||||
if (inputsRet.count(prevout.hash))
|
||||
continue; // Got it already
|
||||
|
||||
// Read txindex
|
||||
CTxIndex& txindex = inputsRet[prevout.hash].first;
|
||||
bool fFound = true;
|
||||
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
|
||||
{
|
||||
// Get txindex from current proposed changes
|
||||
txindex = mapTestPool.find(prevout.hash)->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read txindex from txdb
|
||||
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
|
||||
}
|
||||
if (!fFound && (fBlock || fMiner))
|
||||
return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
|
||||
// Read txPrev
|
||||
CTransaction& txPrev = inputsRet[prevout.hash].second;
|
||||
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
|
||||
{
|
||||
// Get prev tx from single transactions in memory
|
||||
CRITICAL_BLOCK(cs_mapTransactions)
|
||||
{
|
||||
if (!mapTransactions.count(prevout.hash))
|
||||
return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
txPrev = mapTransactions[prevout.hash];
|
||||
}
|
||||
if (!fFound)
|
||||
txindex.vSpent.resize(txPrev.vout.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get prev tx from disk
|
||||
if (!txPrev.ReadFromDisk(txindex.pos))
|
||||
return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all prevout.n's are valid:
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
const COutPoint prevout = vin[i].prevout;
|
||||
assert(inputsRet.count(prevout.hash) != 0);
|
||||
const CTxIndex& txindex = inputsRet[prevout.hash].first;
|
||||
const CTransaction& txPrev = inputsRet[prevout.hash].second;
|
||||
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
|
||||
{
|
||||
// Revisit this if/when transaction replacement is implemented and allows
|
||||
// adding inputs:
|
||||
fInvalid = true;
|
||||
return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
|
||||
{
|
||||
MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
|
||||
if (mi == inputs.end())
|
||||
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
|
||||
|
||||
const CTransaction& txPrev = (mi->second).second;
|
||||
if (input.prevout.n >= txPrev.vout.size())
|
||||
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");
|
||||
|
||||
return txPrev.vout[input.prevout.n];
|
||||
}
|
||||
|
||||
int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const
|
||||
{
|
||||
if (IsCoinBase())
|
||||
return 0;
|
||||
|
||||
int64 nResult = 0;
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
nResult += GetOutputFor(vin[i], inputs).nValue;
|
||||
}
|
||||
return nResult;
|
||||
|
||||
}
|
||||
|
||||
int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
|
||||
{
|
||||
if (IsCoinBase())
|
||||
return 0;
|
||||
|
||||
int nSigOps = 0;
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
const CTxOut& prevout = GetOutputFor(vin[i], inputs);
|
||||
if (prevout.scriptPubKey.IsPayToScriptHash())
|
||||
nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
|
||||
}
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
bool CTransaction::ConnectInputs(MapPrevTx inputs,
|
||||
map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
|
||||
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
|
||||
{
|
||||
// Take over previous transactions' spent pointers
|
||||
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
|
||||
// fMiner is true when called from the internal bitcoin miner
|
||||
@@ -892,69 +1013,23 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
if (!IsCoinBase())
|
||||
{
|
||||
int64 nValueIn = 0;
|
||||
int64 nFees = 0;
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
COutPoint prevout = vin[i].prevout;
|
||||
|
||||
// Read txindex
|
||||
CTxIndex txindex;
|
||||
bool fFound = true;
|
||||
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
|
||||
{
|
||||
// Get txindex from current proposed changes
|
||||
txindex = mapTestPool[prevout.hash];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read txindex from txdb
|
||||
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
|
||||
}
|
||||
if (!fFound && (fBlock || fMiner))
|
||||
return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
|
||||
// Read txPrev
|
||||
CTransaction txPrev;
|
||||
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
|
||||
{
|
||||
// Get prev tx from single transactions in memory
|
||||
CRITICAL_BLOCK(cs_mapTransactions)
|
||||
{
|
||||
if (!mapTransactions.count(prevout.hash))
|
||||
return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
txPrev = mapTransactions[prevout.hash];
|
||||
}
|
||||
if (!fFound)
|
||||
txindex.vSpent.resize(txPrev.vout.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get prev tx from disk
|
||||
if (!txPrev.ReadFromDisk(txindex.pos))
|
||||
return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
assert(inputs.count(prevout.hash) > 0);
|
||||
CTxIndex& txindex = inputs[prevout.hash].first;
|
||||
CTransaction& txPrev = inputs[prevout.hash].second;
|
||||
|
||||
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
|
||||
{
|
||||
// Revisit this if/when transaction replacement is implemented and allows
|
||||
// adding inputs:
|
||||
fInvalid = true;
|
||||
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
|
||||
}
|
||||
|
||||
// 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)
|
||||
for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
|
||||
if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
|
||||
return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
|
||||
|
||||
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
|
||||
// before the last blockchain checkpoint. This is safe because block merkle hashes are
|
||||
// still computed and checked, and any change will be caught at the next checkpoint.
|
||||
if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
|
||||
// Verify signature
|
||||
if (!VerifySignature(txPrev, *this, i))
|
||||
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
|
||||
|
||||
// Check for conflicts (double-spend)
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
@@ -966,6 +1041,23 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
||||
return DoS(100, error("ConnectInputs() : txin values out of range"));
|
||||
|
||||
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
|
||||
// before the last blockchain checkpoint. This is safe because block merkle hashes are
|
||||
// still computed and checked, and any change will be caught at the next checkpoint.
|
||||
if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
|
||||
{
|
||||
// Verify signature
|
||||
if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
|
||||
{
|
||||
// only during transition phase for P2SH: do not invoke anti-DoS code for
|
||||
// potentially old clients relaying bad P2SH transactions
|
||||
if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
|
||||
return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
|
||||
|
||||
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark outpoints as spent
|
||||
txindex.vSpent[prevout.n] = posThisTx;
|
||||
|
||||
@@ -983,24 +1075,11 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
int64 nTxFee = nValueIn - GetValueOut();
|
||||
if (nTxFee < 0)
|
||||
return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
|
||||
if (nTxFee < nMinFee)
|
||||
return false;
|
||||
nFees += nTxFee;
|
||||
if (!MoneyRange(nFees))
|
||||
return DoS(100, error("ConnectInputs() : nFees out of range"));
|
||||
}
|
||||
|
||||
if (fBlock)
|
||||
{
|
||||
// Add transaction to changes
|
||||
mapTestPool[GetHash()] = CTxIndex(posThisTx, vout.size());
|
||||
}
|
||||
else if (fMiner)
|
||||
{
|
||||
// Add transaction to test pool
|
||||
mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1105,7 @@ bool CTransaction::ClientConnectInputs()
|
||||
return false;
|
||||
|
||||
// Verify signature
|
||||
if (!VerifySignature(txPrev, *this, i))
|
||||
if (!VerifySignature(txPrev, *this, i, true, 0))
|
||||
return error("ConnectInputs() : VerifySignature failed");
|
||||
|
||||
///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
|
||||
@@ -1099,20 +1178,51 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
|
||||
return false;
|
||||
}
|
||||
|
||||
// P2SH didn't become active until Apr 1 2012 (Feb 15 on testnet)
|
||||
int64 nEvalSwitchTime = fTestNet ? 1329264000 : 1333238400;
|
||||
bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime);
|
||||
|
||||
//// issue here: it doesn't know the version
|
||||
unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size());
|
||||
|
||||
map<uint256, CTxIndex> mapQueuedChanges;
|
||||
int64 nFees = 0;
|
||||
int nSigOps = 0;
|
||||
BOOST_FOREACH(CTransaction& tx, vtx)
|
||||
{
|
||||
nSigOps += tx.GetSigOpCount();
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return DoS(100, error("ConnectBlock() : too many sigops"));
|
||||
|
||||
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
|
||||
nTxPos += ::GetSerializeSize(tx, SER_DISK);
|
||||
|
||||
bool fInvalid;
|
||||
if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false, 0, fInvalid))
|
||||
return false;
|
||||
MapPrevTx mapInputs;
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
|
||||
return false;
|
||||
|
||||
if (fStrictPayToScriptHash)
|
||||
{
|
||||
// Add in sigops done by pay-to-script-hash inputs;
|
||||
// this is to prevent a "rogue miner" from creating
|
||||
// an incredibly-expensive-to-validate block.
|
||||
nSigOps += tx.GetP2SHSigOpCount(mapInputs);
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return DoS(100, error("ConnectBlock() : too many sigops"));
|
||||
}
|
||||
|
||||
nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
|
||||
|
||||
if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
|
||||
return false;
|
||||
}
|
||||
|
||||
mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size());
|
||||
}
|
||||
|
||||
// Write queued txindex changes
|
||||
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
|
||||
{
|
||||
@@ -2916,6 +3026,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
|
||||
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
|
||||
continue;
|
||||
|
||||
// Legacy limits on sigOps:
|
||||
int nTxSigOps = tx.GetSigOpCount();
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
@@ -2928,14 +3040,28 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
// because we're already processing them in order of dependency
|
||||
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
|
||||
bool fInvalid;
|
||||
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee, fInvalid))
|
||||
MapPrevTx mapInputs;
|
||||
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
|
||||
continue;
|
||||
|
||||
int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
|
||||
if (nTxFees < nMinFee)
|
||||
continue;
|
||||
|
||||
nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
|
||||
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
|
||||
continue;
|
||||
mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
|
||||
swap(mapTestPool, mapTestPoolTmp);
|
||||
|
||||
// Added
|
||||
pblock->vtx.push_back(tx);
|
||||
nBlockSize += nTxSize;
|
||||
nBlockSigOps += nTxSigOps;
|
||||
nFees += nTxFees;
|
||||
|
||||
// Add transactions that depend on this one to the priority queue
|
||||
uint256 hash = tx.GetHash();
|
||||
|
||||
53
src/main.h
53
src/main.h
@@ -388,6 +388,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
|
||||
|
||||
|
||||
//
|
||||
@@ -500,6 +501,15 @@ public:
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Count ECDSA signature operations in pay-to-script-hash inputs.
|
||||
This is a better measure of how expensive it is to process this transaction.
|
||||
|
||||
@param[in] mapInputsMap of previous transactions that have outputs we're spending
|
||||
@return maximum number of sigops required to validate this transaction's inputs
|
||||
@see CTransaction::FetchInputs
|
||||
*/
|
||||
int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
|
||||
|
||||
bool IsStandard() const
|
||||
{
|
||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||
@@ -523,6 +533,16 @@ public:
|
||||
return nValueOut;
|
||||
}
|
||||
|
||||
/** Amount of bitcoins coming in to this transaction
|
||||
Note that lightweight clients may not know anything besides the hash of previous transactions,
|
||||
so may not be able to calculate this.
|
||||
|
||||
@param[in] mapInputsMap of previous transactions that have outputs we're spending
|
||||
@returnSum of value of all inputs (scriptSigs)
|
||||
@see CTransaction::FetchInputs
|
||||
*/
|
||||
int64 GetValueIn(const MapPrevTx& mapInputs) const;
|
||||
|
||||
static bool AllowFree(double dPriority)
|
||||
{
|
||||
// Large (in bytes) low-priority (new, small-coin) transactions
|
||||
@@ -637,14 +657,41 @@ public:
|
||||
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
|
||||
bool ReadFromDisk(COutPoint prevout);
|
||||
bool DisconnectInputs(CTxDB& txdb);
|
||||
bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee,
|
||||
bool& fInvalid);
|
||||
|
||||
/** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
|
||||
|
||||
@param[in] txdb Transaction database
|
||||
@param[in] mapTestPool List of pending changes to the transaction index database
|
||||
@param[in] fBlock True if being called to add a new best-block to the chain
|
||||
@param[in] fMiner True if being called by CreateNewBlock
|
||||
@param[out] inputsRet Pointers to this transaction's inputs
|
||||
@param[out] fInvalid returns true if transaction is invalid
|
||||
@return Returns true if all inputs are in txdb or mapTestPool
|
||||
*/
|
||||
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
|
||||
bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
|
||||
|
||||
/** Sanity check previous transactions, then, if all checks succeed,
|
||||
mark them as spent by this transaction.
|
||||
|
||||
@param[in] inputsPrevious transactions (from FetchInputs)
|
||||
@param[out] mapTestPoolKeeps track of inputs that need to be updated on disk
|
||||
@param[in] posThisTxPosition of this transaction on disk
|
||||
@param[in] pindexBlock
|
||||
@param[in] fBlock true if called from ConnectBlock
|
||||
@param[in] fMiner true if called from CreateNewBlock
|
||||
@param[in] fStrictPayToScriptHash true if fully validating p2sh transactions
|
||||
@return Returns true if all checks succeed
|
||||
*/
|
||||
bool ConnectInputs(MapPrevTx inputs,
|
||||
std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
|
||||
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
|
||||
bool ClientConnectInputs();
|
||||
bool CheckTransaction() const;
|
||||
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
||||
bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
||||
protected:
|
||||
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
|
||||
bool AddToMemoryPoolUnchecked();
|
||||
public:
|
||||
bool RemoveFromMemoryPool();
|
||||
|
||||
@@ -1147,16 +1147,40 @@ bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBit
|
||||
}
|
||||
|
||||
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
|
||||
bool fValidatePayToScriptHash, int nHashType)
|
||||
{
|
||||
vector<vector<unsigned char> > stack;
|
||||
vector<vector<unsigned char> > stack, stackCopy;
|
||||
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
|
||||
return false;
|
||||
if (fValidatePayToScriptHash)
|
||||
stackCopy = stack;
|
||||
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
|
||||
return false;
|
||||
if (stack.empty())
|
||||
return false;
|
||||
return CastToBool(stack.back());
|
||||
|
||||
if (CastToBool(stack.back()) == false)
|
||||
return false;
|
||||
|
||||
// Additional validation for spend-to-script-hash transactions:
|
||||
if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash())
|
||||
{
|
||||
if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only
|
||||
return false; // or validation fails
|
||||
|
||||
const valtype& pubKeySerialized = stackCopy.back();
|
||||
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
|
||||
popstack(stackCopy);
|
||||
|
||||
if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType))
|
||||
return false;
|
||||
if (stackCopy.empty())
|
||||
return false;
|
||||
return CastToBool(stackCopy.back());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1178,14 +1202,14 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
|
||||
|
||||
// Test solution
|
||||
if (scriptPrereq.empty())
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0))
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, true, 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
const CTxIn& txin = txTo.vin[nIn];
|
||||
@@ -1196,8 +1220,65 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
|
||||
if (txin.prevout.hash != txFrom.GetHash())
|
||||
return false;
|
||||
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CScript::GetSigOpCount(bool fAccurate) const
|
||||
{
|
||||
int n = 0;
|
||||
const_iterator pc = begin();
|
||||
opcodetype lastOpcode = OP_INVALIDOPCODE;
|
||||
while (pc < end())
|
||||
{
|
||||
opcodetype opcode;
|
||||
if (!GetOp(pc, opcode))
|
||||
break;
|
||||
if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY)
|
||||
n++;
|
||||
else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY)
|
||||
{
|
||||
if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16)
|
||||
n += DecodeOP_N(lastOpcode);
|
||||
else
|
||||
n += 20;
|
||||
}
|
||||
lastOpcode = opcode;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int CScript::GetSigOpCount(const CScript& scriptSig) const
|
||||
{
|
||||
if (!IsPayToScriptHash())
|
||||
return GetSigOpCount(true);
|
||||
|
||||
// This is a pay-to-script-hash scriptPubKey;
|
||||
// get the last item that the scriptSig
|
||||
// pushes onto the stack:
|
||||
const_iterator pc = scriptSig.begin();
|
||||
vector<unsigned char> data;
|
||||
while (pc < scriptSig.end())
|
||||
{
|
||||
opcodetype opcode;
|
||||
if (!scriptSig.GetOp(pc, opcode, data))
|
||||
return 0;
|
||||
if (opcode > OP_16)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// ... and return it's opcount:
|
||||
CScript subscript(data.begin(), data.end());
|
||||
return subscript.GetSigOpCount(true);
|
||||
}
|
||||
|
||||
bool CScript::IsPayToScriptHash() const
|
||||
{
|
||||
// Extra-fast test for pay-to-script-hash CScripts:
|
||||
return (this->size() == 23 &&
|
||||
this->at(0) == OP_HASH160 &&
|
||||
this->at(1) == 0x14 &&
|
||||
this->at(22) == OP_EQUAL);
|
||||
}
|
||||
|
||||
45
src/script.h
45
src/script.h
@@ -574,6 +574,14 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode/decode small integers:
|
||||
static int DecodeOP_N(opcodetype opcode)
|
||||
{
|
||||
if (opcode == OP_0)
|
||||
return 0;
|
||||
assert(opcode >= OP_1 && opcode <= OP_16);
|
||||
return (int)opcode - (int)(OP_1 - 1);
|
||||
}
|
||||
|
||||
void FindAndDelete(const CScript& b)
|
||||
{
|
||||
@@ -588,25 +596,28 @@ public:
|
||||
}
|
||||
while (GetOp(pc, opcode));
|
||||
}
|
||||
|
||||
|
||||
int GetSigOpCount() const
|
||||
int Find(opcodetype op) const
|
||||
{
|
||||
int n = 0;
|
||||
const_iterator pc = begin();
|
||||
while (pc < end())
|
||||
{
|
||||
opcodetype opcode;
|
||||
if (!GetOp(pc, opcode))
|
||||
break;
|
||||
if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY)
|
||||
n++;
|
||||
else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY)
|
||||
n += 20;
|
||||
}
|
||||
return n;
|
||||
int nFound = 0;
|
||||
opcodetype opcode;
|
||||
for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
|
||||
if (opcode == op)
|
||||
++nFound;
|
||||
return nFound;
|
||||
}
|
||||
|
||||
// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
|
||||
// as 20 sigops. With pay-to-script-hash, that changed:
|
||||
// CHECKMULTISIGs serialized in scriptSigs are
|
||||
// counted more accurately, assuming they are of the form
|
||||
// ... OP_N CHECKMULTISIG ...
|
||||
int GetSigOpCount(bool fAccurate=false) const;
|
||||
|
||||
// Accurately count sigOps, including sigOps in
|
||||
// pay-to-script-hash transactions:
|
||||
int GetSigOpCount(const CScript& scriptSig) const;
|
||||
|
||||
bool IsPayToScriptHash() const;
|
||||
|
||||
bool IsPushOnly() const
|
||||
{
|
||||
@@ -698,6 +709,6 @@ bool IsStandard(const CScript& scriptPubKey);
|
||||
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
||||
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
|
||||
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0);
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user