Fix consensus quorum comparison

This commit is contained in:
Brad Chase
2017-07-13 12:02:46 -04:00
committed by seelabs
parent 01b4d5cdd4
commit df086301b6
2 changed files with 107 additions and 37 deletions

View File

@@ -102,7 +102,7 @@ checkConsensusReached(
std::size_t currentPercentage = (agreeing * 100) / total; std::size_t currentPercentage = (agreeing * 100) / total;
return currentPercentage > minConsensusPct; return currentPercentage >= minConsensusPct;
} }
ConsensusState ConsensusState

View File

@@ -175,30 +175,30 @@ public:
} }
void void
testSlowPeer() testSlowPeers()
{ {
using namespace csf; using namespace csf;
using namespace std::chrono; using namespace std::chrono;
// Run two tests // Several tests of a complete trust graph with a subset of peers
// 1. The slow peer is participating in consensus // that have significantly longer network delays to the rest of the
// 2. The slow peer is just observing // network
for (auto isParticipant : {true, false}) // Test when a slow peer doesn't delay a consensus quorum (4/5 agree)
{ {
ConsensusParms parms; ConsensusParms parms;
auto tg = TrustGraph::makeComplete(5); auto tg = TrustGraph::makeComplete(5);
// Peers 0 is slow, 1-4 are fast
// This choice is based on parms.minCONSENSUS_PCT of 80
Sim sim(parms, tg, topology(tg, [&](PeerID i, PeerID j) { Sim sim(parms, tg, topology(tg, [&](PeerID i, PeerID j) {
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2; auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
return round<milliseconds>( return round<milliseconds>(
delayFactor * parms.ledgerGRANULARITY); delayFactor * parms.ledgerGRANULARITY);
})); }));
sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant; // All peers submit their own ID as a transaction and relay it
// to peers
// All peers submit their own ID as a transaction and relay it to
// peers
for (auto& p : sim.peers) for (auto& p : sim.peers)
{ {
p.submit(Tx{p.id}); p.submit(Tx{p.id});
@@ -213,26 +213,94 @@ public:
auto const& lgrID = p.prevLedgerID(); auto const& lgrID = p.prevLedgerID();
BEAST_EXPECT(lgrID.seq == 1); BEAST_EXPECT(lgrID.seq == 1);
// If peer 0 is participating BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
BEAST_EXPECT(p.prevRoundTime() == sim.peers[0].prevRoundTime());
BEAST_EXPECT(lgrID.txs.find(Tx{0}) == lgrID.txs.end());
for (std::uint32_t i = 2; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
// Matches peer 0 ledger
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
}
BEAST_EXPECT(
sim.peers[0].openTxs.find(Tx{0}) != sim.peers[0].openTxs.end());
}
// Test when the slow peers delay a consensus quorum (4/6 agree)
{
// Run two tests
// 1. The slow peers are participating in consensus
// 2. The slow peers are just observing
for (auto isParticipant : {true, false})
{
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(6);
// Peers 0,1 are slow, 2-5 are fast
// This choice is based on parms.minCONSENSUS_PCT of 80
Sim sim(
parms, tg, topology(tg, [&](PeerID i, PeerID j) {
auto delayFactor = (i <= 1 || j <= 1) ? 1.1 : 0.2;
return round<milliseconds>(
delayFactor * parms.ledgerGRANULARITY);
}));
sim.peers[0].proposing_ = sim.peers[0].validating_ =
isParticipant;
sim.peers[1].proposing_ = sim.peers[1].validating_ =
isParticipant;
// All peers submit their own ID as a transaction and relay it
// to peers
for (auto& p : sim.peers)
{
p.submit(Tx{p.id});
}
sim.run(1);
// Verify all peers have same LCL but are missing transaction 0
// which was not received by all peers before the ledger closed
for (auto& p : sim.peers)
{
auto const& lgrID = p.prevLedgerID();
BEAST_EXPECT(lgrID.seq == 1);
// If peer 0,1 are participating
if (isParticipant) if (isParticipant)
{ {
BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1); BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
// Peer 0 closes first because it sees a quorum of agreeing // Due to the network link delay settings
// positions from all other peers in one hop (1->0, 2->0, // Peer 0 initially proposes {0}
// ..) The other peers take an extra timer period before // Peer 1 initially proposes {1}
// they find that Peer 0 agrees with them ( 1->0->1, // Peers 2-5 initially propose {2,3,4,5}
// 2->0->2, ...) // Since peers 2-5 agree, 4/6 > the initial 50%
if (p.id != 0) // threshold on disputed transactions, so Peer 0 and 1
// change their position to match peers 2-5 and declare
// consensus now that 5/6 proposed positions match
// (themselves and peers 2-5).
//
// Peers 2-5 do not change position, since tx 0 or tx 1
// have less than the 50% initial threshold. They also
// cannot declare consensus, since 4/6 < 80% threshold
// agreement on current positions. Instead, they have
// to wait an additional timerEntry call for the updated
// peer 0 and peer 1 positions to arrive. Once they do,
// now peers 2-5 see complete agreement and declare
// consensus
if (p.id > 1)
BEAST_EXPECT( BEAST_EXPECT(
p.prevRoundTime() > sim.peers[0].prevRoundTime()); p.prevRoundTime() >
sim.peers[0].prevRoundTime());
} }
else // peer 0 is not participating else // peer 0,1 are not participating
{ {
auto const proposers = p.prevProposers(); auto const proposers = p.prevProposers();
if (p.id == 0) if (p.id <= 1)
BEAST_EXPECT(proposers == sim.peers.size() - 1);
else
BEAST_EXPECT(proposers == sim.peers.size() - 2); BEAST_EXPECT(proposers == sim.peers.size() - 2);
else
BEAST_EXPECT(proposers == sim.peers.size() - 3);
// so all peers should have closed together // so all peers should have closed together
BEAST_EXPECT( BEAST_EXPECT(
@@ -240,13 +308,15 @@ public:
} }
BEAST_EXPECT(lgrID.txs.find(Tx{0}) == lgrID.txs.end()); BEAST_EXPECT(lgrID.txs.find(Tx{0}) == lgrID.txs.end());
for (std::uint32_t i = 1; i < sim.peers.size(); ++i) for (std::uint32_t i = 2; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end()); BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
// Matches peer 0 ledger // Matches peer 0 ledger
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs); BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
} }
BEAST_EXPECT( BEAST_EXPECT(
sim.peers[0].openTxs.find(Tx{0}) != sim.peers[0].openTxs.end()); sim.peers[0].openTxs.find(Tx{0}) !=
sim.peers[0].openTxs.end());
}
} }
} }
@@ -618,7 +688,7 @@ public:
testStandalone(); testStandalone();
testPeersAgree(); testPeersAgree();
testSlowPeer(); testSlowPeers();
testCloseTimeDisagree(); testCloseTimeDisagree();
testWrongLCL(); testWrongLCL();
testFork(); testFork();