Skip to content

Commit

Permalink
Implement and enforce DIP6 commitments (#2477)
Browse files Browse the repository at this point in the history
* Add LLMQ parameters to consensus params

* Add DIP6 quorum commitment special TX

* Implement CQuorumBlockProcessor which validates and handles commitments

* Add quorum commitments to new blocks

* Propagate QFCOMMITMENT messages to all nodes

* Allow special transactions in blocks which have no inputs/outputs

But only for TRANSACTION_QUORUM_COMMITMENT for now.

* Add quorum commitments to self-crafted blocks in DIP3 tests

* Add simple fork logic for current testnet

This should avoid a fork on the current testnet. It only applies to the
current chain which activated DIP3 at height 264000 and block
00000048e6e71d4bd90e7c456dcb94683ae832fcad13e1760d8283f7e89f332f.

When we revert the chain to retest the DIP3 deployment, this fork logic
can be removed again.

* Use quorumVvecHash instead of quorumHash to make null commitments unique

Implementation of dashpay/dips#31

* Re-add quorum commitments after pruning mempool selected blocks

* Refactor CQuorumBlockProcessor::ProcessBlock to have less nested if/else statements

Also add BEGIN/END markers for temporary code.

* Add comments/documentation to LLMQParams

* Move code which determines if a commitment is required into IsCommitmentRequired

This should make the code easier to read and also removes some duplication.

The also changes the error types that are possible from 3 to 2 now. Instead
of having "bad-qc-already-mined" and "bad-qc-not-mining-phase", there is
only "bad-qc-not-allowed" now.

* Use new parameter from consensus parames for the temporary fork
  • Loading branch information
codablock authored Nov 23, 2018
1 parent 6c190d1 commit 22b5952
Show file tree
Hide file tree
Showing 24 changed files with 1,085 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ file(GLOB SOURCE_FILES
src/*.h
src/evo/*.h
src/evo/*.cpp
src/llmq/*.h
src/llmq/*.cpp
src/rpc/*.cpp
src/rpc/*.h
)
Expand Down
7 changes: 7 additions & 0 deletions qa/rpc-tests/dip3-deterministicmns.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,13 @@ def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=

block = create_block(int(tip_hash, 16), coinbase)
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()
result = node.submitblock(ToHex(block))
Expand Down
8 changes: 8 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ BITCOIN_CORE_H = \
keystore.h \
dbwrapper.h \
limitedmap.h \
llmq/quorums_commitment.h \
llmq/quorums_blockprocessor.h \
llmq/quorums_utils.h \
llmq/quorums_init.h \
masternode.h \
masternode-payments.h \
masternode-sync.h \
Expand Down Expand Up @@ -243,6 +247,10 @@ libdash_server_a_SOURCES = \
governance-validators.cpp \
governance-vote.cpp \
governance-votedb.cpp \
llmq/quorums_commitment.cpp \
llmq/quorums_blockprocessor.cpp \
llmq/quorums_utils.cpp \
llmq/quorums_init.cpp \
masternode.cpp \
masternode-payments.cpp \
masternode-sync.cpp \
Expand Down
76 changes: 75 additions & 1 deletion src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,61 @@ static CBlock FindDevNetGenesisBlock(const Consensus::Params& params, const CBlo
assert(false);
}

// this one is for testing only
static Consensus::LLMQParams llmq10_60 = {
.type = Consensus::LLMQ_10_60,
.name = "llmq_10",
.size = 10,
.minSize = 6,
.threshold = 6,

.dkgInterval = 24, // one DKG per hour
.dkgPhaseBlocks = 2,
.dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 18,
};

static Consensus::LLMQParams llmq50_60 = {
.type = Consensus::LLMQ_50_60,
.name = "llmq_50_60",
.size = 50,
.minSize = 40,
.threshold = 30,

.dkgInterval = 24, // one DKG per hour
.dkgPhaseBlocks = 2,
.dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 18,
};

static Consensus::LLMQParams llmq400_60 = {
.type = Consensus::LLMQ_400_60,
.name = "llmq_400_51",
.size = 400,
.minSize = 300,
.threshold = 240,

.dkgInterval = 24 * 12, // one DKG every 12 hours
.dkgPhaseBlocks = 4,
.dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 28,
};

// Used for deployment and min-proto-version signalling, so it needs a higher threshold
static Consensus::LLMQParams llmq400_85 = {
.type = Consensus::LLMQ_400_85,
.name = "llmq_400_85",
.size = 400,
.minSize = 350,
.threshold = 340,

.dkgInterval = 24 * 24, // one DKG every 24 hours
.dkgPhaseBlocks = 4,
.dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 48, // give it a larger mining window to make sure it is mined
};


/**
* Main network
*/
Expand Down Expand Up @@ -219,6 +274,11 @@ class CMainParams : public CChainParams {

vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));

// long living quorum params
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;

fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
fRequireStandard = true;
Expand Down Expand Up @@ -378,6 +438,11 @@ class CTestNetParams : public CChainParams {
// Testnet Dash BIP44 coin type is '1' (All coin's testnet default)
nExtCoinType = 1;

// long living quorum params
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;

// This is temporary until we reset testnet for retesting of the full DIP3 deployment
consensus.nTemporaryTestnetForkDIP3Height = 264000;
consensus.nTemporaryTestnetForkHeight = 273000;
Expand Down Expand Up @@ -524,6 +589,11 @@ class CDevNetParams : public CChainParams {
// Testnet Dash BIP44 coin type is '1' (All coin's testnet default)
nExtCoinType = 1;

// long living quorum params
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;

fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
fRequireStandard = false;
Expand Down Expand Up @@ -675,7 +745,11 @@ class CRegTestParams : public CChainParams {

// Regtest Dash BIP44 coin type is '1' (All coin's testnet default)
nExtCoinType = 1;
}

// long living quorum params
consensus.llmqs[Consensus::LLMQ_10_60] = llmq10_60;
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
}

void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
{
Expand Down
62 changes: 62 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,66 @@ struct BIP9Deployment {
int64_t nThreshold;
};

enum LLMQType : uint8_t
{
LLMQ_NONE = 0xff,

LLMQ_50_60 = 1, // 50 members, 30 (60%) threshold, one per hour
LLMQ_400_60 = 2, // 400 members, 240 (60%) threshold, one every 12 hours
LLMQ_400_85 = 3, // 400 members, 340 (85%) threshold, one every 24 hours

// for testing only
LLMQ_10_60 = 100, // 10 members, 6 (60%) threshold, one per hour
};

// Configures a LLMQ and its DKG
// See https://github.com/dashpay/dips/blob/master/dip-0006.md for more details
struct LLMQParams {
LLMQType type;

// not consensus critical, only used in logging, RPC and UI
std::string name;

// the size of the quorum, e.g. 50 or 400
int size;

// The minimum number of valid members after the DKK. If less members are determined valid, no commitment can be
// created. Should be higher then the threshold to allow some room for failing nodes, otherwise quorum might end up
// not being able to ever created a recovered signature if more nodes fail after the DKG
int minSize;

// The threshold required to recover a final signature. Should be at least 50%+1 of the quorum size. This value
// also controls the size of the public key verification vector and has a large influence on the performance of
// recovery. It also influences the amount of minimum messages that need to be exchanged for a single signing session.
// This value has the most influence on the security of the quorum. The number of total malicious masternodes
// required to negatively influence signing sessions highly correlates to the threshold percentage.
int threshold;

// The interval in number blocks for DKGs and the creation of LLMQs. If set to 24 for example, a DKG will start
// every 24 blocks, which is approximately once every hour.
int dkgInterval;

// The number of blocks per phase in a DKG session. There are 6 phases plus the mining phase that need to be processed
// per DKG. Set this value to a number of blocks so that each phase has enough time to propagate all required
// messages to all members before the next phase starts. If blocks are produced too fast, whole DKG sessions will
// fail.
int dkgPhaseBlocks;

// The starting block inside the DKG interval for when mining of commitments starts. The value is inclusive.
// Starting from this block, the inclusion of (possibly null) commitments is enforced until the first non-null
// commitment is mined. The chosen value should be at least 5 * dkgPhaseBlocks so that it starts right after the
// finalization phase.
int dkgMiningWindowStart;

// The ending block inside the DKG interval for when mining of commitments ends. The value is inclusive.
// Choose a value so that miners have enough time to receive the commitment and mine it. Also take into consideration
// that miners might omit real commitments and revert to always including null commitments. The mining window should
// be large enough so that other miners have a chance to produce a block containing a non-null commitment. The window
// should at the same time not be too large so that not too much space is wasted with null commitments in case a DKG
// session failed.
int dkgMiningWindowEnd;
};

/**
* Parameters that influence chain consensus.
*/
Expand Down Expand Up @@ -96,6 +156,8 @@ struct Params {
int nHighSubsidyBlocks{0};
int nHighSubsidyFactor{1};

std::map<LLMQType, LLMQParams> llmqs;

// This is temporary until we reset testnet for retesting of the full DIP3 deployment
int nTemporaryTestnetForkDIP3Height{0};
uint256 nTemporaryTestnetForkDIP3BlockHash;
Expand Down
17 changes: 17 additions & 0 deletions src/evo/specialtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include "deterministicmns.h"
#include "specialtx.h"

#include "llmq/quorums_commitment.h"
#include "llmq/quorums_blockprocessor.h"

bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
{
if (tx.nVersion != 3 || tx.nType == TRANSACTION_NORMAL)
Expand All @@ -34,6 +37,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
return CheckProUpRevTx(tx, pindexPrev, state);
case TRANSACTION_COINBASE:
return CheckCbTx(tx, pindexPrev, state);
case TRANSACTION_QUORUM_COMMITMENT:
return true; // can't really check much here. checks are done in ProcessBlock
}

return state.DoS(10, false, REJECT_INVALID, "bad-tx-type-check");
Expand All @@ -53,6 +58,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
return true; // handled in batches per block
case TRANSACTION_COINBASE:
return true; // nothing to do
case TRANSACTION_QUORUM_COMMITMENT:
return true; // handled per block
}

return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc");
Expand All @@ -72,6 +79,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
return true; // handled in batches per block
case TRANSACTION_COINBASE:
return true; // nothing to do
case TRANSACTION_QUORUM_COMMITMENT:
return true; // handled per block
}

return false;
Expand All @@ -97,6 +106,10 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV
return false;
}

if (!llmq::quorumBlockProcessor->ProcessBlock(block, pindex->pprev, state)) {
return false;
}

return true;
}

Expand All @@ -109,6 +122,10 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex)
}
}

if (!llmq::quorumBlockProcessor->UndoBlock(block, pindex)) {
return false;
}

if (!deterministicMNManager->UndoBlock(block, pindex)) {
return false;
}
Expand Down
5 changes: 5 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@

#include "evo/deterministicmns.h"

#include "llmq/quorums_init.h"

#include <stdint.h>
#include <stdio.h>
#include <memory>
Expand Down Expand Up @@ -303,6 +305,7 @@ void PrepareShutdown()
pcoinsdbview = NULL;
delete pblocktree;
pblocktree = NULL;
llmq::DestroyLLMQSystem();
delete deterministicMNManager;
deterministicMNManager = NULL;
delete evoDb;
Expand Down Expand Up @@ -1713,6 +1716,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
delete pcoinsdbview;
delete pcoinscatcher;
delete pblocktree;
llmq::DestroyLLMQSystem();
delete deterministicMNManager;
delete evoDb;

Expand All @@ -1722,6 +1726,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
llmq::InitLLMQSystem(*evoDb);

if (fReindex) {
pblocktree->WriteReindexing(true);
Expand Down
Loading

0 comments on commit 22b5952

Please sign in to comment.