From e0de2ce588b9d49a4ae433735e641cc9ec7848aa Mon Sep 17 00:00:00 2001 From: psolstice Date: Wed, 20 Jan 2021 18:08:40 +0300 Subject: [PATCH 1/3] Limit reorg depth (#976) --- src/blacklists.h | 3 ++- src/chainparams.cpp | 12 ++++++++++++ src/consensus/params.h | 5 +++++ src/validation.cpp | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/blacklists.h b/src/blacklists.h index 49f00a2317..e67cd6f661 100644 --- a/src/blacklists.h +++ b/src/blacklists.h @@ -1666,5 +1666,6 @@ std::set const txid_blacklist { "c76e2ae8ff54adc4dfcf9c42fc39120c40b4b69ca8ea431298f3c78780c08bf4", "b78b836bc8aecda9ba61a7bbee202a81fbaad7a61817b8424e14ce0ac0991b0d", "ef5aab4e9a989c9e64c26f126670e15242b02bb7ca4b694a14a4dbfcf6a7ac28", - "e8d21eb79426e1845d226c29a5444912e6395e5089d798cc63e582913c7835e9" + "e8d21eb79426e1845d226c29a5444912e6395e5089d798cc63e582913c7835e9", + "d4de8ee7db978325d4e327211d00f753cd46711fe897370c8099704f874fe08a" }; \ No newline at end of file diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 122e47348b..6699b06020 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -391,6 +391,10 @@ class CMainParams : public CChainParams { consensus.nEvoSporkStartBlock = ZC_LELANTUS_STARTING_BLOCK; consensus.nEvoSporkStopBlock = ZC_LELANTUS_STARTING_BLOCK + 24*12*365; // one year after lelantus + // reorg + consensus.nMaxReorgDepth = 5; + consensus.nMaxReorgDepthEnforcementBlock = 388000; + // Dandelion related values. consensus.nDandelionEmbargoMinimum = DANDELION_EMBARGO_MINIMUM; consensus.nDandelionEmbargoAvgAdd = DANDELION_EMBARGO_AVG_ADD; @@ -631,6 +635,10 @@ class CTestNetParams : public CChainParams { consensus.nEvoSporkStartBlock = 22000; consensus.nEvoSporkStopBlock = 40000; + // reorg + consensus.nMaxReorgDepth = 4; + consensus.nMaxReorgDepthEnforcementBlock = 25150; + // Dandelion related values. consensus.nDandelionEmbargoMinimum = DANDELION_TESTNET_EMBARGO_MINIMUM; consensus.nDandelionEmbargoAvgAdd = DANDELION_TESTNET_EMBARGO_AVG_ADD; @@ -826,6 +834,10 @@ class CRegTestParams : public CChainParams { consensus.nEvoSporkStartBlock = 1000; consensus.nEvoSporkStopBlock = 1500; + // reorg + consensus.nMaxReorgDepth = 4; + consensus.nMaxReorgDepthEnforcementBlock = 100; + // Dandelion related values. consensus.nDandelionEmbargoMinimum = 0; consensus.nDandelionEmbargoAvgAdd = 1; diff --git a/src/consensus/params.h b/src/consensus/params.h index e2cffc17c7..54edf5575f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -315,6 +315,11 @@ struct Params { /** block to start using chainlocks */ int DIP0008Height; + /** maximum reorg depth */ + int nMaxReorgDepth; + /** block to start reorg depth enforcement */ + int nMaxReorgDepthEnforcementBlock; + int nEvoZnodeMinimumConfirmations; std::map llmqs; diff --git a/src/validation.cpp b/src/validation.cpp index a9b2a11c15..6ace20462e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3761,6 +3761,23 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, CBlockIndex *pindexOldTip = chainActive.Tip(); if (pindexMostWork == NULL) { pindexMostWork = FindMostWorkChain(); + if (pindexMostWork != NULL && + pindexMostWork != chainActive.Tip() && + chainActive.Height() >= chainparams.GetConsensus().nMaxReorgDepthEnforcementBlock && + !GetBoolArg("-allowdeepreorg", false)) { + const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + assert(pindexFork != NULL); + if (chainActive.Tip() != pindexFork && + pindexFork->nHeight < chainActive.Height() - chainparams.GetConsensus().nMaxReorgDepth) { + LogPrintf("Deep reorg of %d blocks blocked (most work block = %s, fork block = %s)\n", + pindexMostWork->nHeight - pindexFork->nHeight, + pindexMostWork->GetBlockHash().ToString(), + pindexFork->GetBlockHash().ToString()); + // mark block on wrong chain as invalid + InvalidateBlock(state, chainparams, pindexMostWork->GetAncestor(pindexFork->nHeight+1)); + return state.Error("Reorg depth exceeds maximum allowed value"); + } + } } // Whether we have anything to do at all. From e743e9cefd04dd4858de6cae208f0b4025d534bd Mon Sep 17 00:00:00 2001 From: a-bezrukov <36845392+a-bezrukov@users.noreply.github.com> Date: Wed, 20 Jan 2021 19:24:21 +0400 Subject: [PATCH 2/3] Version bump (#977) --- configure.ac | 4 ++-- src/clientversion.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 9cc61eeaf1..fb06891675 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 14) -define(_CLIENT_VERSION_REVISION, 2) -define(_CLIENT_VERSION_BUILD, 3) +define(_CLIENT_VERSION_REVISION, 3) +define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) diff --git a/src/clientversion.h b/src/clientversion.h index 76e27c9e3f..782bdaa46a 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,8 +16,8 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 14 -#define CLIENT_VERSION_REVISION 2 -#define CLIENT_VERSION_BUILD 3 +#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true From 552556150c400598172c12eabb02768a9c4d0e4b Mon Sep 17 00:00:00 2001 From: a-bezrukov <36845392+a-bezrukov@users.noreply.github.com> Date: Thu, 21 Jan 2021 20:22:07 +0400 Subject: [PATCH 3/3] Llmq improvements (#978) * Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed RPC broadcast * Znode GUI removed * Removed znodeman * Removed znode-sync * Removed activeznode * Removed ping and broadcast * Removed znode * Removed from GUI and net exchange * Removed -znodeprivkey * Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed RPC broadcast * Znode GUI removed * Removed znodeman * Removed znode-sync * Removed activeznode * Removed ping and broadcast * Removed znode * Removed from GUI and net exchange * Removed -znodeprivkey * Tests are adjusted * Removed sporks * Cleanup in comments, other minor clean-ups * Stopped using *.dat cache files * Minor improvements for LLMQs * Minor improvements for LLMQs * All LLMQ tests are done * Llmqs for chainlocks were enabled * Chainlocks start working * Wrapping up with chainlocks * Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed znodeman * Removed znode * Cleanup in comments, other minor clean-ups * Minor improvements for LLMQs * All LLMQ tests are done * Llmqs for chainlocks were enabled * Chainlocks start working * Wrapping up with chainlocks * Tests are fixed * Removed the original test * Evospork controls chainlocks * More tests for CL * Blockchain params change for testnet * Formatting fix * Minor improvments * Dealing with the code review comments * Version update * HF date is set * Minor cleanup * Dealing with code review Co-authored-by: Andrey Co-authored-by: Peter Shugalev --- configure.ac | 2 +- qa/pull-tester/rpc-tests.py | 9 +- qa/rpc-tests/llmq-chainlocks.py | 132 +++++++ qa/rpc-tests/llmq-cl-evospork.py | 118 +++++++ qa/rpc-tests/llmq-dkgerrors.py | 94 +++++ qa/rpc-tests/llmq-is-cl-conflicts.py | 329 ++++++++++++++++++ qa/rpc-tests/llmq-is-retroactive.py | 169 +++++++++ qa/rpc-tests/llmq-signing.py | 93 +++++ qa/rpc-tests/llmq-simplepose.py | 55 +++ qa/rpc-tests/test_framework/test_framework.py | 11 +- qa/rpc-tests/test_framework/util.py | 8 +- src/Makefile.am | 1 - src/chain_settings.h | 33 -- src/chainparams.cpp | 21 +- src/clientversion.h | 2 +- src/consensus/params.h | 1 + src/dsnotificationinterface.cpp | 6 +- src/evo/deterministicmns.cpp | 5 +- src/evo/spork.cpp | 2 +- src/evo/spork.h | 21 ++ src/init.cpp | 3 - src/llmq/quorums_chainlocks.cpp | 19 +- src/llmq/quorums_init.cpp | 11 +- src/llmq/quorums_instantsend.cpp | 4 +- src/miner.cpp | 9 +- src/net_processing.cpp | 12 +- src/protocol.cpp | 1 - src/rpc/blockchain.cpp | 5 + src/validation.cpp | 4 +- src/wallet/lelantusjoinsplitbuilder.cpp | 2 +- src/wallet/wallet.cpp | 5 +- 31 files changed, 1096 insertions(+), 91 deletions(-) create mode 100755 qa/rpc-tests/llmq-chainlocks.py create mode 100755 qa/rpc-tests/llmq-cl-evospork.py create mode 100755 qa/rpc-tests/llmq-dkgerrors.py create mode 100755 qa/rpc-tests/llmq-is-cl-conflicts.py create mode 100755 qa/rpc-tests/llmq-is-retroactive.py create mode 100755 qa/rpc-tests/llmq-signing.py create mode 100755 qa/rpc-tests/llmq-simplepose.py delete mode 100644 src/chain_settings.h diff --git a/configure.ac b/configure.ac index fb06891675..d83891cd1a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 14) -define(_CLIENT_VERSION_REVISION, 3) +define(_CLIENT_VERSION_REVISION, 4) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2021) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index ed302d42ec..87c931a0b5 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -203,7 +203,14 @@ 'sigma_zapwalletmints_unconf_trans.py', # Evo Znodes - 'dip3-deterministicmns.py' + 'dip3-deterministicmns.py', + 'llmq-signing.py', + 'llmq-dkgerrors.py', + 'llmq-simplepose.py', + 'llmq-chainlocks.py', + 'llmq-cl-evospork.py', +# 'llmq-is-cl-conflicts.py', +# 'llmq-is-retroactive.py' # Unstable tests #, 'dip4-coinbasemerkleroots.py' diff --git a/qa/rpc-tests/llmq-chainlocks.py b/qa/rpc-tests/llmq-chainlocks.py new file mode 100755 index 0000000000..f5a3f2d8d0 --- /dev/null +++ b/qa/rpc-tests/llmq-chainlocks.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import EvoZnodeTestFramework +from test_framework.util import * +from time import * + +''' +llmq-chainlocks.py + +Checks LLMQs based ChainLocks + +''' + +class LLMQChainLocksTest(EvoZnodeTestFramework): + def __init__(self): + super().__init__(6, 5, extra_args=[['-debug=chainlocks']] * 6) + + def run_test(self): + + for i in range(4): + self.mine_quorum() + + # mine single block, wait for chainlock + self.nodes[0].generate(1) + + self.wait_for_chainlock_tip_all_nodes() + + # mine many blocks, wait for chainlock + self.nodes[0].generate(20) + self.wait_for_chainlock_tip_all_nodes() + + # assert that all blocks up until the tip are chainlocked + for h in range(1, self.nodes[0].getblockcount()): + block = self.nodes[0].getblock(self.nodes[0].getblockhash(h)) + assert(block['chainlock']) + + # Isolate node, mine on another, and reconnect + isolate_node(self.nodes[0]) + node0_tip = self.nodes[0].getbestblockhash() + self.nodes[1].generate(5) + self.wait_for_chainlock_tip(self.nodes[1]) + assert(self.nodes[0].getbestblockhash() == node0_tip) + reconnect_isolated_node(self.nodes[0], 1) + self.nodes[1].generate(1) + self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) + + # Isolate node, mine on both parts of the network, and reconnect + isolate_node(self.nodes[0]) + self.nodes[0].generate(5) + self.nodes[1].generate(1) + good_tip = self.nodes[1].getbestblockhash() + self.wait_for_chainlock_tip(self.nodes[1]) + assert(not self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) + reconnect_isolated_node(self.nodes[0], 1) + self.nodes[1].generate(1) + self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) + assert(self.nodes[0].getblock(self.nodes[0].getbestblockhash())["previousblockhash"] == good_tip) + assert(self.nodes[1].getblock(self.nodes[1].getbestblockhash())["previousblockhash"] == good_tip) + + # Keep node connected and let it try to reorg the chain + good_tip = self.nodes[0].getbestblockhash() + # Restart it so that it forgets all the chainlocks from the past + stop_node(self.nodes[0], 0) + self.nodes[0] = start_node(0, self.options.tmpdir, self.extra_args[0]) + connect_nodes(self.nodes[0], 1) + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + # Now try to reorg the chain + self.nodes[0].generate(2) + assert(self.nodes[1].getbestblockhash() == good_tip) + self.nodes[0].generate(2) + sleep(6) + assert(self.nodes[1].getbestblockhash() == good_tip) + + # Now let the node which is on the wrong chain reorg back to the locked chain + self.nodes[0].reconsiderblock(good_tip) + assert(self.nodes[0].getbestblockhash() != good_tip) + self.nodes[1].generate(1) + self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) + assert(self.nodes[0].getbestblockhash() == self.nodes[1].getbestblockhash()) + + isolate_node(self.nodes[0]) + self.nodes[0].generate(1) + reconnect_isolated_node(self.nodes[0], 1) + self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) + + def wait_for_chainlock_tip_all_nodes(self): + for node in self.nodes: + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock_tip(self, node): + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock(self, node, block_hash): + t = time() + while time() - t < 15: + try: + block = node.getblock(block_hash) + if block["confirmations"] > 0 and block["chainlock"]: + return + except: + # block might not be on the node yet + pass + sleep(0.1) + raise AssertionError("wait_for_chainlock timed out") + + def create_chained_txs(self, node, amount): + txid = node.sendtoaddress(node.getnewaddress(), amount) + tx = node.getrawtransaction(txid, 1) + inputs = [] + valueIn = 0 + for txout in tx["vout"]: + inputs.append({"txid": txid, "vout": txout["n"]}) + valueIn += txout["value"] + outputs = { + node.getnewaddress(): round(float(valueIn) - 0.0001, 6) + } + + rawtx = node.createrawtransaction(inputs, outputs) + rawtx = node.signrawtransaction(rawtx) + rawtxid = node.sendrawtransaction(rawtx["hex"]) + + return [txid, rawtxid] + + +if __name__ == '__main__': + LLMQChainLocksTest().main() diff --git a/qa/rpc-tests/llmq-cl-evospork.py b/qa/rpc-tests/llmq-cl-evospork.py new file mode 100755 index 0000000000..6c04d6828e --- /dev/null +++ b/qa/rpc-tests/llmq-cl-evospork.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import EvoZnodeTestFramework +from test_framework.util import * +from time import * + +''' +llmq-chainlocks.py + +Checks LLMQs based ChainLocks + +00 Mine quorum and produce chainlocks +01 Make sure the chainlocked tip does not change after invalidateblock +02 Make sure a rogue miner cannot inject a longer chain +10 Disable chainlocks +11 Make sure 01-09 work as usual +- +''' + +class LLMQChainLocksTest(EvoZnodeTestFramework): + def __init__(self): + super().__init__(6, 5, extra_args=[['-debug=chainlocks']] * 6) + self.sporkprivkey = "cW2YM2xaeCaebfpKguBahUAgEzLXgSserWRuD29kSyKHq1TTgwRQ" + + def run_test(self): + + for i in range(4): + self.mine_quorum() + + # mine single block, wait for chainlock + self.nodes[0].generate(1) + + self.wait_for_chainlock_tip_all_nodes() + self.payment_address = self.nodes[0].getaccountaddress("") + self.nodes[0].sendtoaddress(self.payment_address, 1) + + # mine many blocks, wait for chainlock + while self.nodes[0].getblockcount() < 1000: + self.nodes[0].generate(20) + self.wait_for_chainlock_tip_all_nodes() + + # assert that all blocks up until the tip are chainlocked + for h in range(1, self.nodes[0].getblockcount()): + block = self.nodes[0].getblock(self.nodes[0].getblockhash(h)) + assert(block['chainlock']) + + # cannot invalidate tip + current_tip = self.nodes[0].getbestblockhash() + self.nodes[0].invalidateblock(current_tip) + assert(current_tip == self.nodes[0].getbestblockhash()) + + ##### Disable chainlocks for 10 blocks + + self.nodes[0].importprivkey(self.sporkprivkey) + self.disable_chainlocks(self.nodes[0].getblockcount() + 10) + self.nodes[0].generate(1) + assert(False == self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) + + # can invalidate block now + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + assert(current_tip == self.nodes[0].getbestblockhash()) + + isolate_node(self.nodes[5]) + + ##### Enable chainlocks + + self.nodes[0].generate(10) + self.nodes[0].spork('list') + self.wait_for_chainlock_tip_all_nodes() + sporks = self.nodes[0].spork("list") + assert(not sporks["blockchain"]) + assert(not sporks["mempool"]) + assert(True == self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) + + # generate a longer chain on the isolated node then reconnect it back and make sure it picks the chainlocked chain + self.nodes[5].generate(20) + reconnect_isolated_node(self.nodes[5], 1) + self.nodes[0].generate(1) + current_tip = self.nodes[0].getbestblockhash() + timeout = 10 + while current_tip != self.nodes[5].getbestblockhash(): + assert timeout > 0, "Timed out when waiting for a chainlocked chain" + sleep(1) + timeout = timeout - 1 + + + def wait_for_chainlock_tip_all_nodes(self): + for node in self.nodes: + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock_tip(self, node): + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock(self, node, block_hash): + t = time() + while time() - t < 15: + try: + block = node.getblock(block_hash) + if block["confirmations"] > 0 and block["chainlock"]: + return + except: + # block might not be on the node yet + pass + sleep(0.1) + raise AssertionError("wait_for_chainlock timed out") + + def disable_chainlocks(self, till_height): + self.nodes[0].spork(self.sporkprivkey, self.payment_address, {"disable":{"chainlocks": till_height}}) + + +if __name__ == '__main__': + LLMQChainLocksTest().main() diff --git a/qa/rpc-tests/llmq-dkgerrors.py b/qa/rpc-tests/llmq-dkgerrors.py new file mode 100755 index 0000000000..ab671dbdc9 --- /dev/null +++ b/qa/rpc-tests/llmq-dkgerrors.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import EvoZnodeTestFramework +from test_framework.util import * + +''' +llmq-dkgerrors.py + +Simulate and check DKG errors + +''' + +class LLMQDKGErrors(EvoZnodeTestFramework): + def __init__(self): + super().__init__(6, 5) + + def run_test(self): + + # Mine one quorum without simulating any errors + qh = self.mine_quorum() + self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) + + # Lets omit the contribution + self.mninfo[0].node.quorum('dkgsimerror', 'contribution-omit', '1') + qh = self.mine_quorum(expected_contributions=4) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) + + # Lets lie in the contribution but provide a correct justification + self.mninfo[0].node.quorum('dkgsimerror', 'contribution-omit', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'contribution-lie', '1') + qh = self.mine_quorum(expected_contributions=5, expected_complaints=4, expected_justifications=1) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) + + # Lets lie in the contribution and then omit the justification + self.mninfo[0].node.quorum('dkgsimerror', 'justify-omit', '1') + qh = self.mine_quorum(expected_contributions=4, expected_complaints=4) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) + + # Heal some damage (don't get PoSe banned) + self.heal_masternodes(33) + + # Lets lie in the contribution and then also lie in the justification + self.mninfo[0].node.quorum('dkgsimerror', 'justify-omit', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'justify-lie', '1') + qh = self.mine_quorum(expected_contributions=4, expected_complaints=4, expected_justifications=1) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) + + # Lets lie about another MN + self.mninfo[0].node.quorum('dkgsimerror', 'contribution-lie', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'justify-lie', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'complain-lie', '1') + qh = self.mine_quorum(expected_contributions=5, expected_complaints=1, expected_justifications=4) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) + + # Lets omit 2 premature commitments + self.mninfo[0].node.quorum('dkgsimerror', 'complain-lie', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'commit-omit', '1') + self.mninfo[1].node.quorum('dkgsimerror', 'commit-omit', '1') + qh = self.mine_quorum(expected_contributions=5, expected_complaints=0, expected_justifications=0, expected_commitments=3) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) + + # Lets lie in 2 premature commitments + self.mninfo[0].node.quorum('dkgsimerror', 'commit-omit', '0') + self.mninfo[1].node.quorum('dkgsimerror', 'commit-omit', '0') + self.mninfo[0].node.quorum('dkgsimerror', 'commit-lie', '1') + self.mninfo[1].node.quorum('dkgsimerror', 'commit-lie', '1') + qh = self.mine_quorum(expected_contributions=5, expected_complaints=0, expected_justifications=0, expected_commitments=3) + self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) + + def assert_member_valid(self, quorumHash, proTxHash, expectedValid): + q = self.nodes[0].quorum('info', 100, quorumHash, True) + for m in q['members']: + if m['proTxHash'] == proTxHash: + if expectedValid: + assert(m['valid']) + else: + assert(not m['valid']) + else: + assert(m['valid']) + + def heal_masternodes(self, blockCount): + # We're not testing PoSe here, so lets heal the MNs :) + for i in range(blockCount): + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + self.sync_all() + + +if __name__ == '__main__': + LLMQDKGErrors().main() diff --git a/qa/rpc-tests/llmq-is-cl-conflicts.py b/qa/rpc-tests/llmq-is-cl-conflicts.py new file mode 100755 index 0000000000..ee876ec31d --- /dev/null +++ b/qa/rpc-tests/llmq-is-cl-conflicts.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +from test_framework.blocktools import get_masternode_payment, create_coinbase, create_block +from test_framework.mininode import * +from test_framework.test_framework import EvoZnodeTestFramework +from test_framework.util import * +from time import * + +''' +llmq-is-cl-conflicts.py + +Checks conflict handling between ChainLocks and InstantSend + +''' + +class TestNode(SingleNodeConnCB): + def __init__(self): + SingleNodeConnCB.__init__(self) + self.clsigs = {} + self.islocks = {} + + def send_clsig(self, clsig): + hash = uint256_from_str(hash256(clsig.serialize())) + self.clsigs[hash] = clsig + + inv = msg_inv([CInv(29, hash)]) + self.send_message(inv) + + def send_islock(self, islock): + hash = uint256_from_str(hash256(islock.serialize())) + self.islocks[hash] = islock + + inv = msg_inv([CInv(30, hash)]) + self.send_message(inv) + + def on_getdata(self, conn, message): + for inv in message.inv: + if inv.hash in self.clsigs: + self.send_message(self.clsigs[inv.hash]) + if inv.hash in self.islocks: + self.send_message(self.islocks[inv.hash]) + + +class LLMQ_IS_CL_Conflicts(EvoZnodeTestFramework): + def __init__(self): + super().__init__(6, 5) + #disable_mocktime() + + def run_test(self): + + self.test_node = TestNode() + self.test_node.add_connection(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node)) + NetworkThread().start() # Start up network handling in another thread + self.test_node.wait_for_verack() + + self.mine_quorum() + + # mine single block, wait for chainlock + self.nodes[0].generate(1) + self.wait_for_chainlock_tip_all_nodes() + + self.test_chainlock_overrides_islock(False) + self.test_chainlock_overrides_islock(True) + self.test_islock_overrides_nonchainlock() + + def test_chainlock_overrides_islock(self, test_block_conflict): + # create three raw TXs, they will conflict with each other + rawtx1 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)['hex'] + rawtx2 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)['hex'] + rawtx3 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)['hex'] + rawtx1_obj = FromHex(CTransaction(), rawtx1) + rawtx2_obj = FromHex(CTransaction(), rawtx2) + rawtx3_obj = FromHex(CTransaction(), rawtx3) + + rawtx1_txid = self.nodes[0].sendrawtransaction(rawtx1) + rawtx2_txid = encode(hash256(hex_str_to_bytes(rawtx2))[::-1], 'hex_codec').decode('ascii') + rawtx3_txid = encode(hash256(hex_str_to_bytes(rawtx3))[::-1], 'hex_codec').decode('ascii') + + # Create a chained TX on top of tx1 + inputs = [] + n = 0 + for out in rawtx1_obj.vout: + if out.nValue == 100000000: + inputs.append({"txid": rawtx1_txid, "vout": n}) + n += 1 + rawtx4 = self.nodes[0].createrawtransaction(inputs, {self.nodes[0].getnewaddress(): 0.999}) + rawtx4 = self.nodes[0].signrawtransaction(rawtx4)['hex'] + rawtx4_txid = self.nodes[0].sendrawtransaction(rawtx4) + + for node in self.nodes: + self.wait_for_instantlock(rawtx1_txid, node) + self.wait_for_instantlock(rawtx4_txid, node) + + block = self.create_block(self.nodes[0], [rawtx2_obj]) + if test_block_conflict: + submit_result = self.nodes[0].submitblock(ToHex(block)) + assert(submit_result == "conflict-tx-lock") + + cl = self.create_chainlock(self.nodes[0].getblockcount() + 1, block.sha256) + self.test_node.send_clsig(cl) + + # Give the CLSIG some time to propagate. We unfortunately can't check propagation here as "getblock/getblockheader" + # is required to check for CLSIGs, but this requires the block header to be propagated already + sleep(1) + + # The block should get accepted now, and at the same time prune the conflicting ISLOCKs + submit_result = self.nodes[1].submitblock(ToHex(block)) + if test_block_conflict: + assert(submit_result == "duplicate") + else: + assert(submit_result is None) + + for node in self.nodes: + self.wait_for_chainlock(node, "%064x" % block.sha256) + + # Create a chained TX on top of tx2 + inputs = [] + n = 0 + for out in rawtx2_obj.vout: + if out.nValue == 100000000: + inputs.append({"txid": rawtx2_txid, "vout": n}) + n += 1 + rawtx5 = self.nodes[0].createrawtransaction(inputs, {self.nodes[0].getnewaddress(): 0.999}) + rawtx5 = self.nodes[0].signrawtransaction(rawtx5)['hex'] + rawtx5_txid = self.nodes[0].sendrawtransaction(rawtx5) + for node in self.nodes: + self.wait_for_instantlock(rawtx5_txid, node) + + # Lets verify that the ISLOCKs got pruned + for node in self.nodes: + assert_raises_jsonrpc(-5, "No such mempool or blockchain transaction", node.getrawtransaction, rawtx1_txid, True) + assert_raises_jsonrpc(-5, "No such mempool or blockchain transaction", node.getrawtransaction, rawtx4_txid, True) + rawtx = node.getrawtransaction(rawtx2_txid, True) + assert(rawtx['chainlock']) + assert(rawtx['instantlock']) + assert(not rawtx['instantlock_internal']) + + def test_islock_overrides_nonchainlock(self): + # create two raw TXs, they will conflict with each other + rawtx1 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)['hex'] + rawtx2 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)['hex'] + + rawtx1_txid = encode(hash256(hex_str_to_bytes(rawtx1))[::-1], 'hex_codec').decode('ascii') + rawtx2_txid = encode(hash256(hex_str_to_bytes(rawtx2))[::-1], 'hex_codec').decode('ascii') + + # Create an ISLOCK but don't broadcast it yet + islock = self.create_islock(rawtx2) + + # Stop enough MNs so that ChainLocks don't work anymore + for i in range(3): + self.stop_node(len(self.nodes) - 1) + self.nodes.pop(len(self.nodes) - 1) + self.mninfo.pop(len(self.mninfo) - 1) + + # Send tx1, which will later conflict with the ISLOCK + self.nodes[0].sendrawtransaction(rawtx1) + + # fast forward 11 minutes, so that the TX is considered safe and included in the next block + set_mocktime(get_mocktime() + int(60 * 11)) + set_node_times(self.nodes, get_mocktime()) + + # Mine the conflicting TX into a block + good_tip = self.nodes[0].getbestblockhash() + self.nodes[0].generate(2) + self.sync_all() + + # Assert that the conflicting tx got mined and the locked TX is not valid + assert(self.nodes[0].getrawtransaction(rawtx1_txid, True)['confirmations'] > 0) + assert_raises_jsonrpc(-25, "Missing inputs", self.nodes[0].sendrawtransaction, rawtx2) + + # Send the ISLOCK, which should result in the last 2 blocks to be invalidated, even though the nodes don't know + # the locked transaction yet + self.test_node.send_islock(islock) + sleep(5) + + assert(self.nodes[0].getbestblockhash() == good_tip) + assert(self.nodes[1].getbestblockhash() == good_tip) + + # Send the actual transaction and mine it + self.nodes[0].sendrawtransaction(rawtx2) + self.nodes[0].generate(1) + self.sync_all() + + assert(self.nodes[0].getrawtransaction(rawtx2_txid, True)['confirmations'] > 0) + assert(self.nodes[1].getrawtransaction(rawtx2_txid, True)['confirmations'] > 0) + assert(self.nodes[0].getrawtransaction(rawtx2_txid, True)['instantlock']) + assert(self.nodes[1].getrawtransaction(rawtx2_txid, True)['instantlock']) + assert(self.nodes[0].getbestblockhash() != good_tip) + assert(self.nodes[1].getbestblockhash() != good_tip) + + def wait_for_chainlock_tip_all_nodes(self): + for node in self.nodes: + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock_tip(self, node): + tip = node.getbestblockhash() + self.wait_for_chainlock(node, tip) + + def wait_for_chainlock(self, node, block_hash): + t = time() + while time() - t < 15: + try: + block = node.getblockheader(block_hash) + if block["confirmations"] > 0 and block["chainlock"]: + return + except: + # block might not be on the node yet + pass + sleep(0.1) + raise AssertionError("wait_for_chainlock timed out") + + def create_block(self, node, vtx=[]): + bt = node.getblocktemplate() + height = bt['height'] + tip_hash = bt['previousblockhash'] + + coinbasevalue = bt['coinbasevalue'] + miner_address = node.getnewaddress() + mn_payee = bt['masternode'][0]['payee'] + + # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't + # include the template's transactions + bt_fees = 0 + for tx in bt['transactions']: + bt_fees += tx['fee'] + + new_fees = 0 + for tx in vtx: + in_value = 0 + out_value = 0 + for txin in tx.vin: + txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) + in_value += int(txout['value'] * COIN) + for txout in tx.vout: + out_value += txout.nValue + new_fees += in_value - out_value + + # fix fees + coinbasevalue -= bt_fees + coinbasevalue += new_fees + + mn_amount = get_masternode_payment(height, coinbasevalue) + miner_amount = coinbasevalue - mn_amount + + outputs = {miner_address: str(Decimal(miner_amount) / COIN)} + if mn_amount > 0: + outputs[mn_payee] = str(Decimal(mn_amount) / COIN) + + coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) + coinbase.vin = create_coinbase(height).vin + + # We can't really use this one as it would result in invalid merkle roots for masternode lists + if len(bt['coinbase_payload']) != 0: + cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) + coinbase.nVersion = 3 + coinbase.nType = 5 # CbTx + coinbase.vExtraPayload = cbtx.serialize() + + coinbase.calc_sha256() + + block = create_block(int(tip_hash, 16), coinbase, nTime=bt['curtime']) + block.vtx += vtx + + # Add quorum commitments from template + for tx in bt['transactions']: + tx2 = FromHex(CTransaction(), tx['data']) + if tx2.nType == 6: + block.vtx.append(tx2) + + block.hashMerkleRoot = block.calc_merkle_root() + block.solve() + return block + + def create_chainlock(self, height, blockHash): + request_id = "%064x" % uint256_from_str(hash256(ser_string(b"clsig") + struct.pack(" 0: + return True + return False + + def check_banned(self, mn): + info = self.nodes[0].protx('info', mn.proTxHash) + if info['state']['PoSeBanHeight'] != -1: + return True + return False + +if __name__ == '__main__': + LLMQSimplePoSeTest().main() diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 465c64832b..871ba9c76e 100644 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -14,7 +14,8 @@ import traceback import unittest from concurrent.futures import ThreadPoolExecutor -import time +from time import time, sleep +from .mininode import wait_until from .util import ( assert_equal, @@ -475,7 +476,7 @@ def get_znode_service(znode): return znode_ip_str + ":" + znode_port_str class ZnodeInfo: - def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, priv_key): + def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout): self.proTxHash = proTxHash self.ownerAddr = ownerAddr self.votingAddr = votingAddr @@ -484,7 +485,6 @@ def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator self.collateral_address = collateral_address self.collateral_txid = collateral_txid self.collateral_vout = collateral_vout - self.priv_key = priv_key class EvoZnodeTestFramework(BitcoinTestFramework): def __init__(self, num_nodes, masterodes_count, extra_args=None): @@ -540,7 +540,7 @@ def prepare_masternode(self, idx): proTxHash = self.nodes[0].protx('register', txid, collateral_vout, '127.0.0.1:%d' % port, ownerAddr, bls['public'], votingAddr, 0, rewardsAddr, address) self.nodes[0].generate(1) - self.mninfo.append(ZnodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, self.nodes[0].znode("genkey"))) + self.mninfo.append(ZnodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout)) self.sync_all() def remove_mastermode(self, idx): @@ -572,8 +572,7 @@ def start_masternodes(self): def do_start(idx): args = ['-znode=1', - '-zblsprivkey=%s' % self.mninfo[idx].keyOperator, - '-znodeprivkey=%s' % self.mninfo[idx].priv_key + '-znodeblsprivkey=%s' % self.mninfo[idx].keyOperator ] + self.extra_args[idx + start_idx] node = start_node(idx + start_idx, self.options.tmpdir, args) self.mninfo[idx].nodeIdx = idx + start_idx diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index e339e3e142..3e320b592c 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -116,7 +116,7 @@ def wait_to_sync(node, fast_znsync=False): time.sleep(0.2) if fast_znsync: # skip mnsync states - node.znsync("next") + node.evoznsync("next") tm += 0.2 assert(synced) @@ -384,7 +384,11 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary= if binary is None: binary = os.getenv("FIROD", "firod") args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-dandelion=0", "-usemnemonic=0", "-mocktime="+str(get_mocktime()) ] - # Don't try auto backups (they fail a lot when running tests) +#Useful args for debugging +# "screen", "--", +# "gdb", "-x", "/tmp/gdb_run", "--args", + +# Don't try auto backups (they fail a lot when running tests) args += [ "-createwalletbackups=0" ] if extra_args is not None: args.extend(extra_args) # Allow to redirect stderr to stdout in case we expect some non-critical warnings/errors printed to stderr diff --git a/src/Makefile.am b/src/Makefile.am index d53f6b7c68..af6ef6728f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -554,7 +554,6 @@ libbitcoin_consensus_a_SOURCES = \ script/script_error.cpp \ script/script_error.h \ serialize.h \ - chain_settings.h \ tinyformat.h \ uint256.cpp \ uint256.h \ diff --git a/src/chain_settings.h b/src/chain_settings.h deleted file mode 100644 index 7611a3d00b..0000000000 --- a/src/chain_settings.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef CHAIN_SETTINGS_H -#define CHAIN_SETTINGS_H - -namespace llmq { - -inline bool IsNewInstantSendEnabled() -{ - return false; -} - -inline bool IsChainlocksEnabled() -{ - return false; -} - -inline bool IsBlockFilteringEnabled() -{ - return false; -} - -inline int GetInstantsendMaxValue() -{ - return 500; -} - - -} - -#endif diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6699b06020..39407bd836 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -255,7 +255,8 @@ class CMainParams : public CChainParams { // evo znodes consensus.DIP0003Height = 278300; // Approximately June 22 2020, 12:00 UTC consensus.DIP0003EnforcementHeight = 284400; // Approximately July 13 2020, 12:00 UTC - consensus.DIP0008Height = INT_MAX; + consensus.DIP0003EnforcementHash = uint256S("0x8b8d7c05bb2d75f8c5e076cb6c10ef464e94ddcda2744740db03aeda2d6cc006"); + consensus.DIP0008Height = 341100; // Approximately Jan 28 2021, 11:00 UTC consensus.nEvoZnodeMinimumConfirmations = 15; // long living quorum params @@ -263,6 +264,8 @@ class CMainParams : public CChainParams { consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; consensus.nLLMQPowTargetSpacing = 5*60; + consensus.llmqChainLocks = Consensus::LLMQ_400_60; + consensus.llmqForInstantSend = Consensus::LLMQ_50_60; consensus.nMTPSwitchTime = SWITCH_TO_MTP_BLOCK_HEADER; consensus.nMTPStartBlock = 117564; @@ -393,7 +396,7 @@ class CMainParams : public CChainParams { // reorg consensus.nMaxReorgDepth = 5; - consensus.nMaxReorgDepthEnforcementBlock = 388000; + consensus.nMaxReorgDepthEnforcementBlock = 338000; // Dandelion related values. consensus.nDandelionEmbargoMinimum = DANDELION_EMBARGO_MINIMUM; @@ -517,7 +520,9 @@ class CTestNetParams : public CChainParams { // evo znodes consensus.DIP0003Height = 3340; consensus.DIP0003EnforcementHeight = 3800; - consensus.DIP0008Height = INT_MAX; + consensus.DIP0003EnforcementHash.SetNull(); + + consensus.DIP0008Height = 25000; consensus.nEvoZnodeMinimumConfirmations = 0; // long living quorum params @@ -526,6 +531,8 @@ class CTestNetParams : public CChainParams { consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; consensus.nLLMQPowTargetSpacing = 20; + consensus.llmqChainLocks = Consensus::LLMQ_10_70; + consensus.llmqForInstantSend = Consensus::LLMQ_10_70; consensus.nMTPSwitchTime = 1539172800; consensus.nMTPStartBlock = 1; @@ -724,7 +731,9 @@ class CRegTestParams : public CChainParams { // evo znodes consensus.DIP0003Height = 500; consensus.DIP0003EnforcementHeight = 550; - consensus.DIP0008Height = INT_MAX; + consensus.DIP0003EnforcementHash.SetNull(); + + consensus.DIP0008Height = 550; consensus.nEvoZnodeMinimumConfirmations = 1; // long living quorum params @@ -733,6 +742,8 @@ class CRegTestParams : public CChainParams { consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; consensus.nLLMQPowTargetSpacing = 1; + consensus.llmqChainLocks = Consensus::LLMQ_5_60; + consensus.llmqForInstantSend = Consensus::LLMQ_5_60; consensus.nMTPSwitchTime = INT_MAX; consensus.nMTPStartBlock = 0; @@ -836,7 +847,7 @@ class CRegTestParams : public CChainParams { // reorg consensus.nMaxReorgDepth = 4; - consensus.nMaxReorgDepthEnforcementBlock = 100; + consensus.nMaxReorgDepthEnforcementBlock = 300; // Dandelion related values. consensus.nDandelionEmbargoMinimum = 0; diff --git a/src/clientversion.h b/src/clientversion.h index 782bdaa46a..0b1c2bcae0 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,7 +16,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 14 -#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_REVISION 4 #define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build diff --git a/src/consensus/params.h b/src/consensus/params.h index 54edf5575f..ea5e0dda94 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -311,6 +311,7 @@ struct Params { /** block to switch to evo znode payments */ int DIP0003EnforcementHeight; + uint256 DIP0003EnforcementHash; /** block to start using chainlocks */ int DIP0008Height; diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index 447adeea64..660062db10 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -25,7 +25,7 @@ void CDSNotificationInterface::InitializeCurrentBlockTip() void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew) { - //llmq::chainLocksHandler->AcceptedBlockHeader(pindexNew); + llmq::chainLocksHandler->AcceptedBlockHeader(pindexNew); masternodeSync.AcceptedBlockHeader(pindexNew); } @@ -55,7 +55,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con return; //llmq::quorumInstantSendManager->UpdatedBlockTip(pindexNew); - //llmq::chainLocksHandler->UpdatedBlockTip(pindexNew); + llmq::chainLocksHandler->UpdatedBlockTip(pindexNew); //instantsend.UpdatedBlockTip(pindexNew); //governance.UpdatedBlockTip(pindexNew, connman); @@ -66,7 +66,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) { //llmq::quorumInstantSendManager->SyncTransaction(tx, pindex, posInBlock); - //llmq::chainLocksHandler->SyncTransaction(tx, pindex, posInBlock); + llmq::chainLocksHandler->SyncTransaction(tx, pindex, posInBlock); //instantsend.SyncTransaction(tx, pindex, posInBlock); } diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 40af646735..540a030ed6 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -552,15 +552,14 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde uiInterface.NotifyMasternodeListChanged(newList); } - // TODO: uncomment when DIP3 enforcement block hash is known - /*if (nHeight == consensusParams.DIP0003EnforcementHeight) { + if (nHeight == consensusParams.DIP0003EnforcementHeight) { if (!consensusParams.DIP0003EnforcementHash.IsNull() && consensusParams.DIP0003EnforcementHash != pindex->GetBlockHash()) { LogPrintf("CDeterministicMNManager::%s -- DIP3 enforcement block has wrong hash: hash=%s, expected=%s, nHeight=%d\n", __func__, pindex->GetBlockHash().ToString(), consensusParams.DIP0003EnforcementHash.ToString(), nHeight); return _state.DoS(100, false, REJECT_INVALID, "bad-dip3-enf-block"); } LogPrintf("CDeterministicMNManager::%s -- DIP3 is enforced now. nHeight=%d\n", __func__, nHeight); - }*/ + } LOCK(cs); CleanupCache(nHeight); diff --git a/src/evo/spork.cpp b/src/evo/spork.cpp index 517c9dfc5a..16ebde591f 100644 --- a/src/evo/spork.cpp +++ b/src/evo/spork.cpp @@ -248,4 +248,4 @@ bool CMempoolSporkManager::IsTransactionAllowed(const CTransaction &tx, CValidat return true; return ::IsTransactionAllowed(tx, chainTip->activeDisablingSporks, state); -} \ No newline at end of file +} diff --git a/src/evo/spork.h b/src/evo/spork.h index 4087513813..13640ca6c1 100644 --- a/src/evo/spork.h +++ b/src/evo/spork.h @@ -151,4 +151,25 @@ class CMempoolSporkManager ActiveSporkMap GetActiveSporks() const { return mempoolSporks; } }; +inline bool IsNewInstantSendEnabled() +{ + return false; +} + +inline bool IsChainlocksEnabled(const CBlockIndex *pindex) +{ + return CSporkManager::GetSporkManager()->IsFeatureEnabled(CSporkAction::featureChainlocks, pindex); +} + +inline bool IsBlockFilteringEnabled() +{ + return false; +} + +inline int GetInstantsendMaxValue() +{ + return 500; +} + + #endif \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index 5fb021027d..cadccbd95f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -262,7 +262,6 @@ void Shutdown() pwalletMain->Flush(false); #endif GenerateBitcoins(false, 0, Params()); - MapPort(false); UnregisterValidationInterface(peerLogic.get()); peerLogic.reset(); @@ -2012,7 +2011,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) "Please add txindex=1 to your configuration and start with -reindex"); } - // evo znode system if(fLiteMode && fMasternodeMode) { return InitError(_("You can not start a znode in lite mode.")); @@ -2134,7 +2132,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Generate coins in the background GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams); - // ********************************************************* Step 13: Znode - obsoleted // ********************************************************* Step 14: finished diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp index ccf86507df..42a33efcfe 100644 --- a/src/llmq/quorums_chainlocks.cpp +++ b/src/llmq/quorums_chainlocks.cpp @@ -7,7 +7,7 @@ #include "quorums_instantsend.h" #include "quorums_signing.h" #include "quorums_utils.h" -#include "chain_settings.h" +#include "evo/spork.h" #include "chain.h" #include "masternode-sync.h" @@ -74,8 +74,10 @@ bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLoc void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { - if (!llmq::IsChainlocksEnabled()) { - return; + { + LOCK(cs_main); + if(!IsChainlocksEnabled(chainActive.Tip())) + return; } if (strCommand == NetMsgType::CLSIG) { @@ -210,13 +212,14 @@ void CChainLocksHandler::CheckActiveState() { LOCK(cs_main); fDIP0008Active = chainActive.Height() >= Params().GetConsensus().DIP0008Height; + isChainLocksActive = IsChainlocksEnabled(chainActive.Tip()); } LOCK(cs); bool oldIsEnforced = isEnforced; - isChainLocksActive = llmq::IsChainlocksEnabled(); + // TODO remove this after DIP8 is active - bool fEnforcedBySpork = (Params().NetworkIDString() == CBaseChainParams::TESTNET) && (llmq::IsChainlocksEnabled()); + bool fEnforcedBySpork = (Params().NetworkIDString() == CBaseChainParams::TESTNET) && (isChainLocksActive); isEnforced = (fDIP0008Active && isChainLocksActive) || fEnforcedBySpork; if (!oldIsEnforced && isEnforced) { @@ -519,8 +522,10 @@ void CChainLocksHandler::EnforceBestChainLock() } CValidationState state; - if (activateNeeded && !ActivateBestChain(state, Params())) { - LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); + if (activateNeeded) { + LogPrintf("CChainLocksHandler::%s -- Activated best chain tip: %s\n", __func__, currentBestChainLockBlockIndex->GetBlockHash().ToString()); + if (!ActivateBestChain(state, Params())) + LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); } const CBlockIndex* pindexNotify = nullptr; diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp index 7145c79d35..f9baf638af 100644 --- a/src/llmq/quorums_init.cpp +++ b/src/llmq/quorums_init.cpp @@ -35,12 +35,9 @@ void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler, bool unitTests, bool f quorumManager = new CQuorumManager(evoDb, *blsWorker, *quorumDKGSessionManager); quorumSigSharesManager = new CSigSharesManager(); quorumSigningManager = new CSigningManager(*llmqDb, unitTests); - /* chainLocksHandler = new CChainLocksHandler(scheduler); - quorumInstantSendManager = new CInstantSendManager(*llmqDb); - */ - chainLocksHandler = nullptr; - quorumInstantSendManager = nullptr; +// quorumInstantSendManager = new CInstantSendManager(*llmqDb); + quorumInstantSendManager = nullptr; } void DestroyLLMQSystem() @@ -81,10 +78,10 @@ void StartLLMQSystem() quorumSigSharesManager->RegisterAsRecoveredSigsListener(); quorumSigSharesManager->StartWorkerThread(); } - /* if (chainLocksHandler) { chainLocksHandler->Start(); } + /* if (quorumInstantSendManager) { quorumInstantSendManager->Start(); } @@ -97,10 +94,10 @@ void StopLLMQSystem() if (quorumInstantSendManager) { quorumInstantSendManager->Stop(); } + */ if (chainLocksHandler) { chainLocksHandler->Stop(); } - */ if (quorumSigSharesManager) { quorumSigSharesManager->StopWorkerThread(); quorumSigSharesManager->UnregisterAsRecoveredSigsListener(); diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 5a653a8760..826cc1b9ed 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -5,7 +5,7 @@ #include "quorums_chainlocks.h" #include "quorums_instantsend.h" #include "quorums_utils.h" -#include "chain_settings.h" +#include "evo/spork.h" #include "bls/bls_batchverifier.h" #include "chainparams.h" @@ -1143,7 +1143,7 @@ void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) // TODO remove this after DIP8 has activated bool fDIP0008Active = chainActive.Height() >= Params().GetConsensus().DIP0008Height; - if (IsChainlocksEnabled() && fDIP0008Active) { + if (fDIP0008Active && IsChainlocksEnabled(pindexNew)) { // Nothing to do here. We should keep all islocks and let chainlocks handle them. return; } diff --git a/src/miner.cpp b/src/miner.cpp index 7c37783692..6973cd4a36 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -48,6 +48,7 @@ #include "evo/cbtx.h" #include "evo/simplifiedmns.h" #include "evo/deterministicmns.h" +#include "evo/spork.h" #include "llmq/quorums_blockprocessor.h" @@ -184,6 +185,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc nHeight = pindexPrev->nHeight + 1; bool fDIP0003Active_context = nHeight >= chainparams.GetConsensus().DIP0003Height; + bool fDIP0008Active_context = nHeight >= chainparams.GetConsensus().DIP0008Height; pblock->nTime = GetAdjustedTime(); bool fMTP = pblock->nTime >= params.nMTPSwitchTime; @@ -258,15 +260,12 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc CCbTx cbTx; - /* + if (fDIP0008Active_context) { cbTx.nVersion = 2; } else { - */ cbTx.nVersion = 1; - /* } - */ cbTx.nHeight = nHeight; @@ -274,13 +273,11 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state)) { throw std::runtime_error(strprintf("%s: CalcCbTxMerkleRootMNList failed: %s", __func__, FormatStateMessage(state))); } - /* if (fDIP0008Active_context) { if (!CalcCbTxMerkleRootQuorums(*pblock, pindexPrev, cbTx.merkleRootQuorums, state)) { throw std::runtime_error(strprintf("%s: CalcCbTxMerkleRootQuorums failed: %s", __func__, FormatStateMessage(state))); } } - */ SetTxPayload(coinbaseTx, cbTx); } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4cdc02eb12..92c1fcf4fe 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -971,9 +971,9 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return llmq::quorumDKGSessionManager->AlreadyHave(inv); case MSG_QUORUM_RECOVERED_SIG: return llmq::quorumSigningManager->AlreadyHave(inv); - /* case MSG_CLSIG: return llmq::chainLocksHandler->AlreadyHave(inv); + /* case MSG_ISLOCK: return llmq::quorumInstantSendManager->AlreadyHave(inv); */ @@ -1253,6 +1253,13 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } + if (!pushed && (inv.type == MSG_CLSIG)) { + llmq::CChainLockSig o; + if (llmq::chainLocksHandler->GetChainLockByHash(inv.hash, o)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::CLSIG, o)); + pushed = true; + } + } if (!pushed) vNotFound.push_back(inv); } @@ -2556,6 +2563,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fNewBlock = false; // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) + ProcessNewBlock(chainparams, pblock, true, &fNewBlock); if (fNewBlock) pfrom->nLastBlockTime = GetTime(); @@ -2989,7 +2997,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr llmq::quorumDKGSessionManager->ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumSigSharesManager->ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumSigningManager->ProcessMessage(pfrom, strCommand, vRecv, connman); - //llmq::chainLocksHandler->ProcessMessage(pfrom, strCommand, vRecv, connman); + llmq::chainLocksHandler->ProcessMessage(pfrom, strCommand, vRecv, connman); //llmq::quorumInstantSendManager->ProcessMessage(pfrom, strCommand, vRecv, connman); } else { // Ignore unknown commands for extensibility diff --git a/src/protocol.cpp b/src/protocol.cpp index fff97e2597..a3766a953a 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -40,7 +40,6 @@ namespace NetMsgType { const char *GETBLOCKTXN="getblocktxn"; const char *BLOCKTXN="blocktxn"; const char *DANDELIONTX="dandeliontx"; -//znode const char *SYNCSTATUSCOUNT="ssc"; const char *GETMNLISTDIFF="getmnlistd"; const char *MNLISTDIFF="mnlistdiff"; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f225b8971a..30c4112d8f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -26,6 +26,9 @@ #include "evo/deterministicmns.h" #include "evo/cbtx.h" +#include "llmq/quorums_chainlocks.h" +#include "llmq/quorums_instantsend.h" + #include #include @@ -105,6 +108,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.push_back(Pair("chainlock", llmq::chainLocksHandler->HasChainLock(blockindex->nHeight, blockindex->GetBlockHash()))); return result; } @@ -152,6 +156,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.push_back(Pair("chainlock", llmq::chainLocksHandler->HasChainLock(blockindex->nHeight, blockindex->GetBlockHash()))); return result; } diff --git a/src/validation.cpp b/src/validation.cpp index 6ace20462e..1ea0851dfe 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2728,8 +2728,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if (!ProcessSpecialTxsInBlock(block, pindex, state, fJustCheck, fScriptChecks)) { - return error("ConnectBlock(): ProcessSpecialTxsInBlock for block %s failed with %s", - pindex->GetBlockHash().ToString(), FormatStateMessage(state)); + return error("ConnectBlock(): ProcessSpecialTxsInBlock for block %s at height %i failed with %s", + pindex->GetBlockHash().ToString(), pindex->nHeight, FormatStateMessage(state)); } // END ZNODE diff --git a/src/wallet/lelantusjoinsplitbuilder.cpp b/src/wallet/lelantusjoinsplitbuilder.cpp index 55633d3e88..ea7dd3c31d 100644 --- a/src/wallet/lelantusjoinsplitbuilder.cpp +++ b/src/wallet/lelantusjoinsplitbuilder.cpp @@ -203,7 +203,7 @@ CWalletTx LelantusJoinSplitBuilder::Build( isSigmaToLelantusJoinSplit = true; } - } catch (std::runtime_error) { + } catch (std::runtime_error const &) { } if(required > 0) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b7fc2bfe1b..522d07eb8c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3309,7 +3309,6 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const void CWallet::AvailableCoins(vector &vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const { static const int ZNODE_COIN_REQUIRED = 1000; - vCoins.clear(); CoinType nCoinType = coinControl ? coinControl->nCoinType : CoinType::ALL_COINS; @@ -6881,7 +6880,7 @@ CAmount CWallet::EstimateJoinSplitFee(CAmount required, bool subtractFeeFromAmou consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl, true); currentRequired -= inputFromSigma; } - } catch (std::runtime_error) { + } catch (std::runtime_error const &) { } if (currentRequired > 0) { @@ -8489,4 +8488,4 @@ bool CompHeight(const CZerocoinEntry &a, const CZerocoinEntry &b) { return a.nHe bool CompSigmaHeight(const CSigmaEntry &a, const CSigmaEntry &b) { return a.nHeight < b.nHeight; } bool CompID(const CZerocoinEntry &a, const CZerocoinEntry &b) { return a.id < b.id; } -bool CompSigmaID(const CSigmaEntry &a, const CSigmaEntry &b) { return a.id < b.id; } \ No newline at end of file +bool CompSigmaID(const CSigmaEntry &a, const CSigmaEntry &b) { return a.id < b.id; }