mirror of
https://github.com/Xahau/xahaud.git
synced 2026-06-10 20:26:37 +00:00
test: improve export rng coverage
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
#include <xrpld/app/consensus/ExportSignatureHarvester.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
@@ -116,16 +117,11 @@ makeBlob(uint256 const& txHash, PublicKey const& pk, Buffer const& sig)
|
||||
return makeBlob(txHash, pk, Slice(sig.data(), sig.size()));
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
journal()
|
||||
{
|
||||
return beast::Journal{beast::Journal::getNullSink()};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ExportSignatureHarvester_test : public beast::unit_test::suite
|
||||
{
|
||||
SuiteJournal journal_{"ExportSignatureHarvester_test", *this};
|
||||
std::pair<PublicKey, SecretKey> const sender_ =
|
||||
randomKeyPair(KeyType::secp256k1);
|
||||
std::pair<PublicKey, SecretKey> const other_ =
|
||||
@@ -139,7 +135,8 @@ class ExportSignatureHarvester_test : public beast::unit_test::suite
|
||||
ExportTxnLookup const& exportTxns,
|
||||
bool active = true,
|
||||
std::optional<uint256> sourceLedgerHash = std::nullopt,
|
||||
PublicKey const* sender = nullptr) const
|
||||
PublicKey const* sender = nullptr,
|
||||
std::size_t maxEntries = 2) const
|
||||
{
|
||||
return ExportSignatureHarvestInput{
|
||||
sender ? *sender : sender_.first,
|
||||
@@ -150,7 +147,13 @@ class ExportSignatureHarvester_test : public beast::unit_test::suite
|
||||
exportTxns,
|
||||
42,
|
||||
source_,
|
||||
2};
|
||||
maxEntries};
|
||||
}
|
||||
|
||||
beast::Journal&
|
||||
journal()
|
||||
{
|
||||
return journal_;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -238,7 +241,8 @@ public:
|
||||
ExportTxnLookup lookup;
|
||||
ExportSigCollector collector;
|
||||
|
||||
auto input = makeInput(blobs, lookup, true, prevLedger_);
|
||||
auto input =
|
||||
makeInput(blobs, lookup, true, prevLedger_, nullptr, blobs.size());
|
||||
BEAST_EXPECT(harvestExportSignatures(input, collector, journal()) == 0);
|
||||
BEAST_EXPECT(!collector.hasUnverifiedSignatures());
|
||||
BEAST_EXPECT(collector.signatureCount(txHash) == 0);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <test/jtx/import.h>
|
||||
#include <test/jtx/xpop.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/misc/RuntimeConfig.h>
|
||||
#include <xrpld/app/tx/detail/ExportLedgerOps.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ExportLimits.h>
|
||||
@@ -51,6 +52,12 @@ struct Export_test : public beast::unit_test::suite
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static void
|
||||
forceNonStandalone(Application& app)
|
||||
{
|
||||
const_cast<Config&>(app.config()).setupControl(true, true, false);
|
||||
}
|
||||
|
||||
// Build a minimal unsigned Payment STObject suitable for sfExportedTxn.
|
||||
static STObject
|
||||
buildExportedPayment(
|
||||
@@ -613,6 +620,64 @@ struct Export_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testExportNetworkRetryWithoutQuorum(FeatureBitset features)
|
||||
{
|
||||
testcase("ttEXPORT network mode retries without quorum");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env{*this, exportTestConfig(), features};
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const carol{"carol"};
|
||||
|
||||
env.fund(XRP(10000), alice, carol);
|
||||
env.close();
|
||||
forceNonStandalone(env.app());
|
||||
BEAST_EXPECT(!env.app().config().standalone());
|
||||
ConfigVals cfg;
|
||||
cfg.noExportSig = true;
|
||||
env.app().getRuntimeConfig().setConfig("*", cfg);
|
||||
|
||||
auto const seq = env.current()->seq();
|
||||
auto const ticketSeq = std::uint32_t{1};
|
||||
auto const lls = seq + 5;
|
||||
auto innerObj = buildExportedPayment(
|
||||
alice.id(), carol.id(), seq + 1, lls, ticketSeq);
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::Export;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::LastLedgerSequence] = lls;
|
||||
jv[sfExportedTxn.jsonName] = innerObj.getJson(JsonOptions::none);
|
||||
|
||||
env(jv, fee(XRP(1)), ter(tesSUCCESS));
|
||||
BEAST_EXPECT(env.close(
|
||||
env.now() + std::chrono::seconds{5}, std::chrono::milliseconds{0}));
|
||||
|
||||
std::optional<TER> closedResult;
|
||||
for (auto const& [stx, meta] : env.closed()->txs)
|
||||
{
|
||||
if (!stx || stx->getTxnType() != ttEXPORT ||
|
||||
stx->getAccountID(sfAccount) != alice.id())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const& exported =
|
||||
stx->peekAtField(sfExportedTxn).downcast<STObject>();
|
||||
if (exported.getFieldU32(sfTicketSequence) != ticketSeq)
|
||||
continue;
|
||||
|
||||
if (meta)
|
||||
closedResult = TER::fromInt((*meta)[sfTransactionResult]);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(!closedResult);
|
||||
BEAST_EXPECT(!env.le(keylet::shadowTicket(alice.id(), ticketSeq)));
|
||||
}
|
||||
|
||||
void
|
||||
testOpenLedgerExportLimit(FeatureBitset features)
|
||||
{
|
||||
@@ -1060,6 +1125,7 @@ struct Export_test : public beast::unit_test::suite
|
||||
|
||||
// ttEXPORT transactor tests
|
||||
testExportTxnOpenLedger(allWithExport);
|
||||
testExportNetworkRetryWithoutQuorum(allWithExport);
|
||||
testOpenLedgerExportLimit(allWithExport);
|
||||
testShadowTicketLimit(allWithExport);
|
||||
testShadowTicketLifecycle(allWithExport);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/import.h>
|
||||
#include <test/jtx/xpop.h>
|
||||
#include <test/shamap/common.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/proof/LedgerProof.h>
|
||||
#include <xrpld/app/proof/ProofBuilder.h>
|
||||
@@ -152,6 +153,62 @@ struct XPOP_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(proofJson[3].asString() == to_string(manual.leafHash));
|
||||
}
|
||||
|
||||
void
|
||||
testProofBuilderSyntheticTrie()
|
||||
{
|
||||
testcase("ProofBuilder synthetic trie collisions");
|
||||
|
||||
tests::TestNodeFamily f{beast::Journal{beast::Journal::getNullSink()}};
|
||||
SHAMap map{SHAMapType::TRANSACTION, f};
|
||||
|
||||
auto const keyA = uint256{
|
||||
"1000000000000000000000000000000000000000000000000000000000000001"};
|
||||
auto const keyB = uint256{
|
||||
"1800000000000000000000000000000000000000000000000000000000000002"};
|
||||
auto const keyC = uint256{
|
||||
"2000000000000000000000000000000000000000000000000000000000000003"};
|
||||
auto const keyD = uint256{
|
||||
"1f00000000000000000000000000000000000000000000000000000000000004"};
|
||||
|
||||
auto add = [&](uint256 const& key, Blob data) {
|
||||
return map.addItem(
|
||||
SHAMapNodeType::tnTRANSACTION_NM,
|
||||
make_shamapitem(key, makeSlice(data)));
|
||||
};
|
||||
auto payload = [](std::uint8_t first) {
|
||||
Blob data;
|
||||
data.reserve(12);
|
||||
for (std::uint8_t i = 0; i < 12; ++i)
|
||||
data.push_back(first + i);
|
||||
return data;
|
||||
};
|
||||
|
||||
BEAST_EXPECT(add(keyA, payload(0x01)));
|
||||
BEAST_EXPECT(add(keyB, payload(0x11)));
|
||||
BEAST_EXPECT(add(keyC, payload(0x21)));
|
||||
BEAST_EXPECT(add(keyD, payload(0x31)));
|
||||
map.invariants();
|
||||
|
||||
auto const proof = proof::extractProofV1(map, keyA);
|
||||
BEAST_EXPECT(proof.has_value());
|
||||
if (proof)
|
||||
{
|
||||
BEAST_EXPECT(proof->path.size() == 2);
|
||||
auto const computedRoot = proof->computeRoot();
|
||||
BEAST_EXPECT(computedRoot.has_value());
|
||||
if (computedRoot)
|
||||
BEAST_EXPECT(proof->verify(*computedRoot));
|
||||
|
||||
auto const json = proof->toJsonV1();
|
||||
BEAST_EXPECT(json.isArray());
|
||||
BEAST_EXPECT(json[proof->path.front().targetBranch].isArray());
|
||||
}
|
||||
|
||||
auto const nearMiss = uint256{
|
||||
"10000000000000000000000000000000000000000000000000000000000000ff"};
|
||||
BEAST_EXPECT(!proof::extractProofV1(map, nearMiss));
|
||||
}
|
||||
|
||||
void
|
||||
testBuildXPOPv1()
|
||||
{
|
||||
@@ -385,6 +442,7 @@ struct XPOP_test : public beast::unit_test::suite
|
||||
{
|
||||
testBuildLedgerProof();
|
||||
testProofBuilderEdgeCases();
|
||||
testProofBuilderSyntheticTrie();
|
||||
testBuildXPOPv1();
|
||||
testBuildXPOPv1WithoutMerkleProof();
|
||||
testMerkleProofVerification();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
enableSilentTracing(csf::Sim& sim)
|
||||
{
|
||||
sim.sink.silent(true);
|
||||
sim.sink.threshold(beast::severities::kTrace);
|
||||
}
|
||||
|
||||
void
|
||||
testShouldCloseLedger()
|
||||
{
|
||||
@@ -214,6 +221,7 @@ public:
|
||||
//@@start peers-agree
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup peers = sim.createGroup(5);
|
||||
|
||||
// Connected trust and network graphs with single fixed delay
|
||||
@@ -260,6 +268,7 @@ public:
|
||||
{
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup slow = sim.createGroup(1);
|
||||
PeerGroup fast = sim.createGroup(4);
|
||||
PeerGroup network = fast + slow;
|
||||
@@ -318,6 +327,7 @@ public:
|
||||
ConsensusParms const parms{};
|
||||
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup slow = sim.createGroup(2);
|
||||
PeerGroup fast = sim.createGroup(4);
|
||||
PeerGroup network = fast + slow;
|
||||
@@ -447,6 +457,7 @@ public:
|
||||
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
|
||||
PeerGroup groupA = sim.createGroup(2);
|
||||
PeerGroup groupB = sim.createGroup(2);
|
||||
@@ -480,6 +491,52 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBootstrapFastStart()
|
||||
{
|
||||
using namespace csf;
|
||||
using namespace std::chrono;
|
||||
testcase("bootstrap fast start");
|
||||
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup peers = sim.createGroup(4);
|
||||
peers.trustAndConnect(
|
||||
peers, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
|
||||
|
||||
for (Peer* peer : peers)
|
||||
{
|
||||
peer->ce().bootstrapFastStartEnabled_ = true;
|
||||
peer->targetLedgers =
|
||||
static_cast<int>(parms.bootstrapStableRoundsRequired);
|
||||
peer->start();
|
||||
|
||||
auto const json = peer->consensus.getJson(true);
|
||||
BEAST_EXPECT(json.isMember("bootstrap_fast_start"));
|
||||
BEAST_EXPECT(json["bootstrap_fast_start"].asBool());
|
||||
BEAST_EXPECT(
|
||||
json["previous_mseconds"].asInt() ==
|
||||
parms.bootstrapRoundTimeSeed.count());
|
||||
BEAST_EXPECT(json["bootstrap_stable_rounds"].asInt() == 0);
|
||||
}
|
||||
|
||||
sim.scheduler.step();
|
||||
|
||||
if (BEAST_EXPECT(sim.synchronized()))
|
||||
{
|
||||
for (Peer* peer : peers)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
peer->completedLedgers ==
|
||||
static_cast<int>(parms.bootstrapStableRoundsRequired));
|
||||
auto const json = peer->consensus.getJson(true);
|
||||
BEAST_EXPECT(!json.isMember("bootstrap_fast_start"));
|
||||
BEAST_EXPECT(peer->prevRoundTime < parms.ledgerIDLE_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWrongLCL()
|
||||
{
|
||||
@@ -520,6 +577,7 @@ public:
|
||||
//@@end wrong-lcl-scenario
|
||||
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
|
||||
PeerGroup minority = sim.createGroup(2);
|
||||
PeerGroup majorityA = sim.createGroup(3);
|
||||
@@ -624,6 +682,7 @@ public:
|
||||
// after it is already in the establish phase of the next round.
|
||||
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup loner = sim.createGroup(1);
|
||||
PeerGroup friends = sim.createGroup(3);
|
||||
loner.trust(loner + friends);
|
||||
@@ -669,6 +728,7 @@ public:
|
||||
ConsensusParms parms;
|
||||
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
|
||||
// This requires a group of 4 fast and 2 slow peers to create a
|
||||
// situation in which a subset of peers requires seeing additional
|
||||
@@ -770,6 +830,7 @@ public:
|
||||
{
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
|
||||
std::uint32_t numA = (numPeers - overlap) / 2;
|
||||
std::uint32_t numB = numPeers - numA - overlap;
|
||||
@@ -828,6 +889,7 @@ public:
|
||||
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
PeerGroup validators = sim.createGroup(5);
|
||||
PeerGroup center = sim.createGroup(1);
|
||||
validators.trust(validators);
|
||||
@@ -943,6 +1005,7 @@ public:
|
||||
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
|
||||
// Goes A->B->D
|
||||
PeerGroup groupABD = sim.createGroup(2);
|
||||
@@ -1066,6 +1129,7 @@ public:
|
||||
|
||||
ConsensusParms const parms{};
|
||||
Sim sim;
|
||||
enableSilentTracing(sim);
|
||||
SimDuration delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
|
||||
|
||||
PeerGroup behind = sim.createGroup(3);
|
||||
@@ -1542,6 +1606,7 @@ public:
|
||||
testPeersAgree();
|
||||
testSlowPeers();
|
||||
testCloseTimeDisagree();
|
||||
testBootstrapFastStart();
|
||||
testWrongLCL();
|
||||
testConsensusCloseTimeRounding();
|
||||
testFork();
|
||||
|
||||
@@ -367,6 +367,9 @@ struct Peer
|
||||
// Optional test hook: stay an active proposer but do not originate an
|
||||
// export signature, so tests can force sidecar-fetch-only convergence.
|
||||
bool suppressOwnExportSig_ = false;
|
||||
// Optional test hook: exercise generic Consensus bootstrap timing
|
||||
// without making the CSF runtime-config aware.
|
||||
bool bootstrapFastStartEnabled_ = false;
|
||||
|
||||
explicit Extensions(Peer& p) : peer(p), j_(p.j)
|
||||
{
|
||||
@@ -887,7 +890,7 @@ struct Peer
|
||||
bool
|
||||
bootstrapFastStartEnabled() const
|
||||
{
|
||||
return false;
|
||||
return bootstrapFastStartEnabled_;
|
||||
}
|
||||
bool
|
||||
shouldSendExplicitFinalProposal() const
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace csf {
|
||||
class BasicSink : public beast::Journal::Sink
|
||||
{
|
||||
Scheduler::clock_type const& clock_;
|
||||
bool silent_ = false;
|
||||
|
||||
public:
|
||||
BasicSink(Scheduler::clock_type const& clock)
|
||||
@@ -49,11 +50,19 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
silent(bool value)
|
||||
{
|
||||
silent_ = value;
|
||||
}
|
||||
|
||||
void
|
||||
write(beast::severities::Severity level, std::string const& text) override
|
||||
{
|
||||
if (level < threshold())
|
||||
return;
|
||||
if (silent_)
|
||||
return;
|
||||
|
||||
std::cout << clock_.now().time_since_epoch().count() << " " << text
|
||||
<< std::endl;
|
||||
|
||||
232
src/test/overlay/ProposalPrecheck_test.cpp
Normal file
232
src/test/overlay/ProposalPrecheck_test.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/overlay/detail/ProposalPrecheck.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
|
||||
uint256
|
||||
makeHash(char const* label)
|
||||
{
|
||||
return sha512Half(Slice(label, std::strlen(label)));
|
||||
}
|
||||
|
||||
void
|
||||
setPreviousLedger(protocol::TMProposeSet& set)
|
||||
{
|
||||
auto const prev = makeHash("proposal-precheck-prev");
|
||||
set.set_previousledger(prev.data(), prev.size());
|
||||
}
|
||||
|
||||
void
|
||||
setPosition(protocol::TMProposeSet& set, ExtendedPosition const& position)
|
||||
{
|
||||
Serializer s;
|
||||
position.add(s);
|
||||
set.set_currenttxhash(s.data(), s.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ProposalPrecheck_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using enum detail::ProposalPrecheckResult;
|
||||
|
||||
testcase("legacy and extended ok");
|
||||
{
|
||||
protocol::TMProposeSet set;
|
||||
setPreviousLedger(set);
|
||||
ExtendedPosition position{makeHash("legacy-position")};
|
||||
setPosition(set, position);
|
||||
|
||||
auto const precheck =
|
||||
detail::checkProposalExtensions(set, false, false);
|
||||
BEAST_EXPECT(precheck.result == ok);
|
||||
BEAST_EXPECT(precheck.position);
|
||||
if (precheck.position)
|
||||
BEAST_EXPECT(precheck.position->txSetHash == position.txSetHash);
|
||||
}
|
||||
|
||||
testcase("malformed hashes and extended payload");
|
||||
{
|
||||
protocol::TMProposeSet set;
|
||||
setPreviousLedger(set);
|
||||
set.set_currenttxhash("short", 5);
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(set, true, true).result ==
|
||||
badHashes);
|
||||
|
||||
set.clear_currenttxhash();
|
||||
std::string malformed(uint256::size(), '\0');
|
||||
malformed.push_back(static_cast<char>(0x80));
|
||||
set.set_currenttxhash(malformed.data(), malformed.size());
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(set, true, true).result ==
|
||||
badPosition);
|
||||
|
||||
protocol::TMProposeSet badPrev;
|
||||
ExtendedPosition position{makeHash("bad-prev-position")};
|
||||
setPosition(badPrev, position);
|
||||
badPrev.set_previousledger("short", 5);
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(badPrev, true, true).result ==
|
||||
badHashes);
|
||||
}
|
||||
|
||||
testcase("feature gating");
|
||||
{
|
||||
protocol::TMProposeSet entropySet;
|
||||
setPreviousLedger(entropySet);
|
||||
ExtendedPosition entropy{makeHash("entropy-position")};
|
||||
entropy.myCommitment = makeHash("commitment");
|
||||
setPosition(entropySet, entropy);
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(entropySet, false, true)
|
||||
.result == entropyDisabled);
|
||||
|
||||
protocol::TMProposeSet exportSet;
|
||||
setPreviousLedger(exportSet);
|
||||
ExtendedPosition exportPos{makeHash("export-position")};
|
||||
exportPos.exportSigSetHash = makeHash("export-sidecar");
|
||||
setPosition(exportSet, exportPos);
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(exportSet, true, false)
|
||||
.result == exportDisabled);
|
||||
}
|
||||
|
||||
testcase("export signature binding");
|
||||
{
|
||||
protocol::TMProposeSet tooMany;
|
||||
setPreviousLedger(tooMany);
|
||||
ExtendedPosition position{makeHash("too-many-position")};
|
||||
setPosition(tooMany, position);
|
||||
for (std::uint8_t i = 0; i <= ExportLimits::maxPendingExports; ++i)
|
||||
tooMany.add_exportsignatures("sig");
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(tooMany, true, true).result ==
|
||||
tooManyExportSignatures);
|
||||
|
||||
protocol::TMProposeSet unsignedSigs;
|
||||
setPreviousLedger(unsignedSigs);
|
||||
setPosition(unsignedSigs, position);
|
||||
unsignedSigs.add_exportsignatures("sig");
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(unsignedSigs, true, true)
|
||||
.result == unsignedExportSignatures);
|
||||
|
||||
protocol::TMProposeSet mismatch;
|
||||
setPreviousLedger(mismatch);
|
||||
ExtendedPosition mismatchPos{makeHash("mismatch-position")};
|
||||
mismatchPos.exportSignaturesHash =
|
||||
proposalExportSignaturesHash(std::vector<std::string>{"one"});
|
||||
setPosition(mismatch, mismatchPos);
|
||||
mismatch.add_exportsignatures("two");
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(mismatch, true, true).result ==
|
||||
exportSignaturesHashMismatch);
|
||||
|
||||
protocol::TMProposeSet missing;
|
||||
setPreviousLedger(missing);
|
||||
setPosition(missing, mismatchPos);
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(missing, true, true).result ==
|
||||
missingExportSignatures);
|
||||
|
||||
protocol::TMProposeSet okSet;
|
||||
setPreviousLedger(okSet);
|
||||
std::vector<std::string> const sigs{"signed-export"};
|
||||
ExtendedPosition okPos{makeHash("export-ok-position")};
|
||||
okPos.exportSignaturesHash = proposalExportSignaturesHash(sigs);
|
||||
setPosition(okSet, okPos);
|
||||
okSet.add_exportsignatures(sigs.front());
|
||||
BEAST_EXPECT(
|
||||
detail::checkProposalExtensions(okSet, true, true).result ==
|
||||
ok);
|
||||
}
|
||||
|
||||
testcase("rejection diagnostics");
|
||||
{
|
||||
BEAST_EXPECT(!detail::proposalPrecheckRejection(ok));
|
||||
|
||||
auto const check = [&](detail::ProposalPrecheckResult result,
|
||||
char const* logMessage,
|
||||
char const* feeReason) {
|
||||
auto const rejection = detail::proposalPrecheckRejection(result);
|
||||
BEAST_EXPECT(rejection);
|
||||
if (rejection)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
std::string_view{rejection->logMessage} ==
|
||||
logMessage);
|
||||
BEAST_EXPECT(
|
||||
std::string_view{rejection->feeReason} == feeReason);
|
||||
}
|
||||
};
|
||||
|
||||
check(badHashes, "Proposal: malformed", "bad hashes");
|
||||
check(
|
||||
badPosition,
|
||||
"Proposal: malformed extended position",
|
||||
"bad proposal position");
|
||||
check(
|
||||
entropyDisabled,
|
||||
"Proposal: entropy fields while featureConsensusEntropy disabled",
|
||||
"entropy fields disabled");
|
||||
check(
|
||||
exportDisabled,
|
||||
"Proposal: export fields while featureExport disabled",
|
||||
"export fields disabled");
|
||||
check(
|
||||
tooManyExportSignatures,
|
||||
"Proposal: too many export signatures",
|
||||
"too many export sigs");
|
||||
check(
|
||||
unsignedExportSignatures,
|
||||
"Proposal: unsigned export signatures",
|
||||
"unsigned export sigs");
|
||||
check(
|
||||
exportSignaturesHashMismatch,
|
||||
"Proposal: export signatures hash mismatch",
|
||||
"export sig hash mismatch");
|
||||
check(
|
||||
missingExportSignatures,
|
||||
"Proposal: missing signed export signatures",
|
||||
"missing export sigs");
|
||||
BEAST_EXPECT(!detail::proposalPrecheckRejection(
|
||||
static_cast<detail::ProposalPrecheckResult>(255)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ProposalPrecheck, overlay, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -46,11 +47,64 @@ class Connect_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testDisconnect()
|
||||
{
|
||||
testcase("Disconnect");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
const_cast<Config&>(env.app().config()).setupControl(true, true, false);
|
||||
BEAST_EXPECT(!env.app().config().standalone());
|
||||
|
||||
{
|
||||
auto const result = env.rpc("json", "disconnect", "{}");
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error_message]
|
||||
.asString()
|
||||
.find("ip") != std::string::npos);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc(
|
||||
"json",
|
||||
"disconnect",
|
||||
R"({"ip":"127.0.0.1","port":"bad"})");
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
env.rpc("json", "disconnect", R"({"ip":"0.0.0.0"})");
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::message].asString().find(
|
||||
"port: 21337") != std::string::npos);
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::message].asString().find(
|
||||
"peers: 0") != std::string::npos);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc(
|
||||
"json", "disconnect", R"({"ip":"127.0.0.1","port":6000})");
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::message].asString().find(
|
||||
"port: 6000") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testErrors();
|
||||
testDisconnect();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -850,6 +850,38 @@ class RuntimeConfig_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(cfg->rngClaimDropPctX100 == 0); // clamped to 0%
|
||||
}
|
||||
|
||||
void
|
||||
testRngAndExportRuntimeToggles()
|
||||
{
|
||||
testcase("rng/export runtime toggles round-trip");
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
|
||||
Json::Value params;
|
||||
params["set"] = Json::objectValue;
|
||||
params["set"]["*"] = Json::objectValue;
|
||||
params["set"]["*"]["bootstrap_fast_start"] = false;
|
||||
params["set"]["*"]["rng_poll_ms"] = 5;
|
||||
params["set"]["*"]["no_export_sig"] = true;
|
||||
auto const result = runtimeConfig(env, params);
|
||||
|
||||
auto const& global = result["configs"]["*"];
|
||||
BEAST_EXPECT(global["bootstrap_fast_start"].asBool() == false);
|
||||
BEAST_EXPECT(global["rng_poll_ms"].asInt() == 50);
|
||||
BEAST_EXPECT(global["no_export_sig"].asBool() == true);
|
||||
|
||||
auto const cfg = env.app().getRuntimeConfig().getConfig("*");
|
||||
BEAST_EXPECT(cfg.has_value());
|
||||
if (cfg)
|
||||
{
|
||||
BEAST_EXPECT(cfg->bootstrapFastStart.has_value());
|
||||
BEAST_EXPECT(*cfg->bootstrapFastStart == false);
|
||||
BEAST_EXPECT(cfg->rngPollMs == 50);
|
||||
BEAST_EXPECT(cfg->noExportSig.has_value());
|
||||
BEAST_EXPECT(*cfg->noExportSig == true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testExplicitFinalProposalToggle()
|
||||
{
|
||||
@@ -968,6 +1000,7 @@ public:
|
||||
testDropPctClamping();
|
||||
testRngClaimDropPct();
|
||||
testRngClaimDropPctClamping();
|
||||
testRngAndExportRuntimeToggles();
|
||||
testExplicitFinalProposalToggle();
|
||||
testPerPeerClearInheritedFilter();
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
#include <test/shamap/common.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
#include <xrpld/shamap/SHAMap.h>
|
||||
#include <xrpld/shamap/SHAMapSidecarLeafNode.h>
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace tests {
|
||||
@@ -122,6 +124,65 @@ public:
|
||||
|
||||
run(true, journal);
|
||||
run(false, journal);
|
||||
testSidecarLeaf(journal);
|
||||
}
|
||||
|
||||
void
|
||||
testSidecarLeaf(beast::Journal const& journal)
|
||||
{
|
||||
testcase("sidecar leaf");
|
||||
|
||||
Blob const payload{
|
||||
0x53, 0x49, 0x44, 0x45, 0x43, 0x41, 0x52, 0x00, 0x01, 0x02,
|
||||
0x03, 0x04};
|
||||
auto const itemHash = sha512Half(HashPrefix::sidecar, makeSlice(payload));
|
||||
auto const item = make_shamapitem(itemHash, makeSlice(payload));
|
||||
|
||||
SHAMapSidecarLeafNode leaf{item, 7};
|
||||
BEAST_EXPECT(leaf.cowid() == 7);
|
||||
BEAST_EXPECT(leaf.getType() == SHAMapNodeType::tnSIDECAR);
|
||||
BEAST_EXPECT(leaf.getHash() == SHAMapHash{itemHash});
|
||||
BEAST_EXPECT(leaf.peekItem()->slice() == makeSlice(payload));
|
||||
|
||||
auto const label = leaf.getString(SHAMapNodeID{});
|
||||
BEAST_EXPECT(label.find(",sidecar") != std::string::npos);
|
||||
|
||||
auto const cloned = leaf.clone(11);
|
||||
BEAST_EXPECT(cloned->cowid() == 11);
|
||||
BEAST_EXPECT(cloned->getType() == SHAMapNodeType::tnSIDECAR);
|
||||
BEAST_EXPECT(cloned->getHash() == leaf.getHash());
|
||||
|
||||
Serializer wire;
|
||||
leaf.serializeForWire(wire);
|
||||
auto const wireSlice = wire.slice();
|
||||
auto const wirePayload = Slice{wireSlice.data(), payload.size()};
|
||||
BEAST_EXPECT(wireSlice.size() == payload.size() + 1);
|
||||
BEAST_EXPECT(wirePayload == makeSlice(payload));
|
||||
BEAST_EXPECT(wireSlice[wireSlice.size() - 1] == wireTypeSidecar);
|
||||
|
||||
auto const fromWire = SHAMapTreeNode::makeFromWire(wireSlice);
|
||||
BEAST_EXPECT(fromWire->getType() == SHAMapNodeType::tnSIDECAR);
|
||||
BEAST_EXPECT(fromWire->getHash() == leaf.getHash());
|
||||
|
||||
Serializer prefixed;
|
||||
leaf.serializeWithPrefix(prefixed);
|
||||
auto const fromPrefix =
|
||||
SHAMapTreeNode::makeFromPrefix(prefixed.slice(), leaf.getHash());
|
||||
BEAST_EXPECT(fromPrefix->getType() == SHAMapNodeType::tnSIDECAR);
|
||||
BEAST_EXPECT(fromPrefix->getHash() == leaf.getHash());
|
||||
|
||||
tests::TestNodeFamily f(journal);
|
||||
SHAMap sidecars{SHAMapType::SIDECAR, f};
|
||||
BEAST_EXPECT(sidecars.addItem(
|
||||
SHAMapNodeType::tnSIDECAR, make_shamapitem(*item)));
|
||||
sidecars.invariants();
|
||||
BEAST_EXPECT(sidecars.hasItem(itemHash));
|
||||
BEAST_EXPECT(sidecars.peekItem(itemHash)->slice() == makeSlice(payload));
|
||||
|
||||
SHAMapMissingNode missing{SHAMapType::SIDECAR, leaf.getHash()};
|
||||
BEAST_EXPECT(
|
||||
std::string{missing.what()}.find("Sidecar Tree") !=
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -146,6 +146,10 @@ harvestExportSignatures(
|
||||
continue;
|
||||
|
||||
auto const fullSlice = makeSlice(blob);
|
||||
auto const pkSlice = fullSlice.substr(32, 33);
|
||||
if (!publicKeyType(pkSlice))
|
||||
continue;
|
||||
|
||||
auto const sigSlice = fullSlice.substr(65);
|
||||
|
||||
auto const txIt = input.exportTxns.find(txHash);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <xrpld/app/tx/apply.h>
|
||||
#include <xrpld/overlay/Cluster.h>
|
||||
#include <xrpld/overlay/detail/PeerImp.h>
|
||||
#include <xrpld/overlay/detail/ProposalPrecheck.h>
|
||||
#include <xrpld/overlay/detail/Tuning.h>
|
||||
#include <xrpld/perflog/PerfLog.h>
|
||||
#include <xrpl/basics/UptimeClock.h>
|
||||
@@ -40,7 +41,6 @@
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/protocol/ExportLimits.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
@@ -1721,14 +1721,6 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProposeSet> const& m)
|
||||
return;
|
||||
}
|
||||
|
||||
if (set.currenttxhash().size() < uint256::size() ||
|
||||
!stringIsUint256Sized(set.previousledger()))
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Proposal: malformed";
|
||||
fee_.update(Resource::feeMalformedRequest, "bad hashes");
|
||||
return;
|
||||
}
|
||||
|
||||
// RH TODO: when isTrusted = false we should probably also cache a key
|
||||
// suppression for 30 seconds to avoid doing a relatively expensive lookup
|
||||
// every time a spam packet is received
|
||||
@@ -1741,85 +1733,26 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProposeSet> const& m)
|
||||
if (!isTrusted && app_.config().RELAY_UNTRUSTED_PROPOSALS == -1)
|
||||
return;
|
||||
|
||||
auto const currentPosSlice = makeSlice(set.currenttxhash());
|
||||
SerialIter currentPosSit{currentPosSlice};
|
||||
auto const parsedPosition = ExtendedPosition::fromSerialIter(
|
||||
currentPosSit, set.currenttxhash().size());
|
||||
if (!parsedPosition)
|
||||
auto const openLedger = app_.openLedger().current();
|
||||
auto const precheck = detail::checkProposalExtensions(
|
||||
set,
|
||||
openLedger && openLedger->rules().enabled(featureConsensusEntropy),
|
||||
openLedger && openLedger->rules().enabled(featureExport));
|
||||
if (auto const rejection =
|
||||
detail::proposalPrecheckRejection(precheck.result))
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Proposal: malformed extended position";
|
||||
fee_.update(Resource::feeMalformedRequest, "bad proposal position");
|
||||
return;
|
||||
}
|
||||
bool const hasEntropyMaterial = parsedPosition->commitSetHash ||
|
||||
parsedPosition->entropySetHash || parsedPosition->myCommitment ||
|
||||
parsedPosition->myReveal;
|
||||
bool const hasExportMaterial = parsedPosition->exportSigSetHash ||
|
||||
parsedPosition->exportSignaturesHash || set.exportsignatures_size() > 0;
|
||||
if (hasEntropyMaterial || hasExportMaterial)
|
||||
{
|
||||
auto const openLedger = app_.openLedger().current();
|
||||
bool const entropyEnabled =
|
||||
openLedger && openLedger->rules().enabled(featureConsensusEntropy);
|
||||
bool const exportEnabled =
|
||||
openLedger && openLedger->rules().enabled(featureExport);
|
||||
if (hasEntropyMaterial && !entropyEnabled)
|
||||
{
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Proposal: entropy fields while featureConsensusEntropy "
|
||||
"disabled";
|
||||
fee_.update(
|
||||
Resource::feeMalformedRequest, "entropy fields disabled");
|
||||
return;
|
||||
}
|
||||
if (hasExportMaterial && !exportEnabled)
|
||||
{
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Proposal: export fields while featureExport disabled";
|
||||
fee_.update(
|
||||
Resource::feeMalformedRequest, "export fields disabled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (set.exportsignatures_size() > ExportLimits::maxPendingExports)
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Proposal: too many export signatures";
|
||||
fee_.update(Resource::feeMalformedRequest, "too many export sigs");
|
||||
return;
|
||||
}
|
||||
|
||||
if (set.exportsignatures_size() > 0)
|
||||
{
|
||||
if (!parsedPosition->exportSignaturesHash)
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Proposal: unsigned export signatures";
|
||||
fee_.update(Resource::feeMalformedRequest, "unsigned export sigs");
|
||||
return;
|
||||
}
|
||||
|
||||
if (proposalExportSignaturesHash(set.exportsignatures()) !=
|
||||
*parsedPosition->exportSignaturesHash)
|
||||
{
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Proposal: export signatures hash mismatch";
|
||||
fee_.update(
|
||||
Resource::feeMalformedRequest, "export sig hash mismatch");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (parsedPosition->exportSignaturesHash)
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Proposal: missing signed export signatures";
|
||||
fee_.update(Resource::feeMalformedRequest, "missing export sigs");
|
||||
JLOG(p_journal_.warn()) << rejection->logMessage;
|
||||
fee_.update(Resource::feeMalformedRequest, rejection->feeReason);
|
||||
return;
|
||||
}
|
||||
auto const& parsedPosition = *precheck.position;
|
||||
|
||||
uint256 const prevLedger{set.previousledger()};
|
||||
|
||||
NetClock::time_point const closeTime{NetClock::duration{set.closetime()}};
|
||||
|
||||
uint256 const suppression = proposalUniqueId(
|
||||
*parsedPosition,
|
||||
parsedPosition,
|
||||
prevLedger,
|
||||
set.proposeseq(),
|
||||
closeTime,
|
||||
@@ -1876,7 +1809,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProposeSet> const& m)
|
||||
RCLCxPeerPos::Proposal{
|
||||
prevLedger,
|
||||
set.proposeseq(),
|
||||
*parsedPosition,
|
||||
parsedPosition,
|
||||
closeTime,
|
||||
app_.timeKeeper().closeTime(),
|
||||
calcNodeID(app_.validatorManifests().getMasterKey(publicKey))},
|
||||
|
||||
138
src/xrpld/overlay/detail/ProposalPrecheck.h
Normal file
138
src/xrpld/overlay/detail/ProposalPrecheck.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef RIPPLE_OVERLAY_DETAIL_PROPOSALPRECHECK_H_INCLUDED
|
||||
#define RIPPLE_OVERLAY_DETAIL_PROPOSALPRECHECK_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/consensus/RCLCxPeerPos.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/ExportLimits.h>
|
||||
#include <xrpl/protocol/messages.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
|
||||
enum class ProposalPrecheckResult {
|
||||
ok,
|
||||
badHashes,
|
||||
badPosition,
|
||||
entropyDisabled,
|
||||
exportDisabled,
|
||||
tooManyExportSignatures,
|
||||
unsignedExportSignatures,
|
||||
exportSignaturesHashMismatch,
|
||||
missingExportSignatures
|
||||
};
|
||||
|
||||
struct ProposalPrecheck
|
||||
{
|
||||
ProposalPrecheckResult result = ProposalPrecheckResult::ok;
|
||||
std::optional<ExtendedPosition> position;
|
||||
};
|
||||
|
||||
struct ProposalPrecheckRejection
|
||||
{
|
||||
char const* logMessage;
|
||||
char const* feeReason;
|
||||
};
|
||||
|
||||
inline std::optional<ProposalPrecheckRejection>
|
||||
proposalPrecheckRejection(ProposalPrecheckResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case ProposalPrecheckResult::ok:
|
||||
return std::nullopt;
|
||||
case ProposalPrecheckResult::badHashes:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: malformed", "bad hashes"};
|
||||
case ProposalPrecheckResult::badPosition:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: malformed extended position",
|
||||
"bad proposal position"};
|
||||
case ProposalPrecheckResult::entropyDisabled:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: entropy fields while featureConsensusEntropy disabled",
|
||||
"entropy fields disabled"};
|
||||
case ProposalPrecheckResult::exportDisabled:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: export fields while featureExport disabled",
|
||||
"export fields disabled"};
|
||||
case ProposalPrecheckResult::tooManyExportSignatures:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: too many export signatures", "too many export sigs"};
|
||||
case ProposalPrecheckResult::unsignedExportSignatures:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: unsigned export signatures", "unsigned export sigs"};
|
||||
case ProposalPrecheckResult::exportSignaturesHashMismatch:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: export signatures hash mismatch",
|
||||
"export sig hash mismatch"};
|
||||
case ProposalPrecheckResult::missingExportSignatures:
|
||||
return ProposalPrecheckRejection{
|
||||
"Proposal: missing signed export signatures",
|
||||
"missing export sigs"};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline ProposalPrecheck
|
||||
checkProposalExtensions(
|
||||
protocol::TMProposeSet const& set,
|
||||
bool entropyEnabled,
|
||||
bool exportEnabled)
|
||||
{
|
||||
if (set.currenttxhash().size() < uint256::size() ||
|
||||
set.previousledger().size() != uint256::size())
|
||||
{
|
||||
return {ProposalPrecheckResult::badHashes, std::nullopt};
|
||||
}
|
||||
|
||||
auto const currentPosSlice = makeSlice(set.currenttxhash());
|
||||
SerialIter currentPosSit{currentPosSlice};
|
||||
auto const parsedPosition = ExtendedPosition::fromSerialIter(
|
||||
currentPosSit, set.currenttxhash().size());
|
||||
if (!parsedPosition)
|
||||
return {ProposalPrecheckResult::badPosition, std::nullopt};
|
||||
|
||||
bool const hasEntropyMaterial = parsedPosition->commitSetHash ||
|
||||
parsedPosition->entropySetHash || parsedPosition->myCommitment ||
|
||||
parsedPosition->myReveal;
|
||||
bool const hasExportMaterial = parsedPosition->exportSigSetHash ||
|
||||
parsedPosition->exportSignaturesHash || set.exportsignatures_size() > 0;
|
||||
if (hasEntropyMaterial && !entropyEnabled)
|
||||
return {ProposalPrecheckResult::entropyDisabled, parsedPosition};
|
||||
if (hasExportMaterial && !exportEnabled)
|
||||
return {ProposalPrecheckResult::exportDisabled, parsedPosition};
|
||||
|
||||
if (set.exportsignatures_size() > ExportLimits::maxPendingExports)
|
||||
return {
|
||||
ProposalPrecheckResult::tooManyExportSignatures, parsedPosition};
|
||||
|
||||
if (set.exportsignatures_size() > 0)
|
||||
{
|
||||
if (!parsedPosition->exportSignaturesHash)
|
||||
return {
|
||||
ProposalPrecheckResult::unsignedExportSignatures,
|
||||
parsedPosition};
|
||||
|
||||
if (proposalExportSignaturesHash(set.exportsignatures()) !=
|
||||
*parsedPosition->exportSignaturesHash)
|
||||
{
|
||||
return {
|
||||
ProposalPrecheckResult::exportSignaturesHashMismatch,
|
||||
parsedPosition};
|
||||
}
|
||||
}
|
||||
else if (parsedPosition->exportSignaturesHash)
|
||||
{
|
||||
return {
|
||||
ProposalPrecheckResult::missingExportSignatures, parsedPosition};
|
||||
}
|
||||
|
||||
return {ProposalPrecheckResult::ok, parsedPosition};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user