From 2845e7811e920a0994a9b4e208a6b849c0262335 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 31 May 2019 10:57:36 -0400 Subject: [PATCH 01/62] Define dynamic federations primitives --- src/primitives/block.cpp | 26 +++++++++ src/primitives/block.h | 110 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index fe72ff037f..707dc68d40 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -40,3 +40,29 @@ std::string CBlock::ToString() const } return s.str(); } + +uint256 ConsensusParamEntry::CalculateRoot() const +{ + if (IsNull()) { + return uint256(); + } + + std::vector leaves; + leaves.push_back(SerializeHash(m_signblockscript, SER_GETHASH, 0)); + leaves.push_back(SerializeHash(m_sbs_wit_limit, SER_GETHASH, 0)); + leaves.push_back(SerializeHash(m_fedpegscript, SER_GETHASH, 0)); + leaves.push_back(SerializeHash(m_extension_space, SER_GETHASH, 0)); + return ComputeFastMerkleRoot(leaves); +} + +uint256 DynaFedParams::CalculateRoot() const +{ + if (IsNull()) { + return uint256(); + } + + std::vector leaves; + leaves.push_back(m_current.CalculateRoot()); + leaves.push_back(m_proposed.CalculateRoot()); + return ComputeFastMerkleRoot(leaves); +} diff --git a/src/primitives/block.h b/src/primitives/block.h index 62aa50e644..4551200d74 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -52,6 +52,116 @@ class CProof std::string ToString() const; }; + +class ConsensusParamEntry +{ +public: + unsigned char m_serialize_type; // Determines how it is serialized, defaults to null + CScript m_signblockscript; + uint32_t m_sbs_wit_limit; // Max block signature witness serialized size + CScript m_fedpegscript; + // No consensus meaning to the particular bytes, currently we interpret as PAK keys, details in pak.h + std::vector> m_extension_space; + + // Each constructor sets its own serialization type implicitly based on which + // arguments are given + ConsensusParamEntry() { m_sbs_wit_limit = 0; m_serialize_type = 0; }; + ConsensusParamEntry(const CScript& signblockscript_in, const uint32_t sbs_wit_limit_in) : m_signblockscript(signblockscript_in), m_sbs_wit_limit(sbs_wit_limit_in) { m_serialize_type = 1; }; + ConsensusParamEntry(const CScript& signblockscript_in, const uint32_t sbs_wit_limit_in, const CScript& fedpegscript_in, const std::vector> extension_space_in) : m_signblockscript(signblockscript_in), m_sbs_wit_limit(sbs_wit_limit_in), m_fedpegscript(fedpegscript_in), m_extension_space(extension_space_in) { m_serialize_type = 2; }; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(m_serialize_type); + switch(m_serialize_type) { + case 0: + /* Null entry, used to signal "no vote" proposal */ + break; + case 1: + READWRITE(m_signblockscript); + READWRITE(m_sbs_wit_limit); + break; + case 2: + READWRITE(m_signblockscript); + READWRITE(m_sbs_wit_limit); + READWRITE(m_fedpegscript); + READWRITE(m_extension_space); + break; + default: + throw std::ios_base::failure("Invalid consensus parameter entry type"); + } + } + + uint256 CalculateRoot() const; + + bool IsNull() const + { + return m_serialize_type == 0 && + m_signblockscript.empty() && + m_sbs_wit_limit == 0 && + m_fedpegscript.empty() && + m_extension_space.empty(); + + } + + void SetNull() + { + m_serialize_type = 0; + m_signblockscript = CScript(); + m_sbs_wit_limit = 0; + m_fedpegscript = CScript(); + m_extension_space.clear(); + } + + bool operator==(const ConsensusParamEntry &other) const + { + return m_serialize_type == other.m_serialize_type && + m_signblockscript == other.m_signblockscript && + m_sbs_wit_limit == other.m_sbs_wit_limit && + m_fedpegscript == other.m_fedpegscript && + m_extension_space == other.m_extension_space; + } + bool operator!=(const ConsensusParamEntry &other) const + { + return !(*this == other); + } +}; + +class DynaFedParams +{ +public: + + // Currently enforced by network, not all fields may be known + ConsensusParamEntry m_current; + // Proposed rules for next epoch + ConsensusParamEntry m_proposed; + + DynaFedParams() {}; + DynaFedParams(const ConsensusParamEntry& current, const ConsensusParamEntry& proposed) : m_current(current), m_proposed(proposed) {}; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(m_current); + READWRITE(m_proposed); + } + + uint256 CalculateRoot() const; + + bool IsNull() const + { + return m_current.IsNull() && m_proposed.IsNull(); + } + + void SetNull() + { + m_current.SetNull(); + m_proposed.SetNull(); + } +}; + /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block From a74accf7d1862f074c4b73565853bb79e68dd45d Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 31 May 2019 10:58:23 -0400 Subject: [PATCH 02/62] Add dynamic federations blockheader serialization with HF bit --- src/primitives/block.h | 60 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/primitives/block.h b/src/primitives/block.h index 4551200d74..9c9203ed56 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -182,29 +182,66 @@ class CBlockHeader uint32_t block_height; uint32_t nBits; uint32_t nNonce; + // Only used pre-dynamic federation CProof proof; + // Dynamic federation: Subsumes the proof field + DynaFedParams m_dyna_params; + CScriptWitness m_signblock_witness; CBlockHeader() { SetNull(); } + // HF bit to detect dynamic federation blocks + static const uint32_t HF_MASK = 1 << 31; + ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - if (g_con_blockheightinheader) { - READWRITE(block_height); + const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS); + + // Detect dynamic federation block serialization using "HF bit", + // or the signed bit which is invalid in Bitcoin + bool is_dyna = false; + int32_t nVersion; + if (ser_action.ForRead()) { + READWRITE(nVersion); + is_dyna = nVersion < 0; + this->nVersion = ~HF_MASK & nVersion; + } else { + nVersion = this->nVersion; + if (!m_dyna_params.IsNull()) { + nVersion |= HF_MASK; + is_dyna = true; + } + READWRITE(nVersion); } - if (g_signed_blocks) { - READWRITE(proof); + + if (is_dyna) { + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(block_height); + READWRITE(m_dyna_params); + // We do not serialize witness for hashes, or weight calculation + if (!(s.GetType() & SER_GETHASH) && fAllowWitness) { + READWRITE(m_signblock_witness.stack); + } } else { - READWRITE(nBits); - READWRITE(nNonce); + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + if (g_con_blockheightinheader) { + READWRITE(block_height); + } + if (g_signed_blocks) { + READWRITE(proof); + } else { + READWRITE(nBits); + READWRITE(nNonce); + } } } @@ -223,7 +260,7 @@ class CBlockHeader bool IsNull() const { if (g_signed_blocks) { - return proof.IsNull(); + return proof.IsNull() && m_dyna_params.IsNull(); } else { return (nBits == 0); } @@ -284,6 +321,7 @@ class CBlock : public CBlockHeader block.nBits = nBits; block.nNonce = nNonce; block.proof = proof; + block.m_dyna_params = m_dyna_params; return block; } From 4e52f2c83cda9ffd2c44b9c29c8340a09adde562 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 31 May 2019 11:00:32 -0400 Subject: [PATCH 03/62] Add dynamic federation blockheader fields to chaindb serialization --- src/chain.h | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/chain.h b/src/chain.h index 21d6122909..ccb6fd3336 100644 --- a/src/chain.h +++ b/src/chain.h @@ -221,6 +221,9 @@ class CBlockIndex uint32_t nBits; uint32_t nNonce; CProof proof; + // Dynamic federation fields + DynaFedParams d_params; + CScriptWitness m_signblock_witness; //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; @@ -250,6 +253,8 @@ class CBlockIndex nBits = 0; nNonce = 0; proof.SetNull(); + d_params.SetNull(); + m_signblock_witness.SetNull(); } CBlockIndex() @@ -267,6 +272,8 @@ class CBlockIndex nBits = block.nBits; nNonce = block.nNonce; proof = block.proof; + d_params = block.m_dyna_params; + m_signblock_witness = block.m_signblock_witness; } CDiskBlockPos GetBlockPos() const { @@ -301,6 +308,8 @@ class CBlockIndex block.nBits = nBits; block.nNonce = nNonce; block.proof = proof; + block.m_dyna_params = d_params; + block.m_signblock_witness = m_signblock_witness; return block; } @@ -423,13 +432,35 @@ class CDiskBlockIndex : public CBlockIndex READWRITE(VARINT(nUndoPos)); // block header - READWRITE(this->nVersion); + + // Detect dynamic federation block serialization using "HF bit", + // or the signed bit which is invalid in Bitcoin + bool is_dyna = false; + int32_t nVersion; + if (ser_action.ForRead()) { + READWRITE(nVersion); + is_dyna = nVersion < 0; + this->nVersion = ~CBlockHeader::HF_MASK & nVersion; + } else { + nVersion = this->nVersion; + if (!d_params.IsNull()) { + nVersion |= CBlockHeader::HF_MASK; + is_dyna = true; + } + READWRITE(nVersion); + } + READWRITE(hashPrev); READWRITE(hashMerkleRoot); READWRITE(nTime); // For compatibility with elements 0.14 based chains if (g_signed_blocks) { - READWRITE(proof); + if (is_dyna) { + READWRITE(d_params); + READWRITE(m_signblock_witness.stack); + } else { + READWRITE(proof); + } } else { READWRITE(nBits); READWRITE(nNonce); @@ -449,6 +480,7 @@ class CDiskBlockIndex : public CBlockIndex block.nBits = nBits; block.nNonce = nNonce; block.proof = proof; + block.m_dyna_params = d_params; return block.GetHash(); } From 4aa5f991f4df3527f0954437713213a2aa5e9df9 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 31 May 2019 11:11:59 -0400 Subject: [PATCH 04/62] Refactor block_proof to support dynafed --- src/block_proof.cpp | 45 ++++++++++++++++++++++++------------------ src/block_proof.h | 2 -- src/miner.cpp | 13 +++++++++--- src/script/generic.hpp | 4 ++-- src/script/script.cpp | 5 +++++ src/script/script.h | 2 ++ 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/block_proof.cpp b/src/block_proof.cpp index 2a73c862a0..33611b203a 100644 --- a/src/block_proof.cpp +++ b/src/block_proof.cpp @@ -19,21 +19,18 @@ bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, con } } -void ResetChallenge(CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params& params) +static bool CheckProofGeneric(const CBlockHeader& block, const uint32_t max_block_signature_size, const CScript& challenge, const CScript& scriptSig, const CScriptWitness& witness) { - block.proof.challenge = indexLast.proof.challenge; -} - -static bool CheckProofGeneric(const CBlockHeader& block, const Consensus::Params& params, const CScript& challenge) -{ - if (block.GetHash() == params.hashGenesisBlock) - return true; + // scriptSig or witness will be nonempty, but not both, so just compare both limits + if (scriptSig.size() > max_block_signature_size) { + return false; + } - if (block.proof.solution.size() > params.max_block_signature_size) { + if (witness.GetSerializedSize() > max_block_signature_size) { return false; } - // Some anti-DoS flags, though consensus.max_block_signature_size caps the possible + // Some anti-DoS flags, though max_block_signature_size caps the possible // danger in malleation of the block witness data. unsigned int proof_flags = SCRIPT_VERIFY_P2SH // For cleanstack evalution under segwit flag | SCRIPT_VERIFY_STRICTENC // Minimally-sized DER sigs @@ -42,15 +39,20 @@ static bool CheckProofGeneric(const CBlockHeader& block, const Consensus::Params | SCRIPT_VERIFY_MINIMALDATA // Pushes are minimally-sized | SCRIPT_VERIFY_SIGPUSHONLY // Witness is push-only | SCRIPT_VERIFY_LOW_S // Stop easiest signature fiddling - | SCRIPT_VERIFY_WITNESS // Required for cleanstack eval in VerifyScript + | SCRIPT_VERIFY_WITNESS // Witness and to enforce cleanstack | SCRIPT_NO_SIGHASH_BYTE; // non-Check(Multi)Sig signatures will not have sighash byte - return GenericVerifyScript(block.proof.solution, challenge, proof_flags, block); + return GenericVerifyScript(scriptSig, witness, challenge, proof_flags, block); } bool CheckProof(const CBlockHeader& block, const Consensus::Params& params) { if (g_signed_blocks) { - return CheckProofGeneric(block, params, params.signblockscript); + const DynaFedParams& d_params = block.m_dyna_params; + if (d_params.IsNull()) { + return CheckProofGeneric(block, params.max_block_signature_size, params.signblockscript, block.proof.solution, CScriptWitness()); + } else { + return CheckProofGeneric(block, d_params.m_current.m_sbs_wit_limit, d_params.m_current.m_signblockscript, CScript(), block.m_signblock_witness); + } } else { return CheckProofOfWork(block.GetHash(), block.nBits, params); } @@ -58,10 +60,15 @@ bool CheckProof(const CBlockHeader& block, const Consensus::Params& params) bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params) { - return CheckProofGeneric(block, params, params.parent_chain_signblockscript); -} - -void ResetProof(CBlockHeader& block) -{ - block.proof.solution.clear(); + const DynaFedParams& d_params = block.m_dyna_params; + if (d_params.IsNull()) { + return CheckProofGeneric(block, params.max_block_signature_size, params.parent_chain_signblockscript, block.proof.solution, CScriptWitness()); + } else { + // Dynamic federations means we cannot validate the signer set + // at least without tracking the parent chain more directly. + // Note that we do not even serialize dynamic federation block witness data + // currently for merkle proofs which is the only context in which + // this function is currently used. + return true; + } } diff --git a/src/block_proof.h b/src/block_proof.h index 256d57d5f8..273699f37f 100644 --- a/src/block_proof.h +++ b/src/block_proof.h @@ -20,8 +20,6 @@ class CScript; /** Check on header proof, depending on chain type, PoW or signed **/ bool CheckProof(const CBlockHeader& block, const Consensus::Params&); bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params&); -void ResetProof(CBlockHeader& block); bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params&); -void ResetChallenge(CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params&); #endif // BITCOIN_BLOCK_PROOF_H diff --git a/src/miner.cpp b/src/miner.cpp index 20e31a5fec..b19f2a8768 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -25,13 +25,21 @@ #include #include -// ELEMENTS -#include // ResetProof, ResetChallenge #include #include #include +void ResetChallenge(CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params& params) +{ + block.proof.challenge = indexLast.proof.challenge; +} + +void ResetProof(CBlockHeader& block) +{ + block.proof.solution.clear(); +} + int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int64_t nOldTime = pblock->nTime; @@ -161,7 +169,6 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // Pad block weight by block proof fields (including upper-bound of signature) nBlockWeight += chainparams.GetConsensus().signblockscript.size() * WITNESS_SCALE_FACTOR; nBlockWeight += chainparams.GetConsensus().max_block_signature_size * WITNESS_SCALE_FACTOR; - // Reset block proof ResetProof(*pblock); ResetChallenge(*pblock, *pindexPrev, chainparams.GetConsensus()); } diff --git a/src/script/generic.hpp b/src/script/generic.hpp index 3b81a520bc..c9e7949f38 100644 --- a/src/script/generic.hpp +++ b/src/script/generic.hpp @@ -45,9 +45,9 @@ class SimpleSignatureCreator : public BaseSignatureCreator }; template -bool GenericVerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const T& data) +bool GenericVerifyScript(const CScript& scriptSig, const CScriptWitness& witness, const CScript& scriptPubKey, unsigned int flags, const T& data) { - return VerifyScript(scriptSig, scriptPubKey, NULL, flags, SimpleSignatureChecker(SerializeHash(data))); + return VerifyScript(scriptSig, scriptPubKey, &witness, flags, SimpleSignatureChecker(SerializeHash(data))); } template diff --git a/src/script/script.cpp b/src/script/script.cpp index 386f9a19a5..a24433e92d 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -335,6 +335,11 @@ std::string CScriptWitness::ToString() const return ret + ")"; } +uint32_t CScriptWitness::GetSerializedSize() const +{ + return ::GetSerializeSize(stack, 0); +} + bool CScript::HasValidOps() const { CScript::const_iterator it = begin(); diff --git a/src/script/script.h b/src/script/script.h index bab3b62d06..d004d41785 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -603,6 +603,8 @@ struct CScriptWitness void SetNull() { stack.clear(); stack.shrink_to_fit(); } std::string ToString() const; + + uint32_t GetSerializedSize() const; }; class CReserveScript From f17d7de526cebde8265d013e3679491af01450d5 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 31 May 2019 11:19:46 -0400 Subject: [PATCH 05/62] Add dynafed helper functions --- src/Makefile.am | 6 +++ src/chainparams.cpp | 3 ++ src/consensus/params.h | 2 + src/dynafed.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++ src/dynafed.h | 19 +++++++++ 5 files changed, 127 insertions(+) create mode 100644 src/dynafed.cpp create mode 100644 src/dynafed.h diff --git a/src/Makefile.am b/src/Makefile.am index 52a74f9b96..ea1acb958c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -134,6 +134,7 @@ BITCOIN_CORE_H = \ core_io.h \ core_memusage.h \ cuckoocache.h \ + dynafed.h \ fs.h \ httprpc.h \ httpserver.h \ @@ -401,10 +402,13 @@ libbitcoin_consensus_a_SOURCES = \ arith_uint256.cpp \ arith_uint256.h \ asset.cpp \ + chain.h \ + chain.cpp \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ consensus/validation.h \ + dynafed.cpp \ hash.cpp \ hash.h \ prevector.h \ @@ -433,6 +437,8 @@ libbitcoin_consensus_a_SOURCES = \ script/script.h \ script/script_error.cpp \ script/script_error.h \ + script/standard.h \ + script/standard.cpp \ serialize.h \ span.h \ tinyformat.h \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0d5081da2e..f3385ef2da 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -482,6 +482,9 @@ class CCustomParams : public CRegTestParams { consensus.nMinimumChainWork = uint256S(args.GetArg("-con_nminimumchainwork", "0x0")); consensus.defaultAssumeValid = uint256S(args.GetArg("-con_defaultassumevalid", "0x00")); + // TODO: pass in serialized vector of byte vectors, parse into extension space + // Junk keys for testing + consensus.first_extension_space = {ParseHex("02fcba7ecf41bc7e1be4ee122d9d22e3333671eb0a3a87b5cdf099d59874e1940f02fcba7ecf41bc7e1be4ee122d9d22e3333671eb0a3a87b5cdf099d59874e1940f")}; nPruneAfterHeight = (uint64_t)args.GetArg("-npruneafterheight", nPruneAfterHeight); fDefaultConsistencyChecks = args.GetBoolArg("-fdefaultconsistencychecks", fDefaultConsistencyChecks); diff --git a/src/consensus/params.h b/src/consensus/params.h index dfe19b5ac8..2840ecf50f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -99,6 +99,8 @@ struct Params { CScript signblockscript; uint32_t max_block_signature_size; // g_signed_blocks - Whether blocks are signed or not, get around circular dep + // Used to seed the extension space for first dynamic blocks + std::vector> first_extension_space; }; } // namespace Consensus diff --git a/src/dynafed.cpp b/src/dynafed.cpp new file mode 100644 index 0000000000..201ca28867 --- /dev/null +++ b/src/dynafed.cpp @@ -0,0 +1,97 @@ + +#include +#include