From 459bc3ee7eadcba83887fd05f4929a8529629231 Mon Sep 17 00:00:00 2001 From: pravblockc <51935684+pravblockc@users.noreply.github.com> Date: Sat, 11 Dec 2021 16:00:27 -0400 Subject: [PATCH] add ehf special tx (#4577) --- src/Makefile.am | 2 + src/Makefile.test.include | 1 + src/bloom.cpp | 1 + src/chainparams.cpp | 4 ++ src/consensus/params.h | 1 + src/core_write.cpp | 8 +++ src/evo/mnhftx.cpp | 70 +++++++++++++++++++++ src/evo/mnhftx.h | 74 ++++++++++++++++++++++ src/evo/specialtx.cpp | 17 +++-- src/evo/specialtx.h | 2 +- src/primitives/transaction.h | 1 + src/test/specialtx_tests.cpp | 84 +++++++++++++++++++++++++ src/validation.cpp | 3 +- test/lint/lint-circular-dependencies.sh | 1 + 14 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 src/evo/mnhftx.cpp create mode 100644 src/evo/mnhftx.h create mode 100644 src/test/specialtx_tests.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 410113a8a9..a26a5d980b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,6 +169,7 @@ BITCOIN_CORE_H = \ evo/deterministicmns.h \ evo/evodb.h \ evo/mnauth.h \ + evo/mnhftx.h \ evo/providertx.h \ evo/simplifiedmns.h \ evo/specialtx.h \ @@ -356,6 +357,7 @@ libdash_server_a_SOURCES = \ evo/deterministicmns.cpp \ evo/evodb.cpp \ evo/mnauth.cpp \ + evo/mnhftx.cpp \ evo/providertx.cpp \ evo/simplifiedmns.cpp \ evo/specialtx.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index d7721d82f8..9d0a5a52ab 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -181,6 +181,7 @@ BITCOIN_TESTS =\ test/sighash_tests.cpp \ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ + test/specialtx_tests.cpp \ test/streams_tests.cpp \ test/subsidy_tests.cpp \ test/sync_tests.cpp \ diff --git a/src/bloom.cpp b/src/bloom.cpp index 224183317b..8025b5d0b3 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -212,6 +212,7 @@ bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &t } case(TRANSACTION_COINBASE): case(TRANSACTION_QUORUM_COMMITMENT): + case(TRANSACTION_MNHF_SIGNAL): // No additional checks for this transaction types return false; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b589144fb5..69c549578d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -281,6 +281,7 @@ public: consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_400_60; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_400_85; fDefaultConsistencyChecks = false; fRequireStandard = true; @@ -492,6 +493,7 @@ public: consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60; fDefaultConsistencyChecks = false; fRequireStandard = false; @@ -685,6 +687,7 @@ public: consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60; UpdateDevnetLLMQChainLocksFromArgs(args); UpdateDevnetLLMQInstantSendFromArgs(args); @@ -922,6 +925,7 @@ public: consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_TEST; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_TEST; consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_TEST; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_TEST; UpdateLLMQTestParametersFromArgs(args); } diff --git a/src/consensus/params.h b/src/consensus/params.h index 4f66dc9fd3..1416938c5e 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -116,6 +116,7 @@ struct Params { LLMQType llmqTypeChainLocks; LLMQType llmqTypeInstantSend{LLMQType::LLMQ_NONE}; LLMQType llmqTypePlatform{LLMQType::LLMQ_NONE}; + LLMQType llmqTypeMnhf{LLMQType::LLMQ_NONE}; }; } // namespace Consensus diff --git a/src/core_write.cpp b/src/core_write.cpp index 091d60df53..8313c911ad 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -305,6 +306,13 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, qcTx.ToJson(obj); entry.pushKV("qcTx", obj); } + } else if (tx.nType == TRANSACTION_MNHF_SIGNAL) { + MNHFTxPayload mnhfTx; + if (GetTxPayload(tx, mnhfTx)) { + UniValue obj; + mnhfTx.ToJson(obj); + entry.pushKV("mnhfTx", obj); + } } if (!hashBlock.IsNull()) diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp new file mode 100644 index 0000000000..cb550b2218 --- /dev/null +++ b/src/evo/mnhftx.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2021-2022 The Dash 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 +#include +#include + +#include + +extern const std::string CBLSIG_REQUESTID_PREFIX = "clsig"; + +bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const +{ + if (nVersion == 0 || nVersion > CURRENT_VERSION) { + return false; + } + + Consensus::LLMQType llmqType = Params().GetConsensus().llmqTypeMnhf; + int signOffset{llmq::GetLLMQParams(llmqType).dkgInterval}; + + const uint256 requestId = ::SerializeHash(std::make_pair(CBLSIG_REQUESTID_PREFIX, pQuorumIndex->nHeight)); + return llmq::CSigningManager::VerifyRecoveredSig(llmqType, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, 0) || + llmq::CSigningManager::VerifyRecoveredSig(llmqType, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, signOffset); +} + +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +{ + MNHFTxPayload mnhfTx; + if (!GetTxPayload(tx, mnhfTx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-payload"); + } + + if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-version"); + } + + const CBlockIndex* pindexQuorum = LookupBlockIndex(mnhfTx.signal.quorumHash); + if (!pindexQuorum) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-quorum-hash"); + } + + if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) { + // not part of active chain + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-quorum-hash"); + } + + if (!Params().GetConsensus().llmqs.count(Params().GetConsensus().llmqTypeMnhf)) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-type"); + } + + if (!mnhfTx.signal.Verify(pindexQuorum)) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-invalid"); + } + + return true; +} + +std::string MNHFTx::ToString() const +{ + return strprintf("MNHFTx(nVersion=%d, quorumHash=%s, sig=%s)", + nVersion, quorumHash.ToString(), sig.ToString()); +} + diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h new file mode 100644 index 0000000000..d846913dee --- /dev/null +++ b/src/evo/mnhftx.h @@ -0,0 +1,74 @@ +// Copyright (c) 2021-2022 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_EVO_MNHFTX_H +#define BITCOIN_EVO_MNHFTX_H + +#include +#include +#include +#include +#include + +class CBlockIndex; +class CValidationState; +extern CCriticalSection cs_main; + +// mnhf signal special transaction +class MNHFTx +{ +public: + static constexpr uint16_t CURRENT_VERSION = 1; + + uint16_t nVersion{CURRENT_VERSION}; + uint256 quorumHash; + CBLSSignature sig; + + MNHFTx() = default; + bool Verify(const CBlockIndex* pQuorumIndex) const; + + SERIALIZE_METHODS(MNHFTx, obj) + { + READWRITE(obj.nVersion, obj.quorumHash, obj.sig); + } + + std::string ToString() const; + + void ToJson(UniValue& obj) const + { + obj.clear(); + obj.setObject(); + obj.pushKV("version", (int)nVersion); + obj.pushKV("quorumHash", quorumHash.ToString()); + obj.pushKV("sig", sig.ToString()); + } +}; + +class MNHFTxPayload +{ +public: + static constexpr uint16_t CURRENT_VERSION = 1; + + uint16_t nVersion{CURRENT_VERSION}; + MNHFTx signal; + + SERIALIZE_METHODS(MNHFTxPayload, obj) + { + READWRITE(obj.nVersion, obj.signal); + } + + void ToJson(UniValue& obj) const + { + obj.setObject(); + obj.pushKV("version", (int)nVersion); + + UniValue mnhfObj; + signal.ToJson(mnhfObj); + obj.pushKV("signal", mnhfObj); + } +}; + +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + +#endif // BITCOIN_EVO_MNHFTX_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index c0cfd5c03a..d6ba066c30 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -6,16 +6,19 @@ #include #include -#include -#include -#include #include #include -#include +#include +#include #include +#include +#include +#include bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view) { + AssertLockHeld(cs_main); + if (tx.nVersion != 3 || tx.nType == TRANSACTION_NORMAL) return true; @@ -37,6 +40,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali return CheckCbTx(tx, pindexPrev, state); case TRANSACTION_QUORUM_COMMITMENT: return llmq::CheckLLMQCommitment(tx, pindexPrev, state); + case TRANSACTION_MNHF_SIGNAL: + return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_GOV_FEE) == ThresholdState::ACTIVE && CheckMNHFTx(tx, pindexPrev, state); } } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); @@ -62,6 +67,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida return true; // nothing to do case TRANSACTION_QUORUM_COMMITMENT: return true; // handled per block + case TRANSACTION_MNHF_SIGNAL: + return true; // handled per block } return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc"); @@ -83,6 +90,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) return true; // nothing to do case TRANSACTION_QUORUM_COMMITMENT: return true; // handled per block + case TRANSACTION_MNHF_SIGNAL: + return true; // handled per block } return false; diff --git a/src/evo/specialtx.h b/src/evo/specialtx.h index 87a3ea4f81..902eb68afe 100644 --- a/src/evo/specialtx.h +++ b/src/evo/specialtx.h @@ -18,7 +18,7 @@ class CValidationState; extern CCriticalSection cs_main; -bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view); +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 6a27096327..724cd99cf3 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -21,6 +21,7 @@ enum { TRANSACTION_PROVIDER_UPDATE_REVOKE = 4, TRANSACTION_COINBASE = 5, TRANSACTION_QUORUM_COMMITMENT = 6, + TRANSACTION_MNHF_SIGNAL = 7, }; /** An outpoint - a combination of a transaction hash and an index n into its vout */ diff --git a/src/test/specialtx_tests.cpp b/src/test/specialtx_tests.cpp new file mode 100644 index 0000000000..756c53e156 --- /dev/null +++ b/src/test/specialtx_tests.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2021 The Dash 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 +#include + +#include +#include + +#include +#include + + +bool VerifyMNHFTx(const CTransaction& tx, CValidationState& state) +{ + MNHFTxPayload mnhfTx; + if (!GetTxPayload(tx, mnhfTx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-payload"); + } + + if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-version"); + } + + return true; +} + +static CMutableTransaction CreateMNHFTx(const uint256& mnhfTxHash, const CBLSSignature& cblSig, const uint16_t& versionBit) +{ + MNHFTxPayload extraPayload; + extraPayload.nVersion = 1; + extraPayload.signal.nVersion = versionBit; + extraPayload.signal.quorumHash = mnhfTxHash; + extraPayload.signal.sig = cblSig; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_MNHF_SIGNAL; + SetTxPayload(tx, extraPayload); + + return tx; +} + +BOOST_FIXTURE_TEST_SUITE(specialtx_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(verify_mnhf_specialtx_tests) +{ + int count = 10; + uint16_t ver = 2; + + std::vector vec_sigs; + std::vector vec_pks; + std::vector vec_sks; + + CBLSSecretKey sk; + uint256 hash = GetRandHash(); + for (int i = 0; i < count; i++) { + sk.MakeNewKey(); + vec_pks.push_back(sk.GetPublicKey()); + vec_sks.push_back(sk); + } + + CBLSSecretKey ag_sk = CBLSSecretKey::AggregateInsecure(vec_sks); + CBLSPublicKey ag_pk = CBLSPublicKey::AggregateInsecure(vec_pks); + + BOOST_CHECK(ag_sk.IsValid()); + BOOST_CHECK(ag_pk.IsValid()); + + uint256 verHash = uint256S(itostr(ver)); + auto sig = ag_sk.Sign(verHash); + BOOST_CHECK(sig.VerifyInsecure(ag_pk, verHash)); + + const CMutableTransaction tx = CreateMNHFTx(hash, sig, ver); + CValidationState state; + BOOST_CHECK(VerifyMNHFTx(CTransaction(tx), state)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp index 021323b17d..1d5f35dcae 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -378,7 +378,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR && tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE && tx.nType != TRANSACTION_COINBASE && - tx.nType != TRANSACTION_QUORUM_COMMITMENT) { + tx.nType != TRANSACTION_QUORUM_COMMITMENT && + tx.nType != TRANSACTION_MNHF_SIGNAL) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); } if (tx.IsCoinBase() && tx.nType != TRANSACTION_COINBASE) diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 2c83bec9d3..02feca84be 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -105,6 +105,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "coinjoin/coinjoin -> llmq/chainlocks -> net -> coinjoin/coinjoin" "evo/deterministicmns -> llmq/utils -> net -> evo/deterministicmns" "evo/deterministicmns -> llmq/utils -> net -> masternode/sync -> evo/deterministicmns" + "evo/mnhftx -> evo/specialtx -> evo/mnhftx" ) EXIT_CODE=0