Skip to content

Commit

Permalink
Include validator manifests in published list:
Browse files Browse the repository at this point in the history
Manifests of validators newly added to a published validator list are
not reliably propagated to network nodes.
This solves the problem by allowing a published validator list to
include the manifest.

RIPD-1559
  • Loading branch information
wilsonianb committed Nov 22, 2017
1 parent cafe18c commit bce9bca
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 28 deletions.
3 changes: 2 additions & 1 deletion src/ripple/app/misc/ValidatorList.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ enum class ListDisposition
"expiration", and @c "validators" field. @c "expiration" contains the
Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when
the list expires. @c "validators" contains an array of objects with a
@c "validation_public_key" field.
@c "validation_public_key" and optional @c "manifest" field.
@c "validation_public_key" should be the hex-encoded master public key.
@c "manifest" should be the base64-encoded validator manifest.
@li @c "manifest": Base64-encoded serialization of a manifest containing the
publisher's master and signing public keys.
Expand Down
3 changes: 2 additions & 1 deletion src/ripple/app/misc/ValidatorSite.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ namespace ripple {
"expiration", and @c "validators" field. @c "expiration" contains the
Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when
the list expires. @c "validators" contains an array of objects with a
@c "validation_public_key" field.
@c "validation_public_key" and optional @c "manifest" field.
@c "validation_public_key" should be the hex-encoded master public key.
@c "manifest" should be the base64-encoded validator manifest.
@li @c "manifest": Base64-encoded serialization of a manifest containing the
publisher's master and signing public keys.
Expand Down
26 changes: 26 additions & 0 deletions src/ripple/app/misc/impl/ValidatorList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ ValidatorList::applyList (
std::vector<PublicKey> oldList = publisherList;
publisherList.clear ();
publisherList.reserve (newList.size ());
std::vector<std::string> manifests;
for (auto const& val : newList)
{
if (val.isObject () &&
Expand All @@ -210,6 +211,9 @@ ValidatorList::applyList (
publisherList.push_back (
PublicKey(Slice{ ret.first.data (), ret.first.size() }));
}

if (val.isMember ("manifest") && val["manifest"].isString ())
manifests.push_back(val["manifest"].asString ());
}
}

Expand Down Expand Up @@ -254,6 +258,28 @@ ValidatorList::applyList (
"No validator keys included in valid list";
}

for (auto const& valManifest : manifests)
{
auto m = Manifest::make_Manifest (
beast::detail::base64_decode(valManifest));

if (! m || ! keyListings_.count (m->masterKey))
{
JLOG (j_.warn()) <<
"List for " << strHex(pubKey) <<
" contained untrusted validator manifest";
continue;
}

auto const result = validatorManifests_.applyManifest (std::move(*m));
if (result == ManifestDisposition::invalid)
{
JLOG (j_.warn()) <<
"List for " << strHex(pubKey) <<
" contained invalid validator manifest";
}
}

return ListDisposition::accepted;
}

Expand Down
70 changes: 53 additions & 17 deletions src/test/app/ValidatorList_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ namespace test {
class ValidatorList_test : public beast::unit_test::suite
{
private:
struct Validator
{
PublicKey masterPublic;
PublicKey signingPublic;
std::string manifest;
};

static
PublicKey
randomNode ()
Expand All @@ -49,6 +56,7 @@ class ValidatorList_test : public beast::unit_test::suite
return derivePublicKey (KeyType::ed25519, randomSecretKey());
}

static
std::string
makeManifestString (
PublicKey const& pk,
Expand All @@ -72,9 +80,22 @@ class ValidatorList_test : public beast::unit_test::suite
return std::string(static_cast<char const*> (s.data()), s.size());
}

static
Validator
randomValidator ()
{
auto const secret = randomSecretKey();
auto const masterPublic =
derivePublicKey(KeyType::ed25519, secret);
auto const signingKeys = randomKeyPair(KeyType::secp256k1);
return { masterPublic, signingKeys.first,
beast::detail::base64_encode(makeManifestString (
masterPublic, secret, signingKeys.first, signingKeys.second, 1)) };
}

std::string
makeList (
std::vector <PublicKey> const& validators,
std::vector <Validator> const& validators,
std::size_t sequence,
std::size_t expiration)
{
Expand All @@ -85,7 +106,8 @@ class ValidatorList_test : public beast::unit_test::suite

for (auto const& val : validators)
{
data += "{\"validation_public_key\":\"" + strHex(val) + "\"},";
data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) +
"\",\"manifest\":\"" + val.manifest + "\"},";
}

data.pop_back();
Expand Down Expand Up @@ -355,15 +377,15 @@ class ValidatorList_test : public beast::unit_test::suite
emptyLocalKey, emptyCfgKeys, cfgKeys1));

