Warn operators about upcoming unknown amendments:

* When an unknown amendment reaches majority, log an error-level
  message, and return a `warnings` array on all successful
  admin-level RPC calls to `server_info` and `server_state` with
  a message describing the problem, and the expected deadline.
* In addition to the `amendment_blocked` flag returned by
  `server_info` and `server_state`, return a warning with a more
  verbose description when the server is amendment blocked.
* Check on every flag ledger to see if the amendment(s) lose majority.
  Logs again if they don't, resumes normal operations if they did.

The intention is to give operators earlier warning that their
instances are in danger of being amendment blocked, which will
hopefully motivate them to update ahead of time.
This commit is contained in:
Edward Hennis
2020-01-09 19:04:20 -05:00
committed by Manoj doshi
parent 4315913a5d
commit 5ff23f8f31
11 changed files with 418 additions and 50 deletions

View File

@@ -20,6 +20,7 @@
#include <test/jtx.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/jss.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <test/jtx/WSClient.h>
@@ -39,7 +40,9 @@ class AmendmentBlocked_test : public beast::unit_test::suite
auto const USD = gw["USD"];
auto const alice = Account {"alice"};
auto const bob = Account {"bob"};
Account const ali {"ali", KeyType::secp256k1};
env.fund (XRP(10000), alice, bob, gw);
env.memoize(ali);
env.trust (USD(600), alice);
env.trust (USD(700), bob);
env(pay (gw, alice, USD(70)));
@@ -52,14 +55,17 @@ class AmendmentBlocked_test : public beast::unit_test::suite
// ledger_accept
auto jr = env.rpc ("ledger_accept") [jss::result];
BEAST_EXPECT (jr[jss::ledger_current_index] == current->seq ()+1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// ledger_current
jr = env.rpc ("ledger_current") [jss::result];
BEAST_EXPECT (jr[jss::ledger_current_index] == current->seq ()+1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// owner_info
jr = env.rpc ("owner_info", alice.human()) [jss::result];
BEAST_EXPECT (jr.isMember (jss::accepted) && jr.isMember (jss::current));
BEAST_EXPECT(!jr.isMember(jss::warnings));
// path_find
Json::Value pf_req;
@@ -72,6 +78,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
BEAST_EXPECT (jr.isMember (jss::alternatives) &&
jr[jss::alternatives].isArray() &&
jr[jss::alternatives].size () == 1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit
auto jt = env.jt (noop (alice));
@@ -80,10 +87,10 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr = env.rpc ("submit", strHex (s.slice ())) [jss::result];
BEAST_EXPECT (jr.isMember (jss::engine_result) &&
jr[jss::engine_result] == "tesSUCCESS");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit_multisigned
env(signers(bob, 1, {{alice, 1}}), sig (bob));
Account const ali {"ali", KeyType::secp256k1};
env(regkey (alice, ali));
env.close();
@@ -101,6 +108,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
sign_for[jss::secret] = ali.name();
jr = env.rpc("json", "sign_for", to_string(sign_for)) [jss::result];
BEAST_EXPECT(jr[jss::status] == "success");
BEAST_EXPECT(!jr.isMember(jss::warnings));
Json::Value ms_req;
ms_req[jss::tx_json] = jr[jss::tx_json];
@@ -108,6 +116,74 @@ class AmendmentBlocked_test : public beast::unit_test::suite
[jss::result];
BEAST_EXPECT (jr.isMember (jss::engine_result) &&
jr[jss::engine_result] == "tesSUCCESS");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// set up an amendment warning. Nothing changes
env.app ().getOPs ().setAmendmentWarned ();
current = env.current ();
// ledger_accept
jr = env.rpc ("ledger_accept") [jss::result];
BEAST_EXPECT (jr[jss::ledger_current_index] == current->seq ()+1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// ledger_current
jr = env.rpc ("ledger_current") [jss::result];
BEAST_EXPECT (jr[jss::ledger_current_index] == current->seq ()+1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// owner_info
jr = env.rpc ("owner_info", alice.human()) [jss::result];
BEAST_EXPECT (jr.isMember (jss::accepted) && jr.isMember (jss::current));
BEAST_EXPECT(!jr.isMember(jss::warnings));
// path_find
pf_req[jss::subcommand] = "create";
pf_req[jss::source_account] = alice.human();
pf_req[jss::destination_account] = bob.human();
pf_req[jss::destination_amount] =
bob["USD"](20).value ().getJson (JsonOptions::none);
jr = wsc->invoke("path_find", pf_req) [jss::result];
BEAST_EXPECT (jr.isMember (jss::alternatives) &&
jr[jss::alternatives].isArray() &&
jr[jss::alternatives].size () == 1);
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit
jt = env.jt (noop (alice));
s.erase();
jt.stx->add (s);
jr = env.rpc ("submit", strHex (s.slice ())) [jss::result];
BEAST_EXPECT (jr.isMember (jss::engine_result) &&
jr[jss::engine_result] == "tesSUCCESS");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit_multisigned
env(signers(bob, 1, {{alice, 1}}), sig (bob));
env(regkey (alice, ali));
env.close();
set_tx[jss::Account] = bob.human();
set_tx[jss::TransactionType] = jss::AccountSet;
set_tx[jss::Fee] =
(8 * env.current()->fees().base).jsonClipped();
set_tx[jss::Sequence] = env.seq(bob);
set_tx[jss::SigningPubKey] = "";
sign_for[jss::tx_json] = set_tx;
sign_for[jss::account] = alice.human();
sign_for[jss::secret] = ali.name();
jr = env.rpc("json", "sign_for", to_string(sign_for)) [jss::result];
BEAST_EXPECT(jr[jss::status] == "success");
BEAST_EXPECT(!jr.isMember(jss::warnings));
ms_req[jss::tx_json] = jr[jss::tx_json];
jr = env.rpc("json", "submit_multisigned", to_string(ms_req))
[jss::result];
BEAST_EXPECT (jr.isMember (jss::engine_result) &&
jr[jss::engine_result] == "tesSUCCESS");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// make the network amendment blocked...now all the same
// requests should fail
@@ -120,6 +196,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(jr[jss::status] == "error");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// ledger_current
jr = env.rpc ("ledger_current") [jss::result];
@@ -127,6 +204,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(jr[jss::status] == "error");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// owner_info
jr = env.rpc ("owner_info", alice.human()) [jss::result];
@@ -134,6 +212,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(jr[jss::status] == "error");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// path_find
jr = wsc->invoke("path_find", pf_req) [jss::result];
@@ -141,6 +220,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(jr[jss::status] == "error");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit
jr = env.rpc("submit", strHex(s.slice())) [jss::result];
@@ -148,6 +228,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(jr[jss::status] == "error");
BEAST_EXPECT(!jr.isMember(jss::warnings));
// submit_multisigned
set_tx[jss::Sequence] = env.seq(bob);
@@ -160,6 +241,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite
BEAST_EXPECT(
jr.isMember (jss::error) &&
jr[jss::error] == "amendmentBlocked");
BEAST_EXPECT(!jr.isMember(jss::warnings));
}
public: