From 0f62abe4dda014777452ea0b45822351b9aea942 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Wed, 17 Jul 2019 17:33:25 +0900 Subject: [PATCH 01/17] add witness commitment section support to primitive/block This also moves GetWitnessCommitmentIndex out of validation.cpp into CBlock, with 3 lines affected (GetWitnessCommitmentIndex(block) -> block.GetWitnessCommitmentIndex()). --- src/primitives/block.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ src/primitives/block.h | 44 ++++++++++++++++++++++++++++ src/validation.cpp | 19 ++---------- src/validation.h | 3 -- 4 files changed, 109 insertions(+), 19 deletions(-) diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 0c84ed6da22e..98c93e986219 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -28,3 +28,65 @@ std::string CBlock::ToString() const } return s.str(); } + +bool CBlock::GetWitnessCommitmentSection(const uint8_t header[4], std::vector& result) const +{ + int cidx = GetWitnessCommitmentIndex(); + if (cidx == -1) return false; + auto script = vtx.at(0)->vout.at(cidx).scriptPubKey; + opcodetype opcode; + CScript::const_iterator pc = script.begin(); + ++pc; // move beyond initial OP_RETURN + while (script.GetOp(pc, opcode, result)) { + if (result.size() > 3 && !memcmp(result.data(), header, 4)) { + result.erase(result.begin(), result.begin() + 4); + return true; + } + } + result.clear(); + return false; +} + +bool CBlock::SetWitnessCommitmentSection(CMutableTransaction& mtx, const uint8_t header[4], const std::vector& data) +{ + int cidx = GetWitnessCommitmentIndex(mtx); + if (cidx == -1) return false; + + CScript result; + std::vector pushdata; + auto script = mtx.vout[cidx].scriptPubKey; + opcodetype opcode; + CScript::const_iterator pc = script.begin(); + result.push_back(*pc++); + bool found = false; + while (script.GetOp(pc, opcode, pushdata)) { + if (pushdata.size() > 0) { + if (pushdata.size() > 3 && !memcmp(pushdata.data(), header, 4)) { + // replace pushdata + found = true; + pushdata.erase(pushdata.begin() + 4, pushdata.end()); + pushdata.insert(pushdata.end(), data.begin(), data.end()); + } + result << pushdata; + } else { + result << opcode; + } + } + if (!found) { + // append section as it did not exist + pushdata.clear(); + pushdata.insert(pushdata.end(), header, header + 4); + pushdata.insert(pushdata.end(), data.begin(), data.end()); + result << pushdata; + } + mtx.vout[cidx].scriptPubKey = result; + return true; +} + +bool CBlock::SetWitnessCommitmentSection(const uint8_t header[4], const std::vector& data) +{ + auto mtx = CMutableTransaction(*vtx[0]); + if (!SetWitnessCommitmentSection(mtx, header, data)) return false; + vtx[0] = std::make_shared(mtx); + return true; +} diff --git a/src/primitives/block.h b/src/primitives/block.h index 750d42efbc23..23a375556502 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -116,6 +116,50 @@ class CBlock : public CBlockHeader return block; } + /** + * Get the vout index of the segwit commitment in the coinbase transaction of this block. + * + * Returns -1 if no witness commitment was found. + */ + inline int GetWitnessCommitmentIndex() const + { + return vtx.empty() ? -1 : GetWitnessCommitmentIndex(*vtx.at(0)); + } + + /** + * Get the vout index of the segwit commitment in the given coinbase transaction. + * + * Returns -1 if no witness commitment was found. + */ + template static inline int GetWitnessCommitmentIndex(const T& coinbase) { + for (int64_t o = coinbase.vout.size() - 1; o > -1; --o) { + auto vospk = coinbase.vout[o].scriptPubKey; + if (vospk.size() >= 38 && vospk[0] == OP_RETURN && vospk[1] == 0x24 && vospk[2] == 0xaa && vospk[3] == 0x21 && vospk[4] == 0xa9 && vospk[5] == 0xed) { + return o; + } + } + return -1; + } + + /** + * Attempt to get the data for the section with the given header in the witness commitment of this block. + * + * Returns false if header was not found. The data (excluding the 4 byte header) is written into result if found. + */ + bool GetWitnessCommitmentSection(const uint8_t header[4], std::vector& result) const; + + /** + * Attempt to add or update the data for the section with the given header in the witness commitment of this block. + * + * This operation may fail and return false, if no witness commitment exists upon call time. Returns true on success. + */ + bool SetWitnessCommitmentSection(const uint8_t header[4], const std::vector& data); + + /** + * The tx based equivalent of the above. + */ + static bool SetWitnessCommitmentSection(CMutableTransaction& tx, const uint8_t header[4], const std::vector& data); + std::string ToString() const; }; diff --git a/src/validation.cpp b/src/validation.cpp index 8f5d3331704d..9db40bd514df 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3335,22 +3335,9 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa return (height >= params.SegwitHeight); } -int GetWitnessCommitmentIndex(const CBlock& block) -{ - int commitpos = -1; - if (!block.vtx.empty()) { - for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) { - if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) { - commitpos = o; - } - } - } - return commitpos; -} - void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) { - int commitpos = GetWitnessCommitmentIndex(block); + int commitpos = block.GetWitnessCommitmentIndex(); static const std::vector nonce(32, 0x00); if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) { CMutableTransaction tx(*block.vtx[0]); @@ -3363,7 +3350,7 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) { std::vector commitment; - int commitpos = GetWitnessCommitmentIndex(block); + int commitpos = block.GetWitnessCommitmentIndex(); std::vector ret(32, 0x00); if (consensusParams.SegwitHeight != std::numeric_limits::max()) { if (commitpos == -1) { @@ -3503,7 +3490,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat // multiple, the last one is used. bool fHaveWitness = false; if (nHeight >= consensusParams.SegwitHeight) { - int commitpos = GetWitnessCommitmentIndex(block); + int commitpos = block.GetWitnessCommitmentIndex(); if (commitpos != -1) { bool malleated = false; uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); diff --git a/src/validation.h b/src/validation.h index 54f97e721325..5e555e5856d4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -382,9 +382,6 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa /** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main); -/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */ -int GetWitnessCommitmentIndex(const CBlock& block); - /** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); From 1fd487727541540a308bcccd28a90a287428c7da Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Wed, 17 Jul 2019 17:38:43 +0900 Subject: [PATCH 02/17] add simple signature support (checker/creator) The simple signature takes a sighash as argument and verifies a signature and pubkey against it. It is used in signet for verifying blocks, but can be used for practically anything. --- src/script/interpreter.cpp | 14 ++++++++++++++ src/script/interpreter.h | 12 ++++++++++++ src/script/sign.cpp | 9 +++++++++ src/script/sign.h | 11 +++++++++++ 4 files changed, 46 insertions(+) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index ad833bc025eb..6ce7d16725d8 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1416,6 +1416,20 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; +bool SimpleSignatureChecker::CheckSig(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const +{ + CPubKey pubkey(vchPubKey); + if (!pubkey.IsValid()) return false; + + // Hash type is one byte tacked on to the end of the signature + std::vector vchSig(vchSigIn); + if (vchSig.empty()) return false; + // int nHashType = vchSig.back(); + vchSig.pop_back(); + + return pubkey.Verify(hash, vchSig); +} + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { std::vector > stack; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index d63d8b85b7b4..06fc3d1f85e2 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -161,6 +161,18 @@ class BaseSignatureChecker virtual ~BaseSignatureChecker() {} }; +/** A general purpose signature checker. */ +class SimpleSignatureChecker : public BaseSignatureChecker +{ +private: + uint256 hash; + +public: + const uint256& GetHash() const { return hash; } + explicit SimpleSignatureChecker(const uint256& hash_in) : hash(hash_in) {} + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; +}; + template class GenericTransactionSignatureChecker : public BaseSignatureChecker { diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 0ed92e8d5b08..11b035812cce 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -33,6 +33,15 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid return true; } +bool SimpleSignatureCreator::CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const +{ + CKey key; + if (!provider.GetKey(keyid, key)) return false; + if (!key.Sign(checker.GetHash(), vchSig)) return false; + vchSig.push_back((unsigned char)SIGHASH_ALL); + return true; +} + static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script) { if (provider.GetCScript(scriptid, script)) { diff --git a/src/script/sign.h b/src/script/sign.h index 4c2403f83f3f..930c26a76375 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -31,6 +31,17 @@ class BaseSignatureCreator { virtual bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; }; +/** A general purpose signature creator. */ +class SimpleSignatureCreator : public BaseSignatureCreator +{ + SimpleSignatureChecker checker; + +public: + explicit SimpleSignatureCreator(const uint256& hashIn) : BaseSignatureCreator(), checker(hashIn) {}; + const BaseSignatureChecker& Checker() const override { return checker; } + bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; +}; + /** A signature creator for transactions. */ class MutableTransactionSignatureCreator : public BaseSignatureCreator { const CMutableTransaction* txTo; From 58ab450de1bd8710e8b389fc06cdd6058af4254d Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Wed, 17 Jul 2019 17:41:32 +0900 Subject: [PATCH 03/17] add signet basic support (signet.cpp) --- src/Makefile.am | 2 ++ src/consensus/params.h | 6 +++++ src/signet.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++ src/signet.h | 43 +++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 src/signet.cpp create mode 100644 src/signet.h diff --git a/src/Makefile.am b/src/Makefile.am index 27c87688b42e..d4516d06efea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -195,6 +195,7 @@ BITCOIN_CORE_H = \ script/signingprovider.h \ script/standard.h \ shutdown.h \ + signet.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ @@ -484,6 +485,7 @@ libbitcoin_common_a_SOURCES = \ script/sign.cpp \ script/signingprovider.cpp \ script/standard.cpp \ + signet.cpp \ versionbitsinfo.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) diff --git a/src/consensus/params.h b/src/consensus/params.h index e191fd6d26bd..ef149d9daca6 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -80,6 +80,12 @@ struct Params { int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; uint256 defaultAssumeValid; + + /** + * If true, witness commitments contain a payload equal to a Bitcoin Script solution + * to a signet challenge as defined in the chain params. + */ + bool signet_blocks{false}; }; } // namespace Consensus diff --git a/src/signet.cpp b/src/signet.cpp new file mode 100644 index 000000000000..e6b8d32186cd --- /dev/null +++ b/src/signet.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include