OP_CHECKBLOCKATHEIGHT anti-replay (BIP 115; logic only) #10391

Open
wants to merge 5 commits into
from
View
@@ -613,7 +613,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
UpdateTransaction(mergedTx, i, sigdata);
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
fComplete = false;
}
View
@@ -5,6 +5,10 @@
#include "chain.h"
+#include "tinyformat.h"
+
+#include <stdexcept>
+
/**
* CChain implementation
*/
@@ -66,6 +70,28 @@ CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
return (lower == vChain.end() ? NULL : *lower);
}
+void CHistoricalChain::SetHeight(const int nHeight)
+{
+ if (nHeight > chain.Height()) {
+ throw std::runtime_error(strprintf("%s: Cannot set a height beyond parent!", __func__));
+ }
+ my_height = nHeight;
+}
+
+void CHistoricalChain::SetTip(CBlockIndex *pindex)
+{
+ throw std::runtime_error("Cannot SetTip of a CHistoricalChain!");
+}
+
+CBlockIndex* CHistoricalChain::FindEarliestAtLeast(int64_t nTime) const
+{
+ CBlockIndex * const pblockindex = chain.FindEarliestAtLeast(nTime);
+ if (pblockindex->nHeight > Height()) {
+ return NULL;
+ }
+ return pblockindex;
+}
+
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }
View
@@ -436,25 +436,26 @@ class CChain {
public:
/** Returns the index entry for the genesis block of this chain, or NULL if none. */
CBlockIndex *Genesis() const {
- return vChain.size() > 0 ? vChain[0] : NULL;
+ return (*this)[0];
}
/** Returns the index entry for the tip of this chain, or NULL if none. */
CBlockIndex *Tip() const {
- return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL;
+ const int nHeight = Height();
+ return nHeight >= 0 ? (*this)[nHeight] : NULL;
}
/** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */
- CBlockIndex *operator[](int nHeight) const {
- if (nHeight < 0 || nHeight >= (int)vChain.size())
+ virtual CBlockIndex *operator[](int nHeight) const {
+ if (nHeight < 0 || nHeight > Height()) {
return NULL;
+ }
return vChain[nHeight];
}
/** Compare two chains efficiently. */
friend bool operator==(const CChain &a, const CChain &b) {
- return a.vChain.size() == b.vChain.size() &&
- a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
+ return a.Tip() == b.Tip();
}
/** Efficiently check whether a block is present in this chain. */
@@ -471,12 +472,12 @@ class CChain {
}
/** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */
- int Height() const {
+ virtual int Height() const {
return vChain.size() - 1;
}
/** Set/initialize a chain with a given tip. */
- void SetTip(CBlockIndex *pindex);
+ virtual void SetTip(CBlockIndex *pindex);
/** Return a CBlockLocator that refers to a block in this chain (by default the tip). */
CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const;
@@ -485,6 +486,32 @@ class CChain {
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
/** Find the earliest block with timestamp equal or greater than the given. */
+ virtual CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
+};
+
+class CHistoricalChain : public CChain {
+private:
+ const CChain& chain;
+ int my_height;
+
+public:
+ CHistoricalChain() = delete;
+ CHistoricalChain(const CChain& chainIn, const int heightIn) : chain(chainIn), my_height(heightIn) { }
+
+ void SetHeight(int nHeight);
+
+ CBlockIndex *operator[](int nHeight) const {
+ if (nHeight > Height()) {
+ return NULL;
+ }
+ return chain[nHeight];
+ }
+
+ int Height() const {
+ return my_height;
+ }
+
+ void SetTip(CBlockIndex *pindex);
CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
};
View
@@ -51,7 +51,7 @@ static const unsigned int DUST_RELAY_TX_FEE = 1000;
* with. However scripts violating these flags may still be present in valid
* blocks and we must accept those blocks.
*/
-static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
+static const unsigned int STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_DERSIG |
SCRIPT_VERIFY_STRICTENC |
SCRIPT_VERIFY_MINIMALDATA |
@@ -65,10 +65,14 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_LOW_S |
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
+ SCRIPT_VERIFY_CHECKBLOCKATHEIGHT;
+
+/** For convenience, standard but not contextual verify flags. */
+static const unsigned int STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS & ~CONTEXTUAL_SCRIPT_VERIFY_FLAGS;
/** For convenience, standard but not mandatory verify flags. */
-static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
+static const unsigned int STANDARD_CONTEXTUAL_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
@@ -777,14 +777,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
// ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
if (txv.vin.size() > i) {
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount, nullptr), sigdata, DataFromTransaction(txv, i));
}
}
UpdateTransaction(mergedTx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, nullptr), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}
@@ -95,7 +95,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
set_error(err, bitcoinconsensus_ERR_OK);
PrecomputedTransactionData txdata(tx);
- return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL);
+ return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, nullptr, txdata), NULL);
} catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
}
@@ -55,6 +55,7 @@ enum
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141)
+ // NOTE: Do NOT add CHECKBLOCKATHEIGHT here without updating verify_script to pass context!
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG |
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY |
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS
View
@@ -5,6 +5,7 @@
#include "interpreter.h"
+#include "chain.h"
#include "primitives/transaction.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
@@ -424,7 +425,36 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
break;
}
- case OP_NOP1: case OP_NOP4: case OP_NOP5:
+ case OP_CHECKBLOCKATHEIGHT:
+ {
+ if (!(flags & SCRIPT_VERIFY_CHECKBLOCKATHEIGHT)) {
+ // not enabled; treat as a NOP5
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
+ }
+ break;
+ }
+
+ if (stack.size() < 2) {
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ }
+
+ // nHeight is a 32-bit signed integer field.
+ const int32_t nHeight = CScriptNum(stacktop(-1), true, 4).getint();
@aayanl

aayanl Jul 10, 2017

Can this cause a buffer overflow if a block height over 2147483647 occurs?

@luke-jr

luke-jr Jul 10, 2017

Member

No? Why would it?

@aayanl

aayanl Jul 10, 2017

nHeight is a int32_t, if someone makes a transaction with 2147483648 as the referenced block, it would cause an overflow

@luke-jr

luke-jr Jul 10, 2017

Member

Not all overflows are buffer overflows. In this case, CScriptNum explicitly limits the size.

+
+ valtype vchHashCheck = stacktop(-2);
+
+ // Compare the specified block hash with the input.
+ if (!checker.CheckBlockHash(nHeight, vchHashCheck)) {
+ // Not final rather than a hard reject to avoid caching across different blockchains
+ // Also because it will *eventually* become final when the height gets old enough
+ return set_error(serror, SCRIPT_ERR_NOT_FINAL);
+ }
+
+ break;
+ }
+
+ case OP_NOP1: case OP_NOP4:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
@@ -1351,6 +1381,28 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con
return true;
}
+bool TransactionSignatureChecker::CheckBlockHash(const int32_t nHeight, const std::vector<unsigned char>& vchCompareTo) const
+{
+ if (!chain) {
+ return false;
+ }
+
+ // If the chain doesn't reach the desired height yet, the transaction is non-final
+ if (nHeight > chain->Height()) {
+ return false;
+ }
+
+ // Sufficiently old blocks are always valid
+ if (nHeight <= chain->Height() - 52596) {
+ return true;
+ }
+
+ CBlockIndex* pblockindex = (*chain)[nHeight];
+ std::vector<unsigned char> vchBlockHash(pblockindex->GetBlockHash().begin(), pblockindex->GetBlockHash().end());
+ vchBlockHash.erase(vchBlockHash.begin(), vchBlockHash.end() - vchCompareTo.size());
+ return (vchCompareTo == vchBlockHash);
+}
+
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
std::vector<std::vector<unsigned char> > stack;
View
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <string>
+class CChain;
class CPubKey;
class CScript;
class CTransaction;
@@ -106,8 +107,13 @@ enum
// Public keys in segregated witness scripts must be compressed
//
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
+
+ // Enforce CHECKBLOCKATHEIGHT opcode
+ SCRIPT_VERIFY_CHECKBLOCKATHEIGHT = (1U << 16),
};
+static const unsigned int CONTEXTUAL_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_CHECKBLOCKATHEIGHT;
+
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
struct PrecomputedTransactionData
@@ -143,6 +149,11 @@ class BaseSignatureChecker
return false;
}
+ virtual bool CheckBlockHash(const int32_t nHeight, const std::vector<unsigned char>& nBlockHash) const
+ {
+ return false;
+ }
+
virtual ~BaseSignatureChecker() {}
};
@@ -152,17 +163,19 @@ class TransactionSignatureChecker : public BaseSignatureChecker
const CTransaction* txTo;
unsigned int nIn;
const CAmount amount;
+ const CChain* chain;
const PrecomputedTransactionData* txdata;
protected:
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
public:
- TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {}
- TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
+ TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), chain(chainIn), txdata(NULL) {}
+ TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), chain(chainIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
bool CheckLockTime(const CScriptNum& nLockTime) const;
bool CheckSequence(const CScriptNum& nSequence) const;
+ bool CheckBlockHash(const int32_t nHeight, const std::vector<unsigned char>& nBlockHash) const;
};
class MutableTransactionSignatureChecker : public TransactionSignatureChecker
@@ -171,7 +184,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker
const CTransaction txTo;
public:
- MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {}
+ MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn, nullptr), txTo(*txToIn) {}
};
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL);
View
@@ -132,7 +132,7 @@ const char* GetOpName(opcodetype opcode)
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY";
case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY";
case OP_NOP4 : return "OP_NOP4";
- case OP_NOP5 : return "OP_NOP5";
+ case OP_CHECKBLOCKATHEIGHT : return "OP_CHECKBLOCKATHEIGHT";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8";
View
@@ -170,7 +170,8 @@ enum opcodetype
OP_CHECKSEQUENCEVERIFY = 0xb2,
OP_NOP3 = OP_CHECKSEQUENCEVERIFY,
OP_NOP4 = 0xb3,
- OP_NOP5 = 0xb4,
+ OP_CHECKBLOCKATHEIGHT = 0xb4,
+ OP_NOP5 = OP_CHECKBLOCKATHEIGHT,
OP_NOP6 = 0xb5,
OP_NOP7 = 0xb6,
OP_NOP8 = 0xb7,
@@ -64,6 +64,8 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_UNEXPECTED,
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
+ SCRIPT_ERR_NOT_FINAL,
+
SCRIPT_ERR_ERROR_COUNT
} ScriptError;
View
@@ -46,7 +46,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
bool store;
public:
- CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
+ CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, chainIn, txdataIn), store(storeIn) {}
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
};
View
@@ -16,7 +16,7 @@
typedef std::vector<unsigned char> valtype;
-TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
+TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, nullptr) {}
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
@@ -184,7 +184,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
sigdata.scriptSig = PushAll(result);
// Test solution
- return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, creator.Checker());
}
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
Oops, something went wrong.