Skip to content

OP_SECURETHEBAG Code Review #4

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

Closed
wants to merge 12 commits into from
Closed
13 changes: 13 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008

consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].bit = 25;
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nStartTime = 1577836800; // January 1, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nTimeout = 1609459200; // January 1, 2021

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000051dc8b82f450202ecb3d471");

Expand Down Expand Up @@ -188,6 +192,11 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008


consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].bit = 25;
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000007dbe94253893cbd463");

Expand Down Expand Up @@ -272,6 +281,10 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;

consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].bit = 25;
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_SECURETHEBAG].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");

Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
DEPLOYMENT_SECURETHEBAG,
MAX_VERSION_BITS_DEPLOYMENTS
};

Expand Down
10 changes: 10 additions & 0 deletions src/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>

#include <string>

inline uint32_t ROTL32(uint32_t x, int8_t r)
{
Expand Down Expand Up @@ -77,3 +78,12 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he
num[3] = (nChild >> 0) & 0xFF;
CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output);
}

CHashWriter TaggedHash(const std::string& tag)
{
CHashWriter writer(SER_GETHASH, 0);
uint256 taghash;
CSHA256().Write((unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
writer << taghash << taghash;
return writer;
}
26 changes: 21 additions & 5 deletions src/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <uint256.h>
#include <version.h>

#include <string>
#include <vector>

typedef uint256 ChainCode;
Expand Down Expand Up @@ -117,7 +118,7 @@ inline uint160 Hash160(const prevector<N, unsigned char>& vch)
class CHashWriter
{
private:
CHash256 ctx;
CSHA256 ctx;

const int nType;
const int nVersion;
Expand All @@ -135,17 +136,24 @@ class CHashWriter
// invalidates the object
uint256 GetHash() {
uint256 result;
ctx.Finalize((unsigned char*)&result);
ctx.Finalize(result.begin());
ctx.Reset().Write(result.begin(), CSHA256::OUTPUT_SIZE).Finalize(result.begin());
return result;
}

// invalidates the object
uint256 GetSHA256() {
uint256 result;
ctx.Finalize(result.begin());
return result;
}

/**
* Returns the first 64 bits from the resulting hash.
*/
inline uint64_t GetCheapHash() {
unsigned char result[CHash256::OUTPUT_SIZE];
ctx.Finalize(result);
return ReadLE64(result);
uint256 result = GetHash();
return ReadLE64(result.begin());
}

template<typename T>
Expand Down Expand Up @@ -204,4 +212,12 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char

void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);

/** Return a CHashWriter primed for computing bip-schnorr compatible tagged hashes.
*
* The returned object will have SHA256(tag) written to it twice (= 64 bytes).
* A tagged hash can be computed by feeding the message into this object, and
* then calling CHashWriter::GetSHA256().
*/
CHashWriter TaggedHash(const std::string& tag);

#endif // BITCOIN_HASH_H
2 changes: 2 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) {
return false;
}
} else if (whichType == TX_SECURETHEBAG) {
if (tx.vin[i].scriptSig.size() != 0) return false;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
SCRIPT_VERIFY_CONST_SCRIPTCODE;
SCRIPT_VERIFY_CONST_SCRIPTCODE |
SCRIPT_VERIFY_SECURETHEBAG;

/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
Expand Down
48 changes: 48 additions & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <stdint.h>
#include <amount.h>
#include <hash.h>
#include <script/script.h>
#include <serialize.h>
#include <uint256.h>
Expand Down Expand Up @@ -263,6 +264,53 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
s << tx.nLockTime;
}

template <class T>
uint256 GetPrevoutSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.prevout;
}
return ss.GetSHA256();
}

template <class T>
uint256 GetSequenceSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.nSequence;
}
return ss.GetSHA256();
}

template <class T>
uint256 GetOutputsSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : txTo.vout) {
ss << txout;
}
return ss.GetSHA256();
}

template<typename TxType>
uint256 GetSecuredBagHash(const TxType& tx) {
return GetSecuredBagHash(tx, GetOutputsSHA256(tx), GetSequenceSHA256(tx));
}

static const CHashWriter BagHash = TaggedHash("BagHash");
template<typename TxType>
uint256 GetSecuredBagHash(const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash) {
auto h = CHashWriter(BagHash)
<< tx.nVersion << tx.nLockTime
<< outputs_hash << sequences_hash
<< uint64_t(tx.vin.size());
for (const auto& in : tx.vin) {
h << in.scriptSig;
}
return h.GetSHA256();
}

/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
Expand Down
82 changes: 48 additions & 34 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/script.h>
#include <uint256.h>
Expand Down Expand Up @@ -457,7 +458,26 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
break;
}

case OP_NOP1: case OP_NOP4: case OP_NOP5:
case OP_SECURETHEBAG:
{
// when not enabled; treat as a NOP4
if (!(flags & SCRIPT_VERIFY_SECURETHEBAG)) break;
// Read one step ahead; we expect to push 32 bytes
CScript::const_iterator tmp_pc = pc;
opcodetype push_op;
if (!script.GetOp(tmp_pc, push_op, vchPushValue)) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
// If the push was not 32 bytes, ignore it:
// upgrade can add semantics for this opcode with different length args
if (vchPushValue.size() != 32) break;
// push_op should be minimal, i.e. 0x20 -- will fail later anyways
if (!CheckMinimalPush(vchPushValue, push_op)) return set_error(serror, SCRIPT_ERR_MINIMALDATA);
// Check the BagHash computed from the transaction matches the literal value
if (!checker.CheckBagSecured(vchPushValue)) return set_error(serror, SCRIPT_ERR_BAG_NOT_SECURED);
// Success
break;
}

case OP_NOP1: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
Expand Down Expand Up @@ -1182,34 +1202,12 @@ class CTransactionSignatureSerializer
}
};

template <class T>
uint256 GetPrevoutHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.prevout;
}
return ss.GetHash();
}

template <class T>
uint256 GetSequenceHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.nSequence;
}
return ss.GetHash();
void RehashSHA256(uint256& hash) {
CSHA256().Write(hash.begin(), 32).Finalize(hash.begin());
}

template <class T>
uint256 GetOutputsHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : txTo.vout) {
ss << txout;
}
return ss.GetHash();
uint256 RehashSHA256(uint256&& hash) {
CSHA256().Write(hash.begin(), 32).Finalize(hash.begin());
return hash;
}

} // namespace
Expand All @@ -1219,9 +1217,13 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
// Cache is calculated only for transactions with witness
if (txTo.HasWitness()) {
hashPrevouts = GetPrevoutHash(txTo);
hashSequence = GetSequenceHash(txTo);
hashOutputs = GetOutputsHash(txTo);
hashOutputs = GetOutputsSHA256(txTo);
hashSequence = GetSequenceSHA256(txTo);
hashBag = GetSecuredBagHash(txTo, hashOutputs, hashSequence);
hashPrevouts = GetPrevoutSHA256(txTo);
RehashSHA256(hashOutputs);
RehashSHA256(hashSequence);
RehashSHA256(hashPrevouts);
ready = true;
}
}
Expand All @@ -1242,16 +1244,16 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
const bool cacheready = cache && cache->ready;

if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
hashPrevouts = cacheready ? cache->hashPrevouts : RehashSHA256(GetPrevoutSHA256(txTo));
}

if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
hashSequence = cacheready ? cache->hashSequence : RehashSHA256(GetSequenceSHA256(txTo));
}


if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
hashOutputs = cacheready ? cache->hashOutputs : RehashSHA256(GetOutputsSHA256(txTo));
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn];
Expand Down Expand Up @@ -1412,6 +1414,18 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
return true;
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckBagSecured(const std::vector<unsigned char>& hash) const
{
// Should already be checked before calling...
assert(hash.size() == 32);
if (txdata && txdata->ready) {
return std::equal(txdata->hashBag.begin(), txdata->hashBag.end(), hash.data());
}
assert(txTo != nullptr);
uint256 hashBag = GetSecuredBagHash(*txTo);
return std::equal(hashBag.begin(), hashBag.end(), hash.data());
}
// explicit instantiation
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
Expand Down
12 changes: 11 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,17 @@ enum
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
//
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),

// support OP_SECURETHEBAG
//
SCRIPT_VERIFY_SECURETHEBAG = (1U << 17),
};

bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);

struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
uint256 hashPrevouts, hashSequence, hashOutputs, hashBag;
bool ready = false;

template <class T>
Expand Down Expand Up @@ -159,6 +163,11 @@ class BaseSignatureChecker
return false;
}

virtual bool CheckBagSecured(const std::vector<unsigned char>& hash) const
{
return false;
}

virtual ~BaseSignatureChecker() {}
};

Expand All @@ -180,6 +189,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
bool CheckBagSecured(const std::vector<unsigned char>& hash) const override;
};

using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
Expand Down
7 changes: 7 additions & 0 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
return subscript.GetSigOpCount(true);
}

bool CScript::IsBasicSecureTheBag() const
{
// Extra-fast test for pay-to-script-hash CScripts:
return (this->size() == 34 &&
(*this)[0] == OP_SECURETHEBAG &&
(*this)[1] == 0x20);
}
bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:
Expand Down
Loading