mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Harden validations:
This commit introduces the "HardenedValidations" amendment which, if enabled, allows validators to include additional information in their validations that can increase the robustness of consensus. Specifically, the commit introduces a new optional field that can be set in validation messages can be used to attest to the hash of the latest ledger that a validator considers to be fully validated. Additionally, the commit leverages the previously introduced "cookie" field to improve the robustness of the network by making it possible for servers to automatically detect accidental misconfiguration which results in two or more validators using the same validation key.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
@@ -372,7 +373,7 @@ public:
|
||||
auto const roundTime = weekTime(week);
|
||||
|
||||
// Build validations
|
||||
std::vector<STValidation::pointer> validations;
|
||||
std::vector<std::shared_ptr<STValidation>> validations;
|
||||
validations.reserve(validators.size());
|
||||
|
||||
int i = 0;
|
||||
@@ -391,16 +392,15 @@ public:
|
||||
}
|
||||
|
||||
auto v = std::make_shared<STValidation>(
|
||||
uint256(),
|
||||
i,
|
||||
uint256(),
|
||||
roundTime,
|
||||
ripple::NetClock::time_point{},
|
||||
val.first,
|
||||
val.second,
|
||||
calcNodeID(val.first),
|
||||
true,
|
||||
STValidation::FeeSettings{},
|
||||
field);
|
||||
[&field](STValidation& v) {
|
||||
if (!field.empty())
|
||||
v.setFieldV256(
|
||||
sfAmendments, STVector256(sfAmendments, field));
|
||||
});
|
||||
|
||||
validations.emplace_back(v);
|
||||
}
|
||||
|
||||
@@ -35,16 +35,11 @@ class RCLValidations_test : public beast::unit_test::suite
|
||||
testcase("Change validation trusted status");
|
||||
auto keys = randomKeyPair(KeyType::secp256k1);
|
||||
auto v = std::make_shared<STValidation>(
|
||||
uint256(),
|
||||
1,
|
||||
uint256(),
|
||||
NetClock::time_point(),
|
||||
ripple::NetClock::time_point{},
|
||||
keys.first,
|
||||
keys.second,
|
||||
calcNodeID(keys.first),
|
||||
true,
|
||||
STValidation::FeeSettings{},
|
||||
std::vector<uint256>{});
|
||||
[&](STValidation& v) {});
|
||||
|
||||
BEAST_EXPECT(v->isTrusted());
|
||||
v->setUntrusted();
|
||||
|
||||
@@ -54,7 +54,7 @@ class Validations_test : public beast::unit_test::suite
|
||||
clock_type const& c_;
|
||||
PeerID nodeID_;
|
||||
bool trusted_ = true;
|
||||
std::size_t signIdx_ = 1;
|
||||
std::size_t signIdx_{1};
|
||||
boost::optional<std::uint32_t> loadFee_;
|
||||
|
||||
public:
|
||||
@@ -394,7 +394,7 @@ class Validations_test : public beast::unit_test::suite
|
||||
|
||||
// If we advance far enough for AB to expire, we can fully
|
||||
// validate or partially validate that sequence number again
|
||||
BEAST_EXPECT(ValStatus::badSeq == process(ledgerAZ));
|
||||
BEAST_EXPECT(ValStatus::conflicting == process(ledgerAZ));
|
||||
harness.clock().advance(
|
||||
harness.parms().validationSET_EXPIRES + 1ms);
|
||||
BEAST_EXPECT(ValStatus::current == process(ledgerAZ));
|
||||
@@ -683,8 +683,10 @@ class Validations_test : public beast::unit_test::suite
|
||||
trustedValidations[val.ledgerID()].emplace_back(val);
|
||||
}
|
||||
// d now thinks ledger 1, but cannot re-issue a previously used seq
|
||||
// and attempting it should generate a conflict.
|
||||
{
|
||||
BEAST_EXPECT(ValStatus::badSeq == harness.add(d.partial(ledgerA)));
|
||||
BEAST_EXPECT(
|
||||
ValStatus::conflicting == harness.add(d.partial(ledgerA)));
|
||||
}
|
||||
// e only issues partials
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ class Validation
|
||||
bool trusted_ = false;
|
||||
bool full_ = false;
|
||||
boost::optional<std::uint32_t> loadFee_;
|
||||
std::uint64_t cookie_{0};
|
||||
|
||||
public:
|
||||
using NodeKey = PeerKey;
|
||||
@@ -68,7 +69,8 @@ public:
|
||||
PeerKey key,
|
||||
PeerID nodeID,
|
||||
bool full,
|
||||
boost::optional<std::uint32_t> loadFee = boost::none)
|
||||
boost::optional<std::uint32_t> loadFee = boost::none,
|
||||
std::uint64_t cookie = 0)
|
||||
: ledgerID_{id}
|
||||
, seq_{seq}
|
||||
, signTime_{sign}
|
||||
@@ -77,6 +79,7 @@ public:
|
||||
, nodeID_{nodeID}
|
||||
, full_{full}
|
||||
, loadFee_{loadFee}
|
||||
, cookie_{cookie}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,6 +131,12 @@ public:
|
||||
return full_;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
cookie() const
|
||||
{
|
||||
return cookie_;
|
||||
}
|
||||
|
||||
boost::optional<std::uint32_t>
|
||||
loadFee() const
|
||||
{
|
||||
@@ -191,4 +200,5 @@ public:
|
||||
} // namespace csf
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -370,6 +370,7 @@ public:
|
||||
|
||||
// Check stream update
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
return jv[jss::type] == "validationReceived" &&
|
||||
jv[jss::validation_public_key].asString() == valPublicKey &&
|
||||
@@ -378,7 +379,7 @@ public:
|
||||
jv[jss::ledger_index] ==
|
||||
std::to_string(env.closed()->info().seq) &&
|
||||
jv[jss::flags] ==
|
||||
(vfFullyCanonicalSig | STValidation::kFullFlag) &&
|
||||
(vfFullyCanonicalSig | vfFullValidation) &&
|
||||
jv[jss::full] == true && !jv.isMember(jss::load_fee) &&
|
||||
jv[jss::signature] && jv[jss::signing_time];
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user