From 5d83d5e6c6cd9a71c134dd8ed569a21915bfdd82 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 8 May 2023 11:00:41 +0700 Subject: [PATCH 1/7] fix: mnhftx never has been mined -> it has only one version of bls (basic) --- src/evo/mnhftx.cpp | 2 +- src/evo/mnhftx.h | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index 55394beaa4ab..a8c74d078c05 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -20,7 +20,7 @@ extern const std::string CBLSIG_REQUESTID_PREFIX = "clsig"; bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const { - if (nVersion == 0 || nVersion > (llmq::utils::IsV19Active(pQuorumIndex) ? BASIC_BLS_VERSION : LEGACY_BLS_VERSION)) { + if (nVersion == 0 || nVersion > CURRENT_VERSION) { return false; } diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index d4f405e0f379..838edbc225b8 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -19,10 +19,9 @@ extern RecursiveMutex cs_main; class MNHFTx { public: - static constexpr uint16_t LEGACY_BLS_VERSION = 1; - static constexpr uint16_t BASIC_BLS_VERSION = 2; + static constexpr uint16_t CURRENT_VERSION = 1; - uint16_t nVersion{LEGACY_BLS_VERSION}; + uint16_t nVersion{CURRENT_VERSION}; uint256 quorumHash; CBLSSignature sig; @@ -32,7 +31,7 @@ class MNHFTx SERIALIZE_METHODS(MNHFTx, obj) { READWRITE(obj.nVersion, obj.quorumHash); - READWRITE(CBLSSignatureVersionWrapper(const_cast(obj.sig), (obj.nVersion == LEGACY_BLS_VERSION))); + READWRITE(CBLSSignatureVersionWrapper(const_cast(obj.sig), /* fLegacy= */ false)); } std::string ToString() const; From dcec9c22f0d0fa505db9f1c7b3501a88c2409e43 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 3 Jul 2023 16:24:24 +0700 Subject: [PATCH 2/7] fix!: since format of MnEhf message changes, it should be accepted only since v20 activation --- src/evo/specialtxman.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 943ec68870da..6b3bd5a10a3c 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -45,7 +45,10 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const case TRANSACTION_QUORUM_COMMITMENT: return llmq::CheckLLMQCommitment(tx, pindexPrev, state); case TRANSACTION_MNHF_SIGNAL: - return pindexPrev->nHeight + 1 >= Params().GetConsensus().DIP0024Height && CheckMNHFTx(tx, pindexPrev, state); + if (!llmq::utils::IsV20Active(pindexPrev)) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "mnhf-before-v20"); + } + return CheckMNHFTx(tx, pindexPrev, state); case TRANSACTION_ASSET_LOCK: case TRANSACTION_ASSET_UNLOCK: if (!llmq::utils::IsV20Active(pindexPrev)) { From caa9303c3a15f0739d6aa7feea43b5e716e631ee Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 12 May 2023 17:35:26 +0700 Subject: [PATCH 3/7] feat: adds python implementation for `CMnEhf` in messages.py --- test/functional/test_framework/messages.py | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 075509284939..8a28d5c00826 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1176,6 +1176,45 @@ def __repr__(self): .format(self.version, self.index, self.fee, self.requestedHeight, self.quorumHash, self.quorumSig.hex()) +class CMnEhf: + __slots__ = ("version", "versionBit", "quorumHash", "quorumSig") + + def __init__(self, version=None, versionBit=None, quorumHash = 0, quorumSig = None): + self.set_null() + if version is not None: + self.version = version + if versionBit is not None: + self.versionBit = versionBit + if quorumHash is not None: + self.quorumHash = quorumHash + if quorumSig is not None: + self.quorumSig = quorumSig + + def set_null(self): + self.version = 0 + self.versionBit = 0 + self.quorumHash = 0 + self.quorumSig = b'\x00' * 96 + + def deserialize(self, f): + self.version = struct.unpack(" Date: Wed, 14 Jun 2023 16:05:32 +0700 Subject: [PATCH 4/7] refactor: rename 'specialtx_tests -> 'evo_mnhf_tests' --- src/Makefile.test.include | 2 +- src/test/{specialtx_tests.cpp => evo_mnhf_tests.cpp} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/{specialtx_tests.cpp => evo_mnhf_tests.cpp} (97%) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index db14dade783a..c259e53fc689 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -98,6 +98,7 @@ BITCOIN_TESTS =\ test/evo_assetlocks_tests.cpp \ test/evo_deterministicmns_tests.cpp \ test/evo_instantsend_tests.cpp \ + test/evo_mnhf_tests.cpp \ test/evo_simplifiedmns_tests.cpp \ test/evo_trivialvalidation.cpp \ test/evo_utils_tests.cpp \ @@ -146,7 +147,6 @@ BITCOIN_TESTS =\ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ test/sock_tests.cpp \ - test/specialtx_tests.cpp \ test/streams_tests.cpp \ test/subsidy_tests.cpp \ test/sync_tests.cpp \ diff --git a/src/test/specialtx_tests.cpp b/src/test/evo_mnhf_tests.cpp similarity index 97% rename from src/test/specialtx_tests.cpp rename to src/test/evo_mnhf_tests.cpp index 04dc3329afe3..8b1d0478a60b 100644 --- a/src/test/specialtx_tests.cpp +++ b/src/test/evo_mnhf_tests.cpp @@ -48,7 +48,7 @@ static CMutableTransaction CreateMNHFTx(const uint256& mnhfTxHash, const CBLSSig return tx; } -BOOST_FIXTURE_TEST_SUITE(specialtx_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(evo_mnhf_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(verify_mnhf_specialtx_tests) { From 8852601c70ef9564630d1d30a7e50e986c273a7e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 12 May 2023 18:09:55 +0700 Subject: [PATCH 5/7] fix!: changed prefix in requestID for MnEhfTx --- src/evo/mnhftx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index a8c74d078c05..d6b43ee58ae2 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -16,7 +16,7 @@ #include -extern const std::string CBLSIG_REQUESTID_PREFIX = "clsig"; +extern const std::string MNEHF_REQUESTID_PREFIX = "mnhf"; bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const { @@ -29,7 +29,7 @@ bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const assert(llmq_params_opt.has_value()); int signOffset{llmq_params_opt->dkgInterval}; - const uint256 requestId = ::SerializeHash(std::make_pair(CBLSIG_REQUESTID_PREFIX, pQuorumIndex->nHeight)); + const uint256 requestId = ::SerializeHash(std::make_pair(MNEHF_REQUESTID_PREFIX, nVersion)); return llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, 0) || llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, signOffset); } From 4e7450a9ba8ed993f339e63b6fe4ee9c0d603c45 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 25 Jul 2023 03:42:04 +0700 Subject: [PATCH 6/7] fix: proper signature validation of MnEhf message --- src/evo/mnhftx.cpp | 48 +++++++++++++++++++++++++------------ src/evo/mnhftx.h | 14 +++++------ src/test/evo_mnhf_tests.cpp | 2 +- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index d6b43ee58ae2..665e600f734c 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -13,29 +13,39 @@ #include #include #include +#include #include extern const std::string MNEHF_REQUESTID_PREFIX = "mnhf"; -bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const +bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex, const uint256& msgHash, TxValidationState& state) const { - if (nVersion == 0 || nVersion > CURRENT_VERSION) { - return false; + if (versionBit >= VERSIONBITS_NUM_BITS) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-nbit-out-of-bounds"); } Consensus::LLMQType llmqType = Params().GetConsensus().llmqTypeMnhf; const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); - assert(llmq_params_opt.has_value()); + if (!llmq_params_opt.has_value()) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-type"); + } int signOffset{llmq_params_opt->dkgInterval}; - const uint256 requestId = ::SerializeHash(std::make_pair(MNEHF_REQUESTID_PREFIX, nVersion)); - return llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, 0) || - llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, signOffset); + const uint256 requestId = ::SerializeHash(std::make_pair(MNEHF_REQUESTID_PREFIX, int64_t{versionBit})); + + if (!llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight + signOffset, requestId, msgHash, sig)) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-invalid"); + } + return true; } bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { + if (tx.nVersion != 3 || tx.nType != TRANSACTION_MNHF_SIGNAL) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type"); + } + MNHFTxPayload mnhfTx; if (!GetTxPayload(tx, mnhfTx)) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-payload"); @@ -55,12 +65,16 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValida return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash"); } - if (!llmq::GetLLMQParams(Params().GetConsensus().llmqTypeMnhf).has_value()) { - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type"); - } + // Copy transaction except `quorumSig` field to calculate hash + CMutableTransaction tx_copy(tx); + auto payload_copy = mnhfTx; + payload_copy.signal.sig = CBLSSignature(); + SetTxPayload(tx_copy, payload_copy); + uint256 msgHash = tx_copy.GetHash(); - if (!mnhfTx.signal.Verify(pindexQuorum)) { - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-invalid"); + if (!mnhfTx.signal.Verify(pindexQuorum, msgHash, state)) { + // set up inside Verify + return false; } return true; @@ -68,7 +82,11 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValida std::string MNHFTx::ToString() const { - return strprintf("MNHFTx(nVersion=%d, quorumHash=%s, sig=%s)", - nVersion, quorumHash.ToString(), sig.ToString()); + return strprintf("MNHFTx(versionBit=%d, quorumHash=%s, sig=%s)", + versionBit, quorumHash.ToString(), sig.ToString()); +} +std::string MNHFTxPayload::ToString() const +{ + return strprintf("MNHFTxPayload(nVersion=%d, signal=%s)", + nVersion, signal.ToString()); } - diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index 838edbc225b8..392fe5ff857c 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -19,18 +19,16 @@ extern RecursiveMutex cs_main; class MNHFTx { public: - static constexpr uint16_t CURRENT_VERSION = 1; - - uint16_t nVersion{CURRENT_VERSION}; + uint8_t versionBit{0}; uint256 quorumHash; CBLSSignature sig; MNHFTx() = default; - bool Verify(const CBlockIndex* pQuorumIndex) const; + bool Verify(const CBlockIndex* pQuorumIndex, const uint256& msgHash, TxValidationState& state) const; SERIALIZE_METHODS(MNHFTx, obj) { - READWRITE(obj.nVersion, obj.quorumHash); + READWRITE(obj.versionBit, obj.quorumHash); READWRITE(CBLSSignatureVersionWrapper(const_cast(obj.sig), /* fLegacy= */ false)); } @@ -40,7 +38,7 @@ class MNHFTx { obj.clear(); obj.setObject(); - obj.pushKV("version", (int)nVersion); + obj.pushKV("versionBit", (int)versionBit); obj.pushKV("quorumHash", quorumHash.ToString()); obj.pushKV("sig", sig.ToString()); } @@ -52,7 +50,7 @@ class MNHFTxPayload static constexpr auto SPECIALTX_TYPE = TRANSACTION_MNHF_SIGNAL; static constexpr uint16_t CURRENT_VERSION = 1; - uint16_t nVersion{CURRENT_VERSION}; + uint8_t nVersion{CURRENT_VERSION}; MNHFTx signal; SERIALIZE_METHODS(MNHFTxPayload, obj) @@ -60,6 +58,8 @@ class MNHFTxPayload READWRITE(obj.nVersion, obj.signal); } + std::string ToString() const; + void ToJson(UniValue& obj) const { obj.setObject(); diff --git a/src/test/evo_mnhf_tests.cpp b/src/test/evo_mnhf_tests.cpp index 8b1d0478a60b..2e65fcb1c5c4 100644 --- a/src/test/evo_mnhf_tests.cpp +++ b/src/test/evo_mnhf_tests.cpp @@ -36,7 +36,7 @@ static CMutableTransaction CreateMNHFTx(const uint256& mnhfTxHash, const CBLSSig { MNHFTxPayload extraPayload; extraPayload.nVersion = 1; - extraPayload.signal.nVersion = versionBit; + extraPayload.signal.versionBit = versionBit; extraPayload.signal.quorumHash = mnhfTxHash; extraPayload.signal.sig = cblSig; From c4671a30b869590c62c6d0dfc872c8dd1e90eac9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 25 Jul 2023 03:44:27 +0700 Subject: [PATCH 7/7] fix: mempool will accept MnEHF transaction even fee/inputs/outputs are zeroes --- src/consensus/tx_check.cpp | 2 +- src/txmempool.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index 5c54c4e4242a..89ab2efb5d62 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -14,7 +14,7 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state) { bool allowEmptyTxIn = false; bool allowEmptyTxOut = false; - if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { + if (tx.nType == TRANSACTION_QUORUM_COMMITMENT || tx.nType == TRANSACTION_MNHF_SIGNAL) { allowEmptyTxIn = true; allowEmptyTxOut = true; } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 9d42ec927bee..91c29d7e0bda 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -457,6 +457,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces bool ok = GetTxPayload(tx, assetUnlockTx); assert(ok); mapAssetUnlockExpiry.insert({tx.GetHash(), assetUnlockTx.getHeightToExpiry()}); + } else if (tx.nType == TRANSACTION_MNHF_SIGNAL) { + PrioritiseTransaction(tx.GetHash(), 0.1 * COIN); } }