mirror of
https://github.com/Xahau/xahaud.git
synced 2026-10-04 10:58:03 +00:00
Revert "feat(consensus): add UNL report member registry"
This reverts commit ab30826c5c.
This commit is contained in:
@@ -121,9 +121,6 @@ negativeUNL() noexcept;
|
||||
Keylet const&
|
||||
UNLReport() noexcept;
|
||||
|
||||
Keylet
|
||||
UNLReportMember(PublicKey const& key) noexcept;
|
||||
|
||||
/** The (fixed) index of the object containing consensus-derived entropy. */
|
||||
Keylet const&
|
||||
consensusEntropy() noexcept;
|
||||
|
||||
@@ -183,10 +183,6 @@ enum LedgerSpecificFlags {
|
||||
// ltURI_TOKEN
|
||||
lsfBurnable = 0x00000001, // True, issuer can burn the token
|
||||
|
||||
// ltUNL_REPORT_MEMBER
|
||||
lsfUNLReportMemberEquivocationFreeze = 0x00000001,
|
||||
lsfUNLReportMemberSigningKeyCollisionFreeze = 0x00000002,
|
||||
|
||||
// remarks
|
||||
lsfImmutable = 1,
|
||||
|
||||
|
||||
@@ -240,20 +240,6 @@ LEDGER_ENTRY_DUPLICATE(ltCONSENSUS_ENTROPY, 0x0058, ConsensusEntropy, consensus_
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** A ledger object which records a UNL report member.
|
||||
|
||||
\sa keylet::UNLReportMember
|
||||
*/
|
||||
LEDGER_ENTRY_DUPLICATE(ltUNL_REPORT_MEMBER, 0x0059, UNLReportMember, unl_report_member, ({
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfSequence, soeREQUIRED},
|
||||
{sfBlob, soeREQUIRED},
|
||||
{sfDigest, soeREQUIRED},
|
||||
{sfSigningPubKey, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** A ledger object which describes an account.
|
||||
|
||||
\sa keylet::account
|
||||
|
||||
@@ -631,7 +631,3 @@ TRANSACTION(ttEXPORT_SIGNATURES, 106, ExportSignatures, ({
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfTransactionHash, soeREQUIRED},
|
||||
}))
|
||||
|
||||
TRANSACTION(ttUNL_REPORT_MEMBER, 107, UNLReportMember, ({
|
||||
{sfBlob, soeREQUIRED},
|
||||
}))
|
||||
|
||||
@@ -81,7 +81,6 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
URI_TOKEN = 'U',
|
||||
IMPORT_VLSEQ = 'I',
|
||||
UNL_REPORT = 'R',
|
||||
UNL_REPORT_MEMBER = LEDGER_NAMESPACE2(0x01, 'M'),
|
||||
CRON = 'L',
|
||||
CONSENSUS_ENTROPY = 'X',
|
||||
AMM = 'A',
|
||||
@@ -287,14 +286,6 @@ UNLReport() noexcept
|
||||
return ret;
|
||||
}
|
||||
|
||||
Keylet
|
||||
UNLReportMember(PublicKey const& key) noexcept
|
||||
{
|
||||
return {
|
||||
ltUNL_REPORT_MEMBER,
|
||||
indexHash(LedgerNameSpace::UNL_REPORT_MEMBER, key)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
book_t::operator()(Book const& b) const
|
||||
{
|
||||
|
||||
@@ -685,8 +685,7 @@ isPseudoTx(STObject const& tx)
|
||||
auto tt = safe_cast<TxType>(*t);
|
||||
return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY ||
|
||||
tt == ttEMIT_FAILURE || tt == ttUNL_REPORT || tt == ttCRON ||
|
||||
tt == ttCONSENSUS_ENTROPY || tt == ttEXPORT_SIGNATURES ||
|
||||
tt == ttUNL_REPORT_MEMBER;
|
||||
tt == ttCONSENSUS_ENTROPY || tt == ttEXPORT_SIGNATURES;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/xpop.h>
|
||||
#include <xrpld/app/tx/apply.h>
|
||||
#include <xrpld/app/tx/detail/ExportResultBuilder.h>
|
||||
#include <xrpl/protocol/EntropyTier.h>
|
||||
@@ -93,20 +92,6 @@ struct PseudoTx_test : public beast::unit_test::suite
|
||||
res.emplace_back(ExportResultBuilder::buildSignatureWitness(
|
||||
uint256(4), signatures, seq));
|
||||
|
||||
auto const masterSecret = randomSecretKey();
|
||||
auto const masterPublic =
|
||||
derivePublicKey(KeyType::ed25519, masterSecret);
|
||||
auto const [signingPublic, signingSecret] =
|
||||
randomKeyPair(KeyType::secp256k1);
|
||||
auto const manifest = jtx::xpop::makeManifestRaw(
|
||||
masterPublic, masterSecret, signingPublic, signingSecret, 1);
|
||||
res.emplace_back(STTx(ttUNL_REPORT_MEMBER, [&](auto& obj) {
|
||||
obj.setAccountID(sfAccount, AccountID());
|
||||
obj.setFieldU32(sfSequence, 0);
|
||||
obj.setFieldAmount(sfFee, STAmount{});
|
||||
obj.setFieldVL(sfBlob, makeSlice(manifest));
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -6226,15 +6226,6 @@ private:
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testUNLReportMemberTSH(FeatureBitset features)
|
||||
{
|
||||
testcase("unl report member tsh");
|
||||
|
||||
// pseudo transaction
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testConsensusEntropyTSH(FeatureBitset features)
|
||||
{
|
||||
|
||||
@@ -1,362 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2026 Xahau
|
||||
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/xpop.h>
|
||||
#include <xrpld/app/tx/apply.h>
|
||||
#include <xrpld/ledger/OpenView.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Sign.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class UNLReportMember_test : public beast::unit_test::suite
|
||||
{
|
||||
struct Validator
|
||||
{
|
||||
PublicKey masterPublic;
|
||||
SecretKey masterSecret;
|
||||
PublicKey signingPublic;
|
||||
SecretKey signingSecret;
|
||||
};
|
||||
|
||||
static Validator
|
||||
validator()
|
||||
{
|
||||
auto const masterSecret = randomSecretKey();
|
||||
auto const masterPublic =
|
||||
derivePublicKey(KeyType::ed25519, masterSecret);
|
||||
auto const [signingPublic, signingSecret] =
|
||||
randomKeyPair(KeyType::secp256k1);
|
||||
return {masterPublic, masterSecret, signingPublic, signingSecret};
|
||||
}
|
||||
|
||||
static std::string
|
||||
manifest(Validator const& v, std::uint32_t sequence)
|
||||
{
|
||||
return jtx::xpop::makeManifestRaw(
|
||||
v.masterPublic,
|
||||
v.masterSecret,
|
||||
v.signingPublic,
|
||||
v.signingSecret,
|
||||
sequence);
|
||||
}
|
||||
|
||||
static std::string
|
||||
manifest(
|
||||
Validator const& v,
|
||||
PublicKey const& signingPublic,
|
||||
SecretKey const& signingSecret,
|
||||
std::uint32_t sequence)
|
||||
{
|
||||
return jtx::xpop::makeManifestRaw(
|
||||
v.masterPublic,
|
||||
v.masterSecret,
|
||||
signingPublic,
|
||||
signingSecret,
|
||||
sequence);
|
||||
}
|
||||
|
||||
static std::string
|
||||
revocation(Validator const& v)
|
||||
{
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = std::numeric_limits<std::uint32_t>::max();
|
||||
st[sfPublicKey] = v.masterPublic;
|
||||
sign(
|
||||
st,
|
||||
HashPrefix::manifest,
|
||||
KeyType::ed25519,
|
||||
v.masterSecret,
|
||||
sfMasterSignature);
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
return std::string(static_cast<char const*>(s.data()), s.size());
|
||||
}
|
||||
|
||||
static STTx
|
||||
memberTx(std::string const& blob)
|
||||
{
|
||||
return STTx(ttUNL_REPORT_MEMBER, [&](auto& obj) {
|
||||
obj.setAccountID(sfAccount, AccountID{});
|
||||
obj.setFieldU32(sfSequence, 0);
|
||||
obj.setFieldAmount(sfFee, STAmount{});
|
||||
obj.setFieldVL(sfBlob, makeSlice(blob));
|
||||
});
|
||||
}
|
||||
|
||||
static std::shared_ptr<Ledger>
|
||||
parentLedger(jtx::Env& env, std::vector<PublicKey> const& members)
|
||||
{
|
||||
auto const parent = env.app().getLedgerMaster().getClosedLedger();
|
||||
auto ledger = std::make_shared<Ledger>(
|
||||
*parent, env.app().timeKeeper().closeTime());
|
||||
auto report = std::make_shared<SLE>(keylet::UNLReport());
|
||||
std::vector<STObject> active;
|
||||
for (auto const& key : members)
|
||||
{
|
||||
active.push_back(STObject::makeInnerObject(sfActiveValidator));
|
||||
active.back().setFieldVL(sfPublicKey, key);
|
||||
}
|
||||
report->setFieldArray(
|
||||
sfActiveValidators, STArray(active, sfActiveValidators));
|
||||
|
||||
OpenView accum(&*ledger);
|
||||
accum.rawInsert(report);
|
||||
accum.apply(*ledger);
|
||||
return ledger;
|
||||
}
|
||||
|
||||
TER
|
||||
apply(jtx::Env& env, OpenView& view, STTx const& tx)
|
||||
{
|
||||
return ripple::apply(env.app(), view, tx, tapNONE, env.journal).ter;
|
||||
}
|
||||
|
||||
static void
|
||||
setMembers(OpenView& view, std::vector<PublicKey> const& members)
|
||||
{
|
||||
auto report = std::make_shared<SLE>(*view.read(keylet::UNLReport()));
|
||||
std::vector<STObject> active;
|
||||
for (auto const& key : members)
|
||||
{
|
||||
active.push_back(STObject::makeInnerObject(sfActiveValidator));
|
||||
active.back().setFieldVL(sfPublicKey, key);
|
||||
}
|
||||
report->setFieldArray(
|
||||
sfActiveValidators, STArray(active, sfActiveValidators));
|
||||
view.rawReplace(report);
|
||||
}
|
||||
|
||||
void
|
||||
testAmendmentAndAdmission()
|
||||
{
|
||||
testcase("amendment and parent admission");
|
||||
auto const v = validator();
|
||||
|
||||
{
|
||||
jtx::Env env(
|
||||
*this, jtx::supported_amendments() - featureUNLReportV2);
|
||||
auto parent = parentLedger(env, {v.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(manifest(v, 1))) == temDISABLED);
|
||||
BEAST_EXPECT(!view.read(keylet::UNLReportMember(v.masterPublic)));
|
||||
}
|
||||
|
||||
{
|
||||
jtx::Env env(
|
||||
*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto parent = parentLedger(env, {});
|
||||
OpenView view(&*parent);
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(manifest(v, 1))) == tefBAD_AUTH);
|
||||
BEAST_EXPECT(!view.read(keylet::UNLReportMember(v.masterPublic)));
|
||||
}
|
||||
|
||||
{
|
||||
jtx::Env env(
|
||||
*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto parent = parentLedger(env, {});
|
||||
OpenView view(&*parent);
|
||||
setMembers(view, {v.masterPublic});
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(manifest(v, 1))) == tefBAD_AUTH);
|
||||
BEAST_EXPECT(!view.read(keylet::UNLReportMember(v.masterPublic)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testMalformedEvidence()
|
||||
{
|
||||
testcase("malformed evidence is rejected without state changes");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const v = validator();
|
||||
auto parent = parentLedger(env, {v.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(std::string(1, '\0'))) == temMALFORMED);
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(std::string(2049, 'x'))) == temMALFORMED);
|
||||
|
||||
auto badSignature = manifest(v, 1);
|
||||
badSignature.back() ^= 1;
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(badSignature)) == temBAD_SIGNATURE);
|
||||
BEAST_EXPECT(!view.read(keylet::UNLReportMember(v.masterPublic)));
|
||||
}
|
||||
|
||||
void
|
||||
testRotationAndRevocation()
|
||||
{
|
||||
testcase("rotation, idempotence, and revocation");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const v = validator();
|
||||
auto parent = parentLedger(env, {v.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
|
||||
auto const first = memberTx(manifest(v, 1));
|
||||
BEAST_EXPECT(apply(env, view, first) == tesSUCCESS);
|
||||
BEAST_EXPECT(apply(env, view, first) == tefALREADY);
|
||||
|
||||
auto const [nextPublic, nextSecret] = randomKeyPair(KeyType::secp256k1);
|
||||
BEAST_EXPECT(
|
||||
apply(
|
||||
env, view, memberTx(manifest(v, nextPublic, nextSecret, 2))) ==
|
||||
tesSUCCESS);
|
||||
|
||||
auto sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle && sle->getFieldU32(sfSequence) == 2);
|
||||
BEAST_EXPECT(
|
||||
sle &&
|
||||
PublicKey{makeSlice(sle->getFieldVL(sfSigningPubKey))} ==
|
||||
nextPublic);
|
||||
|
||||
BEAST_EXPECT(apply(env, view, memberTx(revocation(v))) == tesSUCCESS);
|
||||
sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(sle && !sle->isFieldPresent(sfSigningPubKey));
|
||||
BEAST_EXPECT(
|
||||
apply(
|
||||
env, view, memberTx(manifest(v, nextPublic, nextSecret, 3))) ==
|
||||
tefPAST_SEQ);
|
||||
}
|
||||
|
||||
void
|
||||
testInactiveMemberCanRevoke()
|
||||
{
|
||||
testcase("inactive historical member can only revoke");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const v = validator();
|
||||
auto ledger = parentLedger(env, {v.masterPublic});
|
||||
{
|
||||
OpenView view(&*ledger);
|
||||
BEAST_EXPECT(
|
||||
apply(env, view, memberTx(manifest(v, 1))) == tesSUCCESS);
|
||||
view.apply(*ledger);
|
||||
}
|
||||
|
||||
auto inactive = std::make_shared<Ledger>(
|
||||
*ledger, env.app().timeKeeper().closeTime());
|
||||
{
|
||||
OpenView view(&*inactive);
|
||||
setMembers(view, {});
|
||||
view.apply(*inactive);
|
||||
}
|
||||
|
||||
OpenView view(&*inactive);
|
||||
BEAST_EXPECT(apply(env, view, memberTx(manifest(v, 2))) == tefBAD_AUTH);
|
||||
BEAST_EXPECT(apply(env, view, memberTx(revocation(v))) == tesSUCCESS);
|
||||
auto const sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(sle && !sle->isFieldPresent(sfSigningPubKey));
|
||||
BEAST_EXPECT(sle && Manifest::revoked(sle->getFieldU32(sfSequence)));
|
||||
}
|
||||
|
||||
void
|
||||
testEquivocationIsOrderIndependent()
|
||||
{
|
||||
testcase("equal-sequence equivocation freezes deterministically");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const v = validator();
|
||||
auto const [otherPublic, otherSecret] =
|
||||
randomKeyPair(KeyType::secp256k1);
|
||||
auto const a = memberTx(manifest(v, 1));
|
||||
auto const b = memberTx(manifest(v, otherPublic, otherSecret, 1));
|
||||
|
||||
auto applyPair = [&](STTx const& first, STTx const& second) {
|
||||
auto parent = parentLedger(env, {v.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
BEAST_EXPECT(apply(env, view, first) == tesSUCCESS);
|
||||
BEAST_EXPECT(apply(env, view, second) == tesSUCCESS);
|
||||
auto const sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(
|
||||
sle &&
|
||||
(sle->getFlags() & lsfUNLReportMemberEquivocationFreeze));
|
||||
return std::make_pair(
|
||||
sle->getFieldH256(sfDigest), sle->getFieldVL(sfBlob));
|
||||
};
|
||||
|
||||
BEAST_EXPECT(applyPair(a, b) == applyPair(b, a));
|
||||
|
||||
auto parent = parentLedger(env, {v.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
BEAST_EXPECT(apply(env, view, a) == tesSUCCESS);
|
||||
BEAST_EXPECT(apply(env, view, b) == tesSUCCESS);
|
||||
BEAST_EXPECT(
|
||||
apply(
|
||||
env,
|
||||
view,
|
||||
memberTx(manifest(v, otherPublic, otherSecret, 2))) ==
|
||||
tesSUCCESS);
|
||||
auto const sle = view.read(keylet::UNLReportMember(v.masterPublic));
|
||||
BEAST_EXPECT(sle && sle->getFlags() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
testSigningKeyCollision()
|
||||
{
|
||||
testcase("cross-member signing-key collision freezes both records");
|
||||
jtx::Env env(*this, jtx::supported_amendments() | featureUNLReportV2);
|
||||
auto const a = validator();
|
||||
auto const b = validator();
|
||||
auto parent = parentLedger(env, {a.masterPublic, b.masterPublic});
|
||||
OpenView view(&*parent);
|
||||
|
||||
BEAST_EXPECT(apply(env, view, memberTx(manifest(a, 1))) == tesSUCCESS);
|
||||
BEAST_EXPECT(
|
||||
apply(
|
||||
env,
|
||||
view,
|
||||
memberTx(manifest(b, a.signingPublic, a.signingSecret, 1))) ==
|
||||
tesSUCCESS);
|
||||
|
||||
auto const aSle = view.read(keylet::UNLReportMember(a.masterPublic));
|
||||
auto const bSle = view.read(keylet::UNLReportMember(b.masterPublic));
|
||||
BEAST_EXPECT(
|
||||
aSle &&
|
||||
(aSle->getFlags() & lsfUNLReportMemberSigningKeyCollisionFreeze));
|
||||
BEAST_EXPECT(
|
||||
bSle &&
|
||||
(bSle->getFlags() & lsfUNLReportMemberSigningKeyCollisionFreeze));
|
||||
|
||||
BEAST_EXPECT(apply(env, view, memberTx(manifest(b, 2))) == tesSUCCESS);
|
||||
BEAST_EXPECT(
|
||||
view.read(keylet::UNLReportMember(b.masterPublic))->getFlags() ==
|
||||
0);
|
||||
BEAST_EXPECT(apply(env, view, memberTx(manifest(a, 2))) == tesSUCCESS);
|
||||
BEAST_EXPECT(
|
||||
view.read(keylet::UNLReportMember(a.masterPublic))->getFlags() ==
|
||||
0);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testAmendmentAndAdmission();
|
||||
testMalformedEvidence();
|
||||
testRotationAndRevocation();
|
||||
testInactiveMemberCanRevoke();
|
||||
testEquivocationIsOrderIndependent();
|
||||
testSigningKeyCollision();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(UNLReportMember, consensus, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -585,8 +585,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
|
||||
case ttUNL_REPORT:
|
||||
case ttEXPORT:
|
||||
case ttCONSENSUS_ENTROPY:
|
||||
case ttEXPORT_SIGNATURES:
|
||||
case ttUNL_REPORT_MEMBER: {
|
||||
case ttEXPORT_SIGNATURES: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -172,12 +172,6 @@ public:
|
||||
return replayParentLedger_;
|
||||
}
|
||||
|
||||
ReadView const&
|
||||
parentView() const noexcept
|
||||
{
|
||||
return base_.parent();
|
||||
}
|
||||
|
||||
ApplyFlags const&
|
||||
flags()
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <xrpld/app/ledger/Ledger.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/app/misc/AmendmentTable.h>
|
||||
#include <xrpld/app/misc/Manifest.h>
|
||||
#include <xrpld/app/misc/NetworkOPs.h>
|
||||
#include <xrpld/app/tx/detail/Change.h>
|
||||
#include <xrpld/app/tx/detail/ExportResultBuilder.h>
|
||||
@@ -47,64 +46,6 @@ namespace ripple {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t maxUNLReportMemberManifestSize = 2048;
|
||||
|
||||
std::optional<Manifest>
|
||||
parseUNLReportMemberManifest(STTx const& tx, beast::Journal j)
|
||||
{
|
||||
auto const& blob = tx.getFieldVL(sfBlob);
|
||||
if (blob.empty() || blob.size() > maxUNLReportMemberManifestSize)
|
||||
return std::nullopt;
|
||||
|
||||
auto manifest = deserializeManifest(blob, j);
|
||||
if (!manifest || !manifest->verify())
|
||||
return std::nullopt;
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
std::vector<PublicKey>
|
||||
parentUNLReportMembers(ReadView const& parent)
|
||||
{
|
||||
std::vector<PublicKey> result;
|
||||
auto const report = parent.read(keylet::UNLReport());
|
||||
if (!report || !report->isFieldPresent(sfActiveValidators))
|
||||
return result;
|
||||
|
||||
for (auto const& entry : report->getFieldArray(sfActiveValidators))
|
||||
{
|
||||
auto const key = entry.getFieldVL(sfPublicKey);
|
||||
if (publicKeyType(makeSlice(key)))
|
||||
result.emplace_back(makeSlice(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
containsMaster(
|
||||
std::vector<PublicKey> const& members,
|
||||
PublicKey const& masterKey)
|
||||
{
|
||||
return std::find(members.begin(), members.end(), masterKey) !=
|
||||
members.end();
|
||||
}
|
||||
|
||||
void
|
||||
writeUNLReportMember(SLE& sle, Manifest const& manifest, std::uint32_t flags)
|
||||
{
|
||||
sle.setFieldVL(sfPublicKey, manifest.masterKey);
|
||||
sle.setFieldU32(sfSequence, manifest.sequence);
|
||||
sle.setFieldVL(sfBlob, makeSlice(manifest.serialized));
|
||||
sle.setFieldH256(sfDigest, manifest.bindingID());
|
||||
|
||||
if (manifest.signingKey)
|
||||
sle.setFieldVL(sfSigningPubKey, *manifest.signingKey);
|
||||
else
|
||||
sle.makeFieldAbsent(sfSigningPubKey);
|
||||
|
||||
sle.setFieldU32(sfFlags, flags);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
countSetBits(Blob const& bytes)
|
||||
{
|
||||
@@ -217,34 +158,6 @@ Change::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx.getTxnType() == ttUNL_REPORT_MEMBER)
|
||||
{
|
||||
if (!ctx.rules.enabled(featureUNLReportV2))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: UNLReportV2 is not enabled.";
|
||||
return temDISABLED;
|
||||
}
|
||||
|
||||
auto const& blob = ctx.tx.getFieldVL(sfBlob);
|
||||
if (blob.empty() || blob.size() > maxUNLReportMemberManifestSize)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: invalid UNLReport member blob size";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto manifest = deserializeManifest(blob, ctx.j);
|
||||
if (!manifest)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: malformed UNLReport member manifest";
|
||||
return temMALFORMED;
|
||||
}
|
||||
if (!manifest->verify())
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: invalid UNLReport member signature";
|
||||
return temBAD_SIGNATURE;
|
||||
}
|
||||
}
|
||||
|
||||
//@@start rng-consensus-entropy-preflight
|
||||
if (ctx.tx.getTxnType() == ttCONSENSUS_ENTROPY)
|
||||
{
|
||||
@@ -385,7 +298,6 @@ Change::preclaim(PreclaimContext const& ctx)
|
||||
case ttAMENDMENT:
|
||||
case ttUNL_MODIFY:
|
||||
case ttEMIT_FAILURE:
|
||||
case ttUNL_REPORT_MEMBER:
|
||||
case ttCONSENSUS_ENTROPY:
|
||||
case ttEXPORT_SIGNATURES:
|
||||
return tesSUCCESS;
|
||||
@@ -444,8 +356,6 @@ Change::doApply()
|
||||
return applyEmitFailure();
|
||||
case ttUNL_REPORT:
|
||||
return applyUNLReport();
|
||||
case ttUNL_REPORT_MEMBER:
|
||||
return applyUNLReportMember();
|
||||
case ttCONSENSUS_ENTROPY:
|
||||
return applyConsensusEntropy();
|
||||
case ttEXPORT_SIGNATURES:
|
||||
@@ -456,102 +366,6 @@ Change::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
TER
|
||||
Change::applyUNLReportMember()
|
||||
{
|
||||
auto manifest = parseUNLReportMemberManifest(ctx_.tx, j_);
|
||||
if (!manifest)
|
||||
return tefBAD_SIGNATURE;
|
||||
|
||||
auto const members = parentUNLReportMembers(ctx_.parentView());
|
||||
auto const memberKey = keylet::UNLReportMember(manifest->masterKey);
|
||||
auto sle = view().peek(memberKey);
|
||||
bool const parentActive = containsMaster(members, manifest->masterKey);
|
||||
|
||||
// Ordinary rotations require parent-ledger membership. Historical members
|
||||
// remain only so a later terminal revocation can close their key history.
|
||||
if (!parentActive && (!manifest->revoked() || !sle))
|
||||
return tefBAD_AUTH;
|
||||
|
||||
bool signingKeyCollision = false;
|
||||
if (manifest->signingKey)
|
||||
{
|
||||
for (auto const& otherMaster : members)
|
||||
{
|
||||
if (otherMaster == manifest->masterKey)
|
||||
continue;
|
||||
|
||||
auto other = view().peek(keylet::UNLReportMember(otherMaster));
|
||||
if (!other || !other->isFieldPresent(sfSigningPubKey))
|
||||
continue;
|
||||
|
||||
auto const otherSigning = other->getFieldVL(sfSigningPubKey);
|
||||
if (PublicKey{makeSlice(otherSigning)} != *manifest->signingKey)
|
||||
continue;
|
||||
|
||||
auto const flags =
|
||||
other->getFlags() | lsfUNLReportMemberSigningKeyCollisionFreeze;
|
||||
other->setFieldU32(sfFlags, flags);
|
||||
view().update(other);
|
||||
signingKeyCollision = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto const collisionFlag =
|
||||
signingKeyCollision ? lsfUNLReportMemberSigningKeyCollisionFreeze : 0u;
|
||||
|
||||
if (!sle)
|
||||
{
|
||||
sle = std::make_shared<SLE>(memberKey);
|
||||
writeUNLReportMember(*sle, *manifest, collisionFlag);
|
||||
view().insert(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const storedSequence = sle->getFieldU32(sfSequence);
|
||||
auto const storedBinding = sle->getFieldH256(sfDigest);
|
||||
auto const incomingBinding = manifest->bindingID();
|
||||
|
||||
if (Manifest::revoked(storedSequence) ||
|
||||
manifest->sequence < storedSequence)
|
||||
return tefPAST_SEQ;
|
||||
|
||||
if (manifest->sequence == storedSequence)
|
||||
{
|
||||
if (incomingBinding == storedBinding)
|
||||
{
|
||||
if (signingKeyCollision &&
|
||||
!(sle->getFlags() &
|
||||
lsfUNLReportMemberSigningKeyCollisionFreeze))
|
||||
{
|
||||
sle->setFieldU32(
|
||||
sfFlags,
|
||||
sle->getFlags() |
|
||||
lsfUNLReportMemberSigningKeyCollisionFreeze);
|
||||
view().update(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
return tefALREADY;
|
||||
}
|
||||
|
||||
auto const flags = sle->getFlags() |
|
||||
lsfUNLReportMemberEquivocationFreeze | collisionFlag;
|
||||
// The record remains frozen regardless of which evidence is retained.
|
||||
// Keeping the smaller binding makes the final SLE application-order
|
||||
// independent without treating either binding as valid authority.
|
||||
if (incomingBinding < storedBinding)
|
||||
writeUNLReportMember(*sle, *manifest, flags);
|
||||
else
|
||||
sle->setFieldU32(sfFlags, flags);
|
||||
view().update(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
writeUNLReportMember(*sle, *manifest, collisionFlag);
|
||||
view().update(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
Change::applyExportSignatures()
|
||||
{
|
||||
|
||||
@@ -77,9 +77,6 @@ private:
|
||||
TER
|
||||
applyUNLReport();
|
||||
|
||||
TER
|
||||
applyUNLReportMember();
|
||||
|
||||
TER
|
||||
applyConsensusEntropy();
|
||||
|
||||
@@ -92,7 +89,6 @@ using SetFee = Change;
|
||||
using UNLModify = Change;
|
||||
using EmitFailure = Change;
|
||||
using UNLReport = Change;
|
||||
using UNLReportMember = Change;
|
||||
using ConsensusEntropy = Change;
|
||||
using ExportSignatures = Change;
|
||||
|
||||
|
||||
@@ -602,7 +602,6 @@ LedgerEntryTypesMatch::visitEntry(
|
||||
case ltIMPORT_VLSEQ:
|
||||
case ltUNL_REPORT:
|
||||
case ltCONSENSUS_ENTROPY:
|
||||
case ltUNL_REPORT_MEMBER:
|
||||
case ltSHADOW_TICKET:
|
||||
case ltAMM:
|
||||
case ltBRIDGE:
|
||||
|
||||
@@ -107,13 +107,6 @@ public:
|
||||
|
||||
OpenView(OpenView&&) = default;
|
||||
|
||||
/** The immutable ledger this view was opened on. */
|
||||
ReadView const&
|
||||
parent() const noexcept
|
||||
{
|
||||
return *base_;
|
||||
}
|
||||
|
||||
/** Construct a shallow copy.
|
||||
|
||||
Effects:
|
||||
|
||||
Reference in New Issue
Block a user