mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Compare commits
7 Commits
vlntb/inbo
...
hotfix2.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16c2ff97cc | ||
|
|
32043463a8 | ||
|
|
1e01cd34f7 | ||
|
|
e2fa5c1b7c | ||
|
|
fc0984d286 | ||
|
|
8b3dcd41f7 | ||
|
|
8f2f5310e2 |
2
.github/workflows/libxrpl.yml
vendored
2
.github/workflows/libxrpl.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: Check libXRPL compatibility with Clio
|
name: Check libXRPL compatibility with Clio
|
||||||
env:
|
env:
|
||||||
CONAN_URL: http://18.143.149.228:8081/artifactory/api/conan/conan-non-prod
|
CONAN_URL: http://18.143.149.228:8081/artifactory/api/conan/dev
|
||||||
CONAN_LOGIN_USERNAME_RIPPLE: ${{ secrets.CONAN_USERNAME }}
|
CONAN_LOGIN_USERNAME_RIPPLE: ${{ secrets.CONAN_USERNAME }}
|
||||||
CONAN_PASSWORD_RIPPLE: ${{ secrets.CONAN_TOKEN }}
|
CONAN_PASSWORD_RIPPLE: ${{ secrets.CONAN_TOKEN }}
|
||||||
on:
|
on:
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace BuildInfo {
|
|||||||
// and follow the format described at http://semver.org/
|
// and follow the format described at http://semver.org/
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// clang-format off
|
// clang-format off
|
||||||
char const* const versionString = "2.5.0-rc2"
|
char const* const versionString = "2.5.1"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#if defined(DEBUG) || defined(SANITIZER)
|
#if defined(DEBUG) || defined(SANITIZER)
|
||||||
|
|||||||
@@ -760,6 +760,12 @@ isRawTransactionOkay(STObject const& st, std::string& reason)
|
|||||||
{
|
{
|
||||||
TxType const tt =
|
TxType const tt =
|
||||||
safe_cast<TxType>(raw.getFieldU16(sfTransactionType));
|
safe_cast<TxType>(raw.getFieldU16(sfTransactionType));
|
||||||
|
if (tt == ttBATCH)
|
||||||
|
{
|
||||||
|
reason = "Raw Transactions may not contain batch transactions.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
raw.applyTemplate(getTxFormat(tt)->getSOTemplate());
|
raw.applyTemplate(getTxFormat(tt)->getSOTemplate());
|
||||||
}
|
}
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <xrpld/app/misc/HashRouter.h>
|
#include <xrpld/app/misc/HashRouter.h>
|
||||||
#include <xrpld/app/misc/Transaction.h>
|
#include <xrpld/app/misc/Transaction.h>
|
||||||
#include <xrpld/app/tx/apply.h>
|
#include <xrpld/app/tx/apply.h>
|
||||||
|
#include <xrpld/app/tx/detail/Batch.h>
|
||||||
|
|
||||||
#include <xrpl/protocol/Batch.h>
|
#include <xrpl/protocol/Batch.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
@@ -317,7 +318,8 @@ class Batch_test : public beast::unit_test::suite
|
|||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// temINVALID: Batch: batch cannot have inner batch txn.
|
// DEFENSIVE: temINVALID: Batch: batch cannot have inner batch txn.
|
||||||
|
// ACTUAL: telENV_RPC_FAILED: isRawTransactionOkay()
|
||||||
{
|
{
|
||||||
auto const seq = env.seq(alice);
|
auto const seq = env.seq(alice);
|
||||||
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||||
@@ -325,7 +327,7 @@ class Batch_test : public beast::unit_test::suite
|
|||||||
batch::inner(
|
batch::inner(
|
||||||
batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
|
batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
|
||||||
batch::inner(pay(alice, bob, XRP(1)), seq + 2),
|
batch::inner(pay(alice, bob, XRP(1)), seq + 2),
|
||||||
ter(temINVALID));
|
ter(telENV_RPC_FAILED));
|
||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3953,6 +3955,176 @@ class Batch_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testValidateRPCResponse(FeatureBitset features)
|
||||||
|
{
|
||||||
|
// Verifying that the RPC response from submit includes
|
||||||
|
// the account_sequence_available, account_sequence_next,
|
||||||
|
// open_ledger_cost and validated_ledger_index fields.
|
||||||
|
testcase("Validate RPC response");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice("alice");
|
||||||
|
Account const bob("bob");
|
||||||
|
env.fund(XRP(10000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// tes
|
||||||
|
{
|
||||||
|
auto const baseFee = env.current()->fees().base;
|
||||||
|
auto const aliceSeq = env.seq(alice);
|
||||||
|
auto jtx = env.jt(pay(alice, bob, XRP(1)));
|
||||||
|
|
||||||
|
Serializer s;
|
||||||
|
jtx.stx->add(s);
|
||||||
|
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jr[jss::account_sequence_available].asUInt() == aliceSeq + 1);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jr[jss::account_sequence_next].asUInt() == aliceSeq + 1);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
|
||||||
|
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tec failure
|
||||||
|
{
|
||||||
|
auto const baseFee = env.current()->fees().base;
|
||||||
|
auto const aliceSeq = env.seq(alice);
|
||||||
|
env(fset(bob, asfRequireDest));
|
||||||
|
auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq));
|
||||||
|
|
||||||
|
Serializer s;
|
||||||
|
jtx.stx->add(s);
|
||||||
|
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jr[jss::account_sequence_available].asUInt() == aliceSeq + 1);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jr[jss::account_sequence_next].asUInt() == aliceSeq + 1);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
|
||||||
|
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tem failure
|
||||||
|
{
|
||||||
|
auto const baseFee = env.current()->fees().base;
|
||||||
|
auto const aliceSeq = env.seq(alice);
|
||||||
|
auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq + 1));
|
||||||
|
|
||||||
|
Serializer s;
|
||||||
|
jtx.stx->add(s);
|
||||||
|
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jr[jss::account_sequence_available].asUInt() == aliceSeq);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
|
||||||
|
BEAST_EXPECT(jr[jss::account_sequence_next].asUInt() == aliceSeq);
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
|
||||||
|
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
|
||||||
|
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testBatchCalculateBaseFee(FeatureBitset features)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice("alice");
|
||||||
|
Account const bob("bob");
|
||||||
|
Account const carol("carol");
|
||||||
|
env.fund(XRP(10000), alice, bob, carol);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto getBaseFee = [&](JTx const& jtx) -> XRPAmount {
|
||||||
|
Serializer s;
|
||||||
|
jtx.stx->add(s);
|
||||||
|
return Batch::calculateBaseFee(*env.current(), *jtx.stx);
|
||||||
|
};
|
||||||
|
|
||||||
|
// bad: Inner Batch transaction found
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||||
|
auto jtx = env.jt(
|
||||||
|
batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||||
|
batch::inner(
|
||||||
|
batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 2));
|
||||||
|
XRPAmount const txBaseFee = getBaseFee(jtx);
|
||||||
|
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad: Raw Transactions array exceeds max entries.
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||||
|
|
||||||
|
auto jtx = env.jt(
|
||||||
|
batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 1),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 2),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 3),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 4),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 5),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 6),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 7),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 8),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 9));
|
||||||
|
|
||||||
|
XRPAmount const txBaseFee = getBaseFee(jtx);
|
||||||
|
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad: Signers array exceeds max entries.
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||||
|
|
||||||
|
auto jtx = env.jt(
|
||||||
|
batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||||
|
batch::inner(pay(alice, bob, XRP(10)), seq + 1),
|
||||||
|
batch::inner(pay(alice, bob, XRP(5)), seq + 2),
|
||||||
|
batch::sig(
|
||||||
|
bob,
|
||||||
|
carol,
|
||||||
|
alice,
|
||||||
|
bob,
|
||||||
|
carol,
|
||||||
|
alice,
|
||||||
|
bob,
|
||||||
|
carol,
|
||||||
|
alice,
|
||||||
|
alice));
|
||||||
|
XRPAmount const txBaseFee = getBaseFee(jtx);
|
||||||
|
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
|
||||||
|
}
|
||||||
|
|
||||||
|
// good:
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||||
|
auto jtx = env.jt(
|
||||||
|
batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||||
|
batch::inner(pay(alice, bob, XRP(1)), seq + 1),
|
||||||
|
batch::inner(pay(bob, alice, XRP(2)), seq + 2));
|
||||||
|
XRPAmount const txBaseFee = getBaseFee(jtx);
|
||||||
|
BEAST_EXPECT(txBaseFee == batchFee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testWithFeats(FeatureBitset features)
|
testWithFeats(FeatureBitset features)
|
||||||
{
|
{
|
||||||
@@ -3983,6 +4155,8 @@ class Batch_test : public beast::unit_test::suite
|
|||||||
testBatchTxQueue(features);
|
testBatchTxQueue(features);
|
||||||
testBatchNetworkOps(features);
|
testBatchNetworkOps(features);
|
||||||
testBatchDelegate(features);
|
testBatchDelegate(features);
|
||||||
|
testValidateRPCResponse(features);
|
||||||
|
testBatchCalculateBaseFee(features);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1136,6 +1136,10 @@ public:
|
|||||||
ConsensusParms p;
|
ConsensusParms p;
|
||||||
std::size_t peersUnchanged = 0;
|
std::size_t peersUnchanged = 0;
|
||||||
|
|
||||||
|
auto logs = std::make_unique<Logs>(beast::severities::kError);
|
||||||
|
auto j = logs->journal("Test");
|
||||||
|
auto clog = std::make_unique<std::stringstream>();
|
||||||
|
|
||||||
// Three cases:
|
// Three cases:
|
||||||
// 1 proposing, initial vote yes
|
// 1 proposing, initial vote yes
|
||||||
// 2 proposing, initial vote no
|
// 2 proposing, initial vote no
|
||||||
@@ -1172,10 +1176,15 @@ public:
|
|||||||
BEAST_EXPECT(proposingFalse.getOurVote() == false);
|
BEAST_EXPECT(proposingFalse.getOurVote() == false);
|
||||||
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// I'm in the majority, my vote should not change
|
// I'm in the majority, my vote should not change
|
||||||
BEAST_EXPECT(!proposingTrue.updateVote(5, true, p));
|
BEAST_EXPECT(!proposingTrue.updateVote(5, true, p));
|
||||||
@@ -1189,10 +1198,15 @@ public:
|
|||||||
BEAST_EXPECT(!followingFalse.updateVote(10, false, p));
|
BEAST_EXPECT(!followingFalse.updateVote(10, false, p));
|
||||||
|
|
||||||
peersUnchanged = 2;
|
peersUnchanged = 2;
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// Right now, the vote is 51%. The requirement is about to jump to
|
// Right now, the vote is 51%. The requirement is about to jump to
|
||||||
// 65%
|
// 65%
|
||||||
@@ -1282,10 +1296,15 @@ public:
|
|||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
peersUnchanged = 3;
|
peersUnchanged = 3;
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// Threshold jumps to 95%
|
// Threshold jumps to 95%
|
||||||
BEAST_EXPECT(proposingTrue.updateVote(220, true, p));
|
BEAST_EXPECT(proposingTrue.updateVote(220, true, p));
|
||||||
@@ -1322,12 +1341,60 @@ public:
|
|||||||
|
|
||||||
for (peersUnchanged = 0; peersUnchanged < 6; ++peersUnchanged)
|
for (peersUnchanged = 0; peersUnchanged < 6; ++peersUnchanged)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto expectStalled = [this, &clog](
|
||||||
|
int txid,
|
||||||
|
bool ourVote,
|
||||||
|
int ourTime,
|
||||||
|
int peerTime,
|
||||||
|
int support,
|
||||||
|
std::uint32_t line) {
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
auto const s = clog->str();
|
||||||
|
expect(s.find("stalled"), s, __FILE__, line);
|
||||||
|
expect(
|
||||||
|
s.starts_with("Transaction "s + std::to_string(txid)),
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find("voting "s + (ourVote ? "YES" : "NO")) != s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find("for "s + std::to_string(ourTime) + " rounds."s) !=
|
||||||
|
s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find(
|
||||||
|
"votes in "s + std::to_string(peerTime) + " rounds.") !=
|
||||||
|
s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.ends_with(
|
||||||
|
"has "s + std::to_string(support) + "% support. "s),
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
clog = std::make_unique<std::stringstream>();
|
||||||
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 1; ++i)
|
for (int i = 0; i < 1; ++i)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p));
|
BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p));
|
||||||
@@ -1342,22 +1409,34 @@ public:
|
|||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
// true vote has changed recently, so not stalled
|
// true vote has changed recently, so not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
// remaining votes have been unchanged in so long that we only
|
// remaining votes have been unchanged in so long that we only
|
||||||
// need to hit the second round at 95% to be stalled, regardless
|
// need to hit the second round at 95% to be stalled, regardless
|
||||||
// of peers
|
// of peers
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
expectStalled(98, false, 11, 0, 2, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11, 0, 3, __LINE__);
|
||||||
|
|
||||||
// true vote has changed recently, so not stalled
|
// true vote has changed recently, so not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECTS(clog->str() == "", clog->str());
|
||||||
// remaining votes have been unchanged in so long that we only
|
// remaining votes have been unchanged in so long that we only
|
||||||
// need to hit the second round at 95% to be stalled, regardless
|
// need to hit the second round at 95% to be stalled, regardless
|
||||||
// of peers
|
// of peers
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
expectStalled(98, false, 11, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
for (int i = 1; i < 3; ++i)
|
for (int i = 1; i < 3; ++i)
|
||||||
{
|
{
|
||||||
@@ -1374,19 +1453,31 @@ public:
|
|||||||
|
|
||||||
// true vote changed 2 rounds ago, and peers are changing, so
|
// true vote changed 2 rounds ago, and peers are changing, so
|
||||||
// not stalled
|
// not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
|
BEAST_EXPECTS(clog->str() == "", clog->str());
|
||||||
// still stalled
|
// still stalled
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
expectStalled(98, false, 11 + i, 0, 2, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 0, 3, __LINE__);
|
||||||
|
|
||||||
// true vote changed 2 rounds ago, and peers are NOT changing,
|
// true vote changed 2 rounds ago, and peers are NOT changing,
|
||||||
// so stalled
|
// so stalled
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
expectStalled(99, true, 1 + i, 6, 97, __LINE__);
|
||||||
// still stalled
|
// still stalled
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
expectStalled(98, false, 11 + i, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
for (int i = 3; i < 5; ++i)
|
for (int i = 3; i < 5; ++i)
|
||||||
{
|
{
|
||||||
@@ -1401,15 +1492,27 @@ public:
|
|||||||
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
expectStalled(99, true, 1 + i, 0, 97, __LINE__);
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
expectStalled(98, false, 11 + i, 0, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 0, 3, __LINE__);
|
||||||
|
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
expectStalled(99, true, 1 + i, 6, 97, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
expectStalled(98, false, 11 + i, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ Env::AppBundle::~AppBundle()
|
|||||||
if (app)
|
if (app)
|
||||||
{
|
{
|
||||||
app->getJobQueue().rendezvous();
|
app->getJobQueue().rendezvous();
|
||||||
app->signalStop();
|
app->signalStop("~AppBundle");
|
||||||
}
|
}
|
||||||
if (thread.joinable())
|
if (thread.joinable())
|
||||||
thread.join();
|
thread.join();
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
|
|||||||
|
|
||||||
if (!ledger)
|
if (!ledger)
|
||||||
{
|
{
|
||||||
JLOG(j_.debug())
|
JLOG(j_.warn())
|
||||||
<< "Need validated ledger for preferred ledger analysis " << hash;
|
<< "Need validated ledger for preferred ledger analysis " << hash;
|
||||||
|
|
||||||
Application* pApp = &app_;
|
Application* pApp = &app_;
|
||||||
|
|||||||
@@ -384,10 +384,17 @@ public:
|
|||||||
{
|
{
|
||||||
auto const start = m_clock.now();
|
auto const start = m_clock.now();
|
||||||
|
|
||||||
|
// Make a list of things to sweep, while holding the lock
|
||||||
|
std::vector<MapType::mapped_type> stuffToSweep;
|
||||||
|
std::size_t total;
|
||||||
|
|
||||||
{
|
{
|
||||||
ScopedLockType sl(mLock);
|
ScopedLockType sl(mLock);
|
||||||
MapType::iterator it(mLedgers.begin());
|
MapType::iterator it(mLedgers.begin());
|
||||||
|
total = mLedgers.size();
|
||||||
|
|
||||||
|
stuffToSweep.reserve(total);
|
||||||
|
|
||||||
while (it != mLedgers.end())
|
while (it != mLedgers.end())
|
||||||
{
|
{
|
||||||
auto const la = it->second->getLastAction();
|
auto const la = it->second->getLastAction();
|
||||||
@@ -397,8 +404,11 @@ public:
|
|||||||
it->second->touch();
|
it->second->touch();
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
else if ((la + std::chrono::seconds(10)) < start)
|
else if ((la + std::chrono::minutes(1)) < start)
|
||||||
{
|
{
|
||||||
|
stuffToSweep.push_back(it->second);
|
||||||
|
// shouldn't cause the actual final delete
|
||||||
|
// since we are holding a reference in the vector.
|
||||||
it = mLedgers.erase(it);
|
it = mLedgers.erase(it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -409,6 +419,14 @@ public:
|
|||||||
|
|
||||||
beast::expire(mRecentFailures, kReacquireInterval);
|
beast::expire(mRecentFailures, kReacquireInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JLOG(j_.debug())
|
||||||
|
<< "Swept " << stuffToSweep.size() << " out of " << total
|
||||||
|
<< " inbound ledgers. Duration: "
|
||||||
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
m_clock.now() - start)
|
||||||
|
.count()
|
||||||
|
<< "ms";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ public:
|
|||||||
config_->CONFIG_DIR),
|
config_->CONFIG_DIR),
|
||||||
*this,
|
*this,
|
||||||
logs_->journal("PerfLog"),
|
logs_->journal("PerfLog"),
|
||||||
[this] { signalStop(); }))
|
[this] { signalStop("PerfLog"); }))
|
||||||
|
|
||||||
, m_txMaster(*this)
|
, m_txMaster(*this)
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ public:
|
|||||||
void
|
void
|
||||||
run() override;
|
run() override;
|
||||||
void
|
void
|
||||||
signalStop(std::string msg = "") override;
|
signalStop(std::string msg) override;
|
||||||
bool
|
bool
|
||||||
checkSigs() const override;
|
checkSigs() const override;
|
||||||
void
|
void
|
||||||
@@ -977,7 +977,7 @@ public:
|
|||||||
if (!config_->standalone() &&
|
if (!config_->standalone() &&
|
||||||
!getRelationalDatabase().transactionDbHasSpace(*config_))
|
!getRelationalDatabase().transactionDbHasSpace(*config_))
|
||||||
{
|
{
|
||||||
signalStop();
|
signalStop("Out of transaction DB space");
|
||||||
}
|
}
|
||||||
|
|
||||||
// VFALCO NOTE Does the order of calls matter?
|
// VFALCO NOTE Does the order of calls matter?
|
||||||
@@ -1193,7 +1193,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
|
|||||||
JLOG(m_journal.info()) << "Received signal " << signum;
|
JLOG(m_journal.info()) << "Received signal " << signum;
|
||||||
|
|
||||||
if (signum == SIGTERM || signum == SIGINT)
|
if (signum == SIGTERM || signum == SIGINT)
|
||||||
signalStop();
|
signalStop("Signal: " + to_string(signum));
|
||||||
});
|
});
|
||||||
|
|
||||||
auto debug_log = config_->getDebugLogFile();
|
auto debug_log = config_->getDebugLogFile();
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public:
|
|||||||
virtual void
|
virtual void
|
||||||
run() = 0;
|
run() = 0;
|
||||||
virtual void
|
virtual void
|
||||||
signalStop(std::string msg = "") = 0;
|
signalStop(std::string msg) = 0;
|
||||||
virtual bool
|
virtual bool
|
||||||
checkSigs() const = 0;
|
checkSigs() const = 0;
|
||||||
virtual void
|
virtual void
|
||||||
|
|||||||
@@ -1701,7 +1701,7 @@ NetworkOPsImp::apply(std::unique_lock<std::mutex>& batchLock)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTemMalformed(e.result) && validatedLedgerIndex)
|
if (validatedLedgerIndex)
|
||||||
{
|
{
|
||||||
auto [fee, accountSeq, availableSeq] =
|
auto [fee, accountSeq, availableSeq] =
|
||||||
app_.getTxQ().getTxRequiredFeeAndSeq(
|
app_.getTxQ().getTxRequiredFeeAndSeq(
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (baseFee > maxAmount - view.fees().base)
|
if (baseFee > maxAmount - view.fees().base)
|
||||||
throw std::overflow_error("XRPAmount overflow");
|
{
|
||||||
|
JLOG(debugLog().error()) << "BatchTrace: Base fee overflow detected.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
XRPAmount const batchBase = view.fees().base + baseFee;
|
XRPAmount const batchBase = view.fees().base + baseFee;
|
||||||
@@ -72,32 +75,36 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
{
|
{
|
||||||
auto const& txns = tx.getFieldArray(sfRawTransactions);
|
auto const& txns = tx.getFieldArray(sfRawTransactions);
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
txns.size() <= maxBatchTxCount,
|
|
||||||
"Raw Transactions array exceeds max entries.");
|
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (txns.size() > maxBatchTxCount)
|
if (txns.size() > maxBatchTxCount)
|
||||||
throw std::length_error(
|
{
|
||||||
"Raw Transactions array exceeds max entries");
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: Raw Transactions array exceeds max entries.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
for (STObject txn : txns)
|
for (STObject txn : txns)
|
||||||
{
|
{
|
||||||
STTx const stx = STTx{std::move(txn)};
|
STTx const stx = STTx{std::move(txn)};
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
stx.getTxnType() != ttBATCH, "Inner Batch transaction found.");
|
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (stx.getTxnType() == ttBATCH)
|
if (stx.getTxnType() == ttBATCH)
|
||||||
throw std::invalid_argument("Inner Batch transaction found");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: Inner Batch transaction found.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
auto const fee = ripple::calculateBaseFee(view, stx);
|
auto const fee = ripple::calculateBaseFee(view, stx);
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (txnFees > maxAmount - fee)
|
if (txnFees > maxAmount - fee)
|
||||||
throw std::overflow_error("XRPAmount overflow");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: XRPAmount overflow in txnFees calculation.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
txnFees += fee;
|
txnFees += fee;
|
||||||
}
|
}
|
||||||
@@ -108,13 +115,14 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
if (tx.isFieldPresent(sfBatchSigners))
|
if (tx.isFieldPresent(sfBatchSigners))
|
||||||
{
|
{
|
||||||
auto const& signers = tx.getFieldArray(sfBatchSigners);
|
auto const& signers = tx.getFieldArray(sfBatchSigners);
|
||||||
XRPL_ASSERT(
|
|
||||||
signers.size() <= maxBatchTxCount,
|
|
||||||
"Batch Signers array exceeds max entries.");
|
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (signers.size() > maxBatchTxCount)
|
if (signers.size() > maxBatchTxCount)
|
||||||
throw std::length_error("Batch Signers array exceeds max entries");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: Batch Signers array exceeds max entries.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
for (STObject const& signer : signers)
|
for (STObject const& signer : signers)
|
||||||
@@ -128,16 +136,28 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (signerCount > 0 && view.fees().base > maxAmount / signerCount)
|
if (signerCount > 0 && view.fees().base > maxAmount / signerCount)
|
||||||
throw std::overflow_error("XRPAmount overflow");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: XRPAmount overflow in signerCount calculation.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
XRPAmount signerFees = signerCount * view.fees().base;
|
XRPAmount signerFees = signerCount * view.fees().base;
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (signerFees > maxAmount - txnFees)
|
if (signerFees > maxAmount - txnFees)
|
||||||
throw std::overflow_error("XRPAmount overflow");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: XRPAmount overflow in signerFees calculation.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
if (txnFees + signerFees > maxAmount - batchBase)
|
if (txnFees + signerFees > maxAmount - batchBase)
|
||||||
throw std::overflow_error("XRPAmount overflow");
|
{
|
||||||
|
JLOG(debugLog().error())
|
||||||
|
<< "BatchTrace: XRPAmount overflow in total fee calculation.";
|
||||||
|
return XRPAmount{INITIAL_XRP};
|
||||||
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
// 10 drops per batch signature + sum of inner tx fees + batchBase
|
// 10 drops per batch signature + sum of inner tx fees + batchBase
|
||||||
|
|||||||
@@ -139,11 +139,11 @@ checkConsensusReached(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only get stalled when every disputed transaction unequivocally has 80%
|
// We only get stalled when there are disputed transactions and all of them
|
||||||
// (minConsensusPct) agreement, either for or against. That is: either under
|
// unequivocally have 80% (minConsensusPct) agreement, either for or
|
||||||
// 20% or over 80% consensus (repectively "nay" or "yay"). This prevents
|
// against. That is: either under 20% or over 80% consensus (repectively
|
||||||
// manipulation by a minority of byzantine peers of which transactions make
|
// "nay" or "yay"). This prevents manipulation by a minority of byzantine
|
||||||
// the cut to get into the ledger.
|
// peers of which transactions make the cut to get into the ledger.
|
||||||
if (stalled)
|
if (stalled)
|
||||||
{
|
{
|
||||||
CLOG(clog) << "consensus stalled. ";
|
CLOG(clog) << "consensus stalled. ";
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ shouldCloseLedger(
|
|||||||
agree
|
agree
|
||||||
@param stalled the network appears to be stalled, where
|
@param stalled the network appears to be stalled, where
|
||||||
neither we nor our peers have changed their vote on any disputes in a
|
neither we nor our peers have changed their vote on any disputes in a
|
||||||
while. This is undesirable, and will cause us to end consensus
|
while. This is undesirable, and should be rare, and will cause us to
|
||||||
without 80% agreement.
|
end consensus without 80% agreement.
|
||||||
@param parms Consensus constant parameters
|
@param parms Consensus constant parameters
|
||||||
@param proposing whether we should count ourselves
|
@param proposing whether we should count ourselves
|
||||||
@param j journal for logging
|
@param j journal for logging
|
||||||
@@ -1712,15 +1712,29 @@ Consensus<Adaptor>::haveConsensus(
|
|||||||
<< ", disagree=" << disagree;
|
<< ", disagree=" << disagree;
|
||||||
|
|
||||||
ConsensusParms const& parms = adaptor_.parms();
|
ConsensusParms const& parms = adaptor_.parms();
|
||||||
// Stalling is BAD
|
// Stalling is BAD. It means that we have a consensus on the close time, so
|
||||||
|
// peers are talking, but we have disputed transactions that peers are
|
||||||
|
// unable or unwilling to come to agreement on one way or the other.
|
||||||
bool const stalled = haveCloseTimeConsensus_ &&
|
bool const stalled = haveCloseTimeConsensus_ &&
|
||||||
|
!result_->disputes.empty() &&
|
||||||
std::ranges::all_of(result_->disputes,
|
std::ranges::all_of(result_->disputes,
|
||||||
[this, &parms](auto const& dispute) {
|
[this, &parms, &clog](auto const& dispute) {
|
||||||
return dispute.second.stalled(
|
return dispute.second.stalled(
|
||||||
parms,
|
parms,
|
||||||
mode_.get() == ConsensusMode::proposing,
|
mode_.get() == ConsensusMode::proposing,
|
||||||
peerUnchangedCounter_);
|
peerUnchangedCounter_,
|
||||||
|
j_,
|
||||||
|
clog);
|
||||||
});
|
});
|
||||||
|
if (stalled)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Consensus detects as stalled with " << (agree + disagree) << "/"
|
||||||
|
<< prevProposers_ << " proposers, and " << result_->disputes.size()
|
||||||
|
<< " stalled disputed transactions.";
|
||||||
|
JLOG(j_.error()) << ss.str();
|
||||||
|
CLOG(clog) << ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if we actually have consensus or not
|
// Determine if we actually have consensus or not
|
||||||
result_->state = checkConsensus(
|
result_->state = checkConsensus(
|
||||||
|
|||||||
@@ -85,7 +85,12 @@ public:
|
|||||||
//! Are we and our peers "stalled" where we probably won't change
|
//! Are we and our peers "stalled" where we probably won't change
|
||||||
//! our vote?
|
//! our vote?
|
||||||
bool
|
bool
|
||||||
stalled(ConsensusParms const& p, bool proposing, int peersUnchanged) const
|
stalled(
|
||||||
|
ConsensusParms const& p,
|
||||||
|
bool proposing,
|
||||||
|
int peersUnchanged,
|
||||||
|
beast::Journal j,
|
||||||
|
std::unique_ptr<std::stringstream> const& clog) const
|
||||||
{
|
{
|
||||||
// at() can throw, but the map is built by hand to ensure all valid
|
// at() can throw, but the map is built by hand to ensure all valid
|
||||||
// values are available.
|
// values are available.
|
||||||
@@ -123,8 +128,24 @@ public:
|
|||||||
int const weight = support / total;
|
int const weight = support / total;
|
||||||
// Returns true if the tx has more than minCONSENSUS_PCT (80) percent
|
// Returns true if the tx has more than minCONSENSUS_PCT (80) percent
|
||||||
// agreement. Either voting for _or_ voting against the tx.
|
// agreement. Either voting for _or_ voting against the tx.
|
||||||
return weight > p.minCONSENSUS_PCT ||
|
bool const stalled =
|
||||||
weight < (100 - p.minCONSENSUS_PCT);
|
weight > p.minCONSENSUS_PCT || weight < (100 - p.minCONSENSUS_PCT);
|
||||||
|
|
||||||
|
if (stalled)
|
||||||
|
{
|
||||||
|
// stalling is an error condition for even a single
|
||||||
|
// transaction.
|
||||||
|
std::stringstream s;
|
||||||
|
s << "Transaction " << ID() << " is stalled. We have been voting "
|
||||||
|
<< (getOurVote() ? "YES" : "NO") << " for " << currentVoteCounter_
|
||||||
|
<< " rounds. Peers have not changed their votes in "
|
||||||
|
<< peersUnchanged << " rounds. The transaction has " << weight
|
||||||
|
<< "% support. ";
|
||||||
|
JLOG(j_.error()) << s.str();
|
||||||
|
CLOG(clog) << s.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! The disputed transaction.
|
//! The disputed transaction.
|
||||||
|
|||||||
@@ -3440,7 +3440,7 @@ PeerImp::processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m)
|
|||||||
if (!m->has_ledgerhash())
|
if (!m->has_ledgerhash())
|
||||||
info += ", no hash specified";
|
info += ", no hash specified";
|
||||||
|
|
||||||
JLOG(p_journal_.error())
|
JLOG(p_journal_.warn())
|
||||||
<< "processLedgerRequest: getNodeFat with nodeId "
|
<< "processLedgerRequest: getNodeFat with nodeId "
|
||||||
<< *shaMapNodeId << " and ledger info type " << info
|
<< *shaMapNodeId << " and ledger info type " << info
|
||||||
<< " throws exception: " << e.what();
|
<< " throws exception: " << e.what();
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct JsonContext;
|
|||||||
Json::Value
|
Json::Value
|
||||||
doStop(RPC::JsonContext& context)
|
doStop(RPC::JsonContext& context)
|
||||||
{
|
{
|
||||||
context.app.signalStop();
|
context.app.signalStop("RPC");
|
||||||
return RPC::makeObjectValue(systemName() + " server stopping");
|
return RPC::makeObjectValue(systemName() + " server stopping");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user