auto constexpr listSize = 20;
std::vector<PublicKey> list1;
std::vector<Validator> list1;
list1.reserve (listSize);
while (list1.size () < listSize)
list1.push_back (randomNode());
list1.push_back (randomValidator());

std::vector<PublicKey> list2;
std::vector<Validator> list2;
list2.reserve (listSize);
while (list2.size () < listSize)
list2.push_back (randomNode());
list2.push_back (randomValidator());

// do not apply expired list
auto const version = 1;
Expand All @@ -387,7 +409,10 @@ class ValidatorList_test : public beast::unit_test::suite
manifest1, blob1, sig1, version));

for (auto const& val : list1)
BEAST_EXPECT(trustedKeys->listed (val));
{
BEAST_EXPECT(trustedKeys->listed (val.masterPublic));
BEAST_EXPECT(trustedKeys->listed (val.signingPublic));
}

// do not use list from untrusted publisher
auto const untrustedManifest = beast::detail::base64_encode(
Expand Down Expand Up @@ -415,10 +440,16 @@ class ValidatorList_test : public beast::unit_test::suite
manifest1, blob2, sig2, version));

for (auto const& val : list1)
BEAST_EXPECT(! trustedKeys->listed (val));
{
BEAST_EXPECT(! trustedKeys->listed (val.masterPublic));
BEAST_EXPECT(! trustedKeys->listed (val.signingPublic));
}

for (auto const& val : list2)
BEAST_EXPECT(trustedKeys->listed (val));
{
BEAST_EXPECT(trustedKeys->listed (val.masterPublic));
BEAST_EXPECT(trustedKeys->listed (val.signingPublic));
}

// do not re-apply lists with past or current sequence numbers
BEAST_EXPECT(ListDisposition::stale ==
Expand Down Expand Up @@ -471,7 +502,10 @@ class ValidatorList_test : public beast::unit_test::suite

BEAST_EXPECT(! trustedKeys->trustedPublisher(publisherPublic));
for (auto const& val : list1)
BEAST_EXPECT(! trustedKeys->listed (val));
{
BEAST_EXPECT(! trustedKeys->listed (val.masterPublic));
BEAST_EXPECT(! trustedKeys->listed (val.signingPublic));
}
}

void
Expand Down Expand Up @@ -723,8 +757,8 @@ class ValidatorList_test : public beast::unit_test::suite
BEAST_EXPECT(trustedKeys->load (
emptyLocalKey, emptyCfgKeys, cfgKeys));

std::vector<PublicKey> list ({randomNode()});
hash_set<PublicKey> activeValidators ({ list[0] });
std::vector<Validator> list ({randomValidator()});
hash_set<PublicKey> activeValidators ({ list[0].masterPublic });

// do not apply expired list
auto const version = 1;
Expand All @@ -740,11 +774,13 @@ class ValidatorList_test : public beast::unit_test::suite
manifest, blob, sig, version));

trustedKeys->onConsensusStart (activeValidators);
BEAST_EXPECT(trustedKeys->trusted (list[0]));
BEAST_EXPECT(trustedKeys->trusted (list[0].masterPublic));
BEAST_EXPECT(trustedKeys->trusted (list[0].signingPublic));

env.timeKeeper().set(expiration);
trustedKeys->onConsensusStart (activeValidators);
BEAST_EXPECT(! trustedKeys->trusted (list[0]));
BEAST_EXPECT(! trustedKeys->trusted (list[0].masterPublic));
BEAST_EXPECT(! trustedKeys->trusted (list[0].signingPublic));
}
{
// Test 1-9 configured validators
Expand Down Expand Up @@ -814,13 +850,13 @@ class ValidatorList_test : public beast::unit_test::suite

hash_set<PublicKey> activeValidators;

std::vector<PublicKey> valKeys;
std::vector<Validator> valKeys;
valKeys.reserve(n);

while (valKeys.size () != n)
{
valKeys.push_back (randomNode());
activeValidators.emplace (valKeys.back());
valKeys.push_back (randomValidator());
activeValidators.emplace (valKeys.back().masterPublic);
}

auto addPublishedList = [this, &env, &trustedKeys, &valKeys]()
Expand Down
48 changes: 39 additions & 9 deletions src/test/app/ValidatorSite_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
namespace ripple {
namespace test {

struct Validator
{
PublicKey masterPublic;
PublicKey signingPublic;
std::string manifest;
};

class http_sync_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
Expand All @@ -57,7 +64,7 @@ class http_sync_server
int sequence,
std::size_t expiration,
int version,
std::vector <PublicKey> const& validators)
std::vector <Validator> const& validators)
: sock_(ios)
, acceptor_(ios)
{
Expand All @@ -68,7 +75,8 @@ class http_sync_server

for (auto const& val : validators)
{
data += "{\"validation_public_key\":\"" + strHex (val) + "\"},";
data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) +
"\",\"manifest\":\"" + val.manifest + "\"},";
}
data.pop_back();
data += "]}";
Expand Down Expand Up @@ -202,6 +210,7 @@ class ValidatorSite_test : public beast::unit_test::suite
return derivePublicKey (KeyType::secp256k1, randomSecretKey());
}

static
std::string
makeManifestString (
PublicKey const& pk,
Expand All @@ -226,6 +235,18 @@ class ValidatorSite_test : public beast::unit_test::suite
static_cast<char const*> (s.data()), s.size()));
}

static
Validator
randomValidator ()
{
auto const secret = randomSecretKey();
auto const masterPublic =
derivePublicKey(KeyType::ed25519, secret);
auto const signingKeys = randomKeyPair(KeyType::secp256k1);
return { masterPublic, signingKeys.first, makeManifestString (
masterPublic, secret, signingKeys.first, signingKeys.second, 1) };
}

void
testConfigLoad ()
{
Expand Down Expand Up @@ -306,15 +327,15 @@ class ValidatorSite_test : public beast::unit_test::suite
emptyLocalKey, emptyCfgKeys, cfgPublishers));

auto constexpr listSize = 20;
std::vector<PublicKey> list1;
std::vector<Validator> list1;
list1.reserve (listSize);
while (list1.size () < listSize)
list1.push_back (randomNode());
list1.push_back (randomValidator());

std::vector<PublicKey> list2;
std::vector<Validator> list2;
list2.reserve (listSize);
while (list2.size () < listSize)
list2.push_back (randomNode());
list2.push_back (randomValidator());

std::uint16_t constexpr port1 = 7475;
std::uint16_t constexpr port2 = 7476;
Expand Down Expand Up @@ -351,7 +372,10 @@ class ValidatorSite_test : public beast::unit_test::suite
sites->join();

for (auto const& val : list1)
BEAST_EXPECT(trustedKeys.listed (val));
{
BEAST_EXPECT(trustedKeys.listed (val.masterPublic));
BEAST_EXPECT(trustedKeys.listed (val.signingPublic));
}
}
{
// fetch multiple sites
Expand All @@ -367,10 +391,16 @@ class ValidatorSite_test : public beast::unit_test::suite
sites->join();

for (auto const& val : list1)
BEAST_EXPECT(trustedKeys.listed (val));
{
BEAST_EXPECT(trustedKeys.listed (val.masterPublic));
BEAST_EXPECT(trustedKeys.listed (val.signingPublic));
}

for (auto const& val : list2)
BEAST_EXPECT(trustedKeys.listed (val));
{
BEAST_EXPECT(trustedKeys.listed (val.masterPublic));
BEAST_EXPECT(trustedKeys.listed (val.signingPublic));
}
}
}

Expand Down

0 comments on commit bce9bca

Please sign in to comment.