diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 09117afff9..c9dd1ce2fa 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -406,6 +406,8 @@ public: void pubProposedTransaction ( std::shared_ptr const& lpCurrent, std::shared_ptr const& stTxn, TER terResult) override; + void pubValidation ( + STValidation::ref val) override; //-------------------------------------------------------------------------- // @@ -492,7 +494,6 @@ private: bool isAccepted); void pubServer (); - void pubValidation (STValidation::ref val); std::string getHostId (bool forAdmin); @@ -1623,6 +1624,8 @@ void NetworkOPsImp::pubValidation (STValidation::ref val) jvObj [jss::ledger_hash] = to_string (val->getLedgerHash ()); jvObj [jss::signature] = strHex (val->getSignature ()); jvObj [jss::full] = val->isFull(); + jvObj [jss::flags] = val->getFlags(); + jvObj [jss::signing_time] = *(*val)[~sfSigningTime]; auto const seq = *(*val)[~sfLedgerSequence]; @@ -1636,6 +1639,21 @@ void NetworkOPsImp::pubValidation (STValidation::ref val) jvObj [jss::amendments].append (to_string (amendment)); } + if (auto const closeTime = (*val)[~sfCloseTime]) + jvObj [jss::close_time] = *closeTime; + + if (auto const loadFee = (*val)[~sfLoadFee]) + jvObj [jss::load_fee] = *loadFee; + + if (auto const baseFee = (*val)[~sfBaseFee]) + jvObj [jss::base_fee] = static_cast (*baseFee); + + if (auto const reserveBase = (*val)[~sfReserveBase]) + jvObj [jss::reserve_base] = *reserveBase; + + if (auto const reserveInc = (*val)[~sfReserveIncrement]) + jvObj [jss::reserve_inc] = *reserveInc; + for (auto i = mSubValidations.begin (); i != mSubValidations.end (); ) { if (auto p = i->second.lock()) diff --git a/src/ripple/app/misc/NetworkOPs.h b/src/ripple/app/misc/NetworkOPs.h index c6da2f2112..d2bed20d8c 100644 --- a/src/ripple/app/misc/NetworkOPs.h +++ b/src/ripple/app/misc/NetworkOPs.h @@ -234,6 +234,7 @@ public: virtual void pubProposedTransaction ( std::shared_ptr const& lpCurrent, std::shared_ptr const& stTxn, TER terResult) = 0; + virtual void pubValidation (STValidation::ref val) = 0; }; //------------------------------------------------------------------------------ diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 3448f10fc0..ea7982a086 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -963,6 +963,11 @@ OverlayImpl::send (protocol::TMValidation& m) if (! m.has_hops() || p->hopsAware()) p->send(sm); }); + + SerialIter sit (m.validation().data(), m.validation().size()); + auto val = std::make_shared < + STValidation> (std::ref (sit), false); + app_.getOPs().pubValidation (val); } void diff --git a/src/ripple/protocol/JsonFields.h b/src/ripple/protocol/JsonFields.h index 9fb00eba76..00942c00ce 100644 --- a/src/ripple/protocol/JsonFields.h +++ b/src/ripple/protocol/JsonFields.h @@ -167,7 +167,8 @@ JSS ( fee_ref ); // out: NetworkOPs JSS ( fetch_pack ); // out: NetworkOPs JSS ( first ); // out: rpc/Version JSS ( fix_txns ); // in: LedgerCleaner -JSS ( flags ); // out: paths/Node, AccountOffers +JSS ( flags ); // out: paths/Node, AccountOffers, + // NetworkOPs JSS ( forward ); // in: AccountTx JSS ( freeze ); // out: AccountLines JSS ( freeze_peer ); // out: AccountLines @@ -237,7 +238,7 @@ JSS ( load_factor_fee_queue ); // out: NetworkOPs JSS ( load_factor_fee_reference ); // out: NetworkOPs JSS ( load_factor_local ); // out: NetworkOPs JSS ( load_factor_net ); // out: NetworkOPs -JSS ( load_fee ); // out: LoadFeeTrackImp +JSS ( load_fee ); // out: LoadFeeTrackImp, NetworkOPs JSS ( local ); // out: resource/Logic.h JSS ( local_txs ); // out: GetCounts JSS ( majority ); // out: RPC feature @@ -347,6 +348,7 @@ JSS ( server_status ); // out: NetworkOPs JSS ( severity ); // in: LogLevel JSS ( signature ); // out: NetworkOPs JSS ( signing_key ); // out: NetworkOPs +JSS ( signing_time ); // out: NetworkOPs JSS ( signer_list ); // in: AccountObjects JSS ( signer_lists ); // in/out: AccountInfo JSS ( snapshot ); // in: Subscribe diff --git a/src/ripple/rpc/tests/Subscribe.test.cpp b/src/ripple/rpc/tests/Subscribe.test.cpp index af12b1920c..0a21312d30 100644 --- a/src/ripple/rpc/tests/Subscribe.test.cpp +++ b/src/ripple/rpc/tests/Subscribe.test.cpp @@ -249,10 +249,42 @@ public: expect(jv[jss::status] == "success"); } + static + std::unique_ptr + makeValidatorConfig( + std::string const& valPrivateKey, std::string const& valPublicKey) + { + auto p = std::make_unique(); + setupConfigForUnitTests(*p); + + // If the config has valid validation keys then we run as a validator. + auto const sk = parseBase58( + TOKEN_NODE_PRIVATE, + valPrivateKey); + if (!sk) + Throw ("Invalid validation private key"); + p->VALIDATION_PRIV = *sk; + + auto const pk = parseBase58( + TOKEN_NODE_PUBLIC, + valPublicKey); + if (!pk) + Throw ("Invalid validation public key"); + p->VALIDATION_PUB = *pk; + + return p; + } + void testValidations() { using namespace jtx; - Env env(*this); + + // Public key must be derived from the private key + const std::string valPrivateKey = + "paEdUCVVCNnv4aYBepid9Xh3NaAr9xWRw2vh351piFJrxQwvExd"; + const std::string valPublicKey = + "n9MvFGjgv1kYkm7bLbb2QUwSqgzrQkYMYHXtrzN8W28Jfp2mVihq"; + Env env(*this, makeValidatorConfig(valPrivateKey, valPublicKey)); auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -264,6 +296,29 @@ public: expect(jv[jss::status] == "success"); } + { + // Accept a ledger + env.close(); + + // Check stream update + expect(wsc->findMsg(5s, + [&](auto const& jv) + { + return jv[jss::type] == "validationReceived" && + jv[jss::validation_public_key] == valPublicKey && + jv[jss::ledger_hash] == + to_string(env.closed()->info().hash) && + jv[jss::ledger_index] == + to_string(env.closed()->info().seq) && + jv[jss::flags] == + (vfFullyCanonicalSig | STValidation::kFullFlag) && + jv[jss::full] == true && + jv[jss::load_fee] == 256000 && + jv[jss::signature] && + jv[jss::signing_time]; + })); + } + // RPC unsubscribe auto jv = wsc->invoke("unsubscribe", stream); expect(jv[jss::status] == "success");