mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-23 15:10:10 +00:00
test(consensus): cover member registry replay
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
#include <test/jtx/unl.h>
|
||||
#include <test/jtx/xpop.h>
|
||||
#include <xrpld/app/ledger/BuildLedger.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/ledger/LedgerReplay.h>
|
||||
@@ -27,11 +29,19 @@
|
||||
#include <xrpld/app/ledger/detail/LedgerDeltaAcquire.h>
|
||||
#include <xrpld/app/ledger/detail/LedgerReplayMsgHandler.h>
|
||||
#include <xrpld/app/ledger/detail/SkipListAcquire.h>
|
||||
#include <xrpld/app/misc/CanonicalTXSet.h>
|
||||
#include <xrpld/app/misc/HashRouter.h>
|
||||
#include <xrpld/app/misc/Manifest.h>
|
||||
#include <xrpld/app/misc/UNLReportMember.h>
|
||||
#include <xrpld/overlay/PeerSet.h>
|
||||
#include <xrpld/overlay/detail/PeerImp.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
namespace ripple {
|
||||
@@ -39,8 +49,86 @@ namespace test {
|
||||
|
||||
struct LedgerReplay_test : public beast::unit_test::suite
|
||||
{
|
||||
static std::unique_ptr<Config>
|
||||
replayConfig()
|
||||
{
|
||||
auto c = jtx::envconfig();
|
||||
auto& sectionNode = c->section(ConfigSection::nodeDatabase());
|
||||
sectionNode.set("type", "memory");
|
||||
c->overwrite(SECTION_RELATIONAL_DB, "backend", "sqlite");
|
||||
return c;
|
||||
}
|
||||
|
||||
bool
|
||||
expectUNLReportMemberSLE(
|
||||
std::shared_ptr<Ledger const> const& ledger,
|
||||
Manifest const& manifest)
|
||||
{
|
||||
auto const sle =
|
||||
ledger->read(keylet::UNLReportMember(manifest.masterKey));
|
||||
BEAST_EXPECT(sle);
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
Blob const expectedBlob{
|
||||
manifest.serialized.begin(), manifest.serialized.end()};
|
||||
|
||||
BEAST_EXPECT(
|
||||
PublicKey{makeSlice(sle->getFieldVL(sfPublicKey))} ==
|
||||
manifest.masterKey);
|
||||
BEAST_EXPECT(sle->getFieldU32(sfSequence) == manifest.sequence);
|
||||
BEAST_EXPECT(sle->getFieldVL(sfBlob) == expectedBlob);
|
||||
BEAST_EXPECT(sle->getFieldH256(sfDigest) == manifest.bindingID());
|
||||
BEAST_EXPECT(sle->getFlags() == 0);
|
||||
BEAST_EXPECT(manifest.signingKey);
|
||||
BEAST_EXPECT(sle->isFieldPresent(sfSigningPubKey));
|
||||
if (manifest.signingKey && sle->isFieldPresent(sfSigningPubKey))
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
PublicKey{makeSlice(sle->getFieldVL(sfSigningPubKey))} ==
|
||||
*manifest.signingKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
expectSameUNLReportMemberSLE(
|
||||
std::shared_ptr<Ledger const> const& lhs,
|
||||
std::shared_ptr<Ledger const> const& rhs,
|
||||
PublicKey const& masterKey)
|
||||
{
|
||||
auto const left = lhs->read(keylet::UNLReportMember(masterKey));
|
||||
auto const right = rhs->read(keylet::UNLReportMember(masterKey));
|
||||
BEAST_EXPECT(left);
|
||||
BEAST_EXPECT(right);
|
||||
if (!left || !right)
|
||||
return false;
|
||||
|
||||
BEAST_EXPECT(left->getFlags() == right->getFlags());
|
||||
BEAST_EXPECT(
|
||||
left->getFieldU32(sfSequence) == right->getFieldU32(sfSequence));
|
||||
BEAST_EXPECT(
|
||||
left->getFieldVL(sfPublicKey) == right->getFieldVL(sfPublicKey));
|
||||
BEAST_EXPECT(left->getFieldVL(sfBlob) == right->getFieldVL(sfBlob));
|
||||
BEAST_EXPECT(
|
||||
left->getFieldH256(sfDigest) == right->getFieldH256(sfDigest));
|
||||
BEAST_EXPECT(
|
||||
left->isFieldPresent(sfSigningPubKey) ==
|
||||
right->isFieldPresent(sfSigningPubKey));
|
||||
if (left->isFieldPresent(sfSigningPubKey) &&
|
||||
right->isFieldPresent(sfSigningPubKey))
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
left->getFieldVL(sfSigningPubKey) ==
|
||||
right->getFieldVL(sfSigningPubKey));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
testReplayLedger()
|
||||
{
|
||||
testcase("Replay ledger");
|
||||
|
||||
@@ -50,13 +138,7 @@ struct LedgerReplay_test : public beast::unit_test::suite
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
|
||||
Env env = [&] {
|
||||
auto c = jtx::envconfig();
|
||||
auto& sectionNode = c->section(ConfigSection::nodeDatabase());
|
||||
sectionNode.set("type", "memory");
|
||||
c->overwrite(SECTION_RELATIONAL_DB, "backend", "sqlite");
|
||||
return jtx::Env(*this, std::move(c));
|
||||
}();
|
||||
Env env(*this, replayConfig());
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
env.close();
|
||||
|
||||
@@ -73,6 +155,107 @@ struct LedgerReplay_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECT(replayed->info().hash == lastClosed->info().hash);
|
||||
}
|
||||
|
||||
void
|
||||
testReplayUNLReportMemberBootstrap()
|
||||
{
|
||||
testcase("Replay UNLReport member bootstrap");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env{
|
||||
*this, replayConfig(), supported_amendments() | featureUNLReportV2};
|
||||
|
||||
auto const validator = xpop::TestValidator::create();
|
||||
auto const manifest =
|
||||
deserializeManifest(validator.manifestRaw, env.journal);
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest && manifest->verify());
|
||||
if (!manifest || !manifest->verify())
|
||||
return;
|
||||
|
||||
auto evidenceInAppCache = [&]() {
|
||||
auto const snapshot =
|
||||
env.app()
|
||||
.validatorManifests()
|
||||
.getUNLReportMemberManifestEvidenceSnapshot();
|
||||
return std::any_of(
|
||||
snapshot.begin(), snapshot.end(), [&](Manifest const& m) {
|
||||
return m.masterKey == validator.masterPublic;
|
||||
});
|
||||
};
|
||||
BEAST_EXPECT(!evidenceInAppCache());
|
||||
|
||||
env.app().openLedger().modify(
|
||||
[&](OpenView& view, beast::Journal) -> bool {
|
||||
auto const tx = unl::createUNLReportTx(
|
||||
env.current()->seq(),
|
||||
validator.masterPublic,
|
||||
validator.masterPublic);
|
||||
auto const txID = tx.getTransactionID();
|
||||
auto s = std::make_shared<Serializer>();
|
||||
tx.add(*s);
|
||||
env.app().getHashRouter().setFlags(txID, SF_PRIVATE2);
|
||||
view.rawTxInsert(txID, std::move(s), nullptr);
|
||||
return true;
|
||||
});
|
||||
|
||||
BEAST_EXPECT(env.close());
|
||||
|
||||
auto const parent = env.app().getLedgerMaster().getClosedLedger();
|
||||
BEAST_EXPECT(parent);
|
||||
if (!parent)
|
||||
return;
|
||||
BEAST_EXPECT(parent->read(keylet::UNLReport()));
|
||||
BEAST_EXPECT(!evidenceInAppCache());
|
||||
|
||||
ManifestCache localManifests{env.journal};
|
||||
BEAST_EXPECT(localManifests.observeUNLReportMemberManifestEvidence(
|
||||
makeSlice(validator.manifestRaw)));
|
||||
|
||||
auto updates = buildUNLReportMemberUpdates(
|
||||
*parent,
|
||||
localManifests.getUNLReportMemberManifestEvidenceSnapshot());
|
||||
BEAST_EXPECT(updates.size() == 1);
|
||||
if (updates.size() != 1)
|
||||
return;
|
||||
|
||||
auto const& memberTx = updates.front();
|
||||
BEAST_EXPECT(memberTx.getTxnType() == ttUNL_REPORT_MEMBER);
|
||||
BEAST_EXPECT(!evidenceInAppCache());
|
||||
|
||||
CanonicalTXSet txns{parent->info().hash};
|
||||
txns.insert(std::make_shared<STTx const>(memberTx));
|
||||
std::set<TxID> failed;
|
||||
|
||||
auto const closed = buildLedger(
|
||||
parent,
|
||||
env.app().timeKeeper().closeTime(),
|
||||
true,
|
||||
parent->info().closeTimeResolution,
|
||||
env.app(),
|
||||
txns,
|
||||
failed,
|
||||
env.journal);
|
||||
BEAST_EXPECT(txns.empty());
|
||||
BEAST_EXPECT(failed.empty());
|
||||
expectUNLReportMemberSLE(closed, *manifest);
|
||||
BEAST_EXPECT(!evidenceInAppCache());
|
||||
|
||||
auto const replayed = buildLedger(
|
||||
LedgerReplay(parent, closed), tapNONE, env.app(), env.journal);
|
||||
BEAST_EXPECT(replayed->info().hash == closed->info().hash);
|
||||
expectUNLReportMemberSLE(replayed, *manifest);
|
||||
expectSameUNLReportMemberSLE(closed, replayed, validator.masterPublic);
|
||||
BEAST_EXPECT(!evidenceInAppCache());
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testReplayLedger();
|
||||
testReplayUNLReportMemberBootstrap();
|
||||
}
|
||||
};
|
||||
|
||||
enum class InboundLedgersBehavior {
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/Sign.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
@@ -347,6 +349,95 @@ class UNLReportMember_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(sle && sle->getFlags() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
testEquivocationConvergesAcrossParentRounds()
|
||||
{
|
||||
testcase(
|
||||
"equal-sequence equivocation converges across parent-ledger "
|
||||
"rounds");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const [masterPublic, masterSecret] = generateKeyPair(
|
||||
KeyType::ed25519,
|
||||
generateSeed("unl-report-member-converge-master"));
|
||||
auto const [signingPublic, signingSecret] = generateKeyPair(
|
||||
KeyType::secp256k1,
|
||||
generateSeed("unl-report-member-converge-signing-0"));
|
||||
Validator const v{
|
||||
masterPublic, masterSecret, signingPublic, signingSecret};
|
||||
auto const [otherPublic, otherSecret] = generateKeyPair(
|
||||
KeyType::secp256k1,
|
||||
generateSeed("unl-report-member-converge-signing-1"));
|
||||
auto const [thirdPublic, thirdSecret] = generateKeyPair(
|
||||
KeyType::secp256k1,
|
||||
generateSeed("unl-report-member-converge-signing-2"));
|
||||
|
||||
struct Statement
|
||||
{
|
||||
std::string serialized;
|
||||
uint256 binding;
|
||||
};
|
||||
|
||||
std::array<Statement, 3> statements{{
|
||||
{manifest(v, 1), {}},
|
||||
{manifest(v, otherPublic, otherSecret, 1), {}},
|
||||
{manifest(v, thirdPublic, thirdSecret, 1), {}},
|
||||
}};
|
||||
for (auto& statement : statements)
|
||||
statement.binding = parsed(statement.serialized).bindingID();
|
||||
std::sort(
|
||||
statements.begin(),
|
||||
statements.end(),
|
||||
[](Statement const& lhs, Statement const& rhs) {
|
||||
return lhs.binding < rhs.binding;
|
||||
});
|
||||
BEAST_EXPECT(statements[0].binding < statements[1].binding);
|
||||
BEAST_EXPECT(statements[1].binding < statements[2].binding);
|
||||
|
||||
auto evidence = [](Statement const& first, Statement const& second) {
|
||||
std::vector<Manifest> result;
|
||||
result.emplace_back(parsed(first.serialized));
|
||||
result.emplace_back(parsed(second.serialized));
|
||||
return result;
|
||||
};
|
||||
auto ledger = parentLedger(env, {v.masterPublic});
|
||||
auto applyRound = [&](std::vector<Manifest> observed) {
|
||||
auto const updates =
|
||||
buildUNLReportMemberUpdates(*ledger, std::move(observed));
|
||||
auto next = std::make_shared<Ledger>(
|
||||
*ledger, env.app().timeKeeper().closeTime());
|
||||
OpenView view(&*next);
|
||||
for (auto const& update : updates)
|
||||
BEAST_EXPECT(apply(env, view, update) == tesSUCCESS);
|
||||
view.apply(*next);
|
||||
ledger = std::move(next);
|
||||
return updates.size();
|
||||
};
|
||||
auto expectStored = [&](Statement const& statement) {
|
||||
ReadView const& view = *ledger;
|
||||
auto const sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle && sle->getFieldU32(sfSequence) == 1);
|
||||
BEAST_EXPECT(
|
||||
sle &&
|
||||
(sle->getFlags() & lsfUNLReportMemberEquivocationFreeze));
|
||||
BEAST_EXPECT(
|
||||
sle && sle->getFieldH256(sfDigest) == statement.binding);
|
||||
};
|
||||
|
||||
BEAST_EXPECT(applyRound(evidence(statements[1], statements[2])) == 2);
|
||||
expectStored(statements[1]);
|
||||
|
||||
BEAST_EXPECT(applyRound(evidence(statements[0], statements[2])) == 1);
|
||||
expectStored(statements[0]);
|
||||
|
||||
BEAST_EXPECT(buildUNLReportMemberUpdates(
|
||||
*ledger, evidence(statements[1], statements[2]))
|
||||
.empty());
|
||||
BEAST_EXPECT(buildUNLReportMemberUpdates(
|
||||
*ledger, evidence(statements[0], statements[2]))
|
||||
.empty());
|
||||
}
|
||||
|
||||
void
|
||||
testSigningKeyCollision()
|
||||
{
|
||||
@@ -474,6 +565,7 @@ public:
|
||||
testRotationAndRevocation();
|
||||
testInactiveMemberCanRevoke();
|
||||
testEquivocationIsOrderIndependent();
|
||||
testEquivocationConvergesAcrossParentRounds();
|
||||
testSigningKeyCollision();
|
||||
testDeltaSelection();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user