New generic Ledger RPC handler.

This commit is contained in:
Tom Ritchford
2014-12-03 18:15:28 -05:00
committed by Vinnie Falco
parent 1cbcc7be21
commit 167f4666e2
8 changed files with 285 additions and 122 deletions

View File

@@ -23,6 +23,7 @@
#include <ripple/server/Role.h>
#include <ripple/core/Config.h>
#include <ripple/net/InfoSub.h>
#include <ripple/rpc/Yield.h>
namespace ripple {
@@ -34,16 +35,20 @@ public:
explicit RPCHandler (
NetworkOPs& netOps, InfoSub::pointer infoSub = nullptr);
using Yield = RPC::Yield;
Json::Value doCommand (
Json::Value const& request,
Role role,
Resource::Charge& loadType);
Resource::Charge& loadType,
Yield yield = {});
Json::Value doRpcCommand (
std::string const& command,
Json::Value const& params,
Role role,
Resource::Charge& loadType);
Resource::Charge& loadType,
Yield yield = {});
private:
NetworkOPs& netOps_;

View File

@@ -38,7 +38,6 @@ Json::Value doFeature (RPC::Context&);
Json::Value doFetchInfo (RPC::Context&);
Json::Value doGetCounts (RPC::Context&);
Json::Value doInternal (RPC::Context&);
Json::Value doLedger (RPC::Context&);
Json::Value doLedgerAccept (RPC::Context&);
Json::Value doLedgerCleaner (RPC::Context&);
Json::Value doLedgerClosed (RPC::Context&);

View File

@@ -17,77 +17,73 @@
*/
//==============================================================================
#include <ripple/app/ledger/LedgerToJson.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/rpc/ErrorCodes.h>
#include <ripple/rpc/handlers/Ledger.h>
#include <ripple/rpc/impl/JsonObject.h>
#include <ripple/server/Role.h>
namespace ripple {
namespace RPC {
// ledger [id|index|current|closed] [full]
// {
// ledger: 'current' | 'closed' | <uint256> | <number>, // optional
// full: true | false // optional, defaults to false.
// }
Json::Value doLedger (RPC::Context& context)
LedgerHandler::LedgerHandler (Context& context) : context_ (context)
{
if (!context.params.isMember ("ledger")
&& !context.params.isMember ("ledger_hash")
&& !context.params.isMember ("ledger_index"))
}
Status LedgerHandler::check (Json::Value& error)
{
bool needsLedger = context_.params.isMember (jss::ledger) ||
context_.params.isMember (jss::ledger_hash) ||
context_.params.isMember (jss::ledger_index);
if (!needsLedger)
return Status::OK;
lookupResult_ = RPC::lookupLedger (
context_.params, ledger_, context_.netOps);
if (!ledger_)
{
Json::Value ret (Json::objectValue), current (Json::objectValue),
closed (Json::objectValue);
getApp().getLedgerMaster ().getCurrentLedger ()->addJson (current, 0);
getApp().getLedgerMaster ().getClosedLedger ()->addJson (closed, 0);
ret["open"] = current;
ret["closed"] = closed;
return ret;
error = lookupResult_;
auto code = error_code_i (error[jss::error_code].asInt());
return {code, {error[jss::error_message].asString()}};
}
Ledger::pointer lpLedger;
Json::Value jvResult = RPC::lookupLedger (
context.params, lpLedger, context.netOps);
if (!lpLedger)
return jvResult;
bool bFull = context.params.isMember ("full")
&& context.params["full"].asBool ();
bool bTransactions = context.params.isMember ("transactions")
&& context.params["transactions"].asBool ();
bool bAccounts = context.params.isMember ("accounts")
&& context.params["accounts"].asBool ();
bool bExpand = context.params.isMember ("expand")
&& context.params["expand"].asBool ();
int iOptions = (bFull ? LEDGER_JSON_FULL : 0)
| (bExpand ? LEDGER_JSON_EXPAND : 0)
| (bTransactions ? LEDGER_JSON_DUMP_TXRP : 0)
| (bAccounts ? LEDGER_JSON_DUMP_STATE : 0);
bool bFull = context_.params.isMember (jss::full)
&& context_.params[jss::full].asBool ();
bool bTransactions = context_.params.isMember (jss::transactions)
&& context_.params[jss::transactions].asBool ();
bool bAccounts = context_.params.isMember (jss::accounts)
&& context_.params[jss::accounts].asBool ();
bool bExpand = context_.params.isMember (jss::expand)
&& context_.params[jss::expand].asBool ();
options_ = (bFull ? LEDGER_JSON_FULL : 0)
| (bExpand ? LEDGER_JSON_EXPAND : 0)
| (bTransactions ? LEDGER_JSON_DUMP_TXRP : 0)
| (bAccounts ? LEDGER_JSON_DUMP_STATE : 0);
if (bFull || bAccounts)
{
if (context.role != Role::ADMIN)
if (context_.role != Role::ADMIN)
{
// Until some sane way to get full ledgers has been implemented,
// disallow retrieving all state nodes.
return rpcError (rpcNO_PERMISSION);
error = rpcError (rpcNO_PERMISSION);
return rpcNO_PERMISSION;
}
if (getApp().getFeeTrack().isLoadedLocal() &&
context.role != Role::ADMIN)
context_.role != Role::ADMIN)
{
WriteLog (lsDEBUG, Peer) << "Too busy to give full ledger";
return rpcError(rpcTOO_BUSY);
error = rpcError(rpcTOO_BUSY);
return rpcTOO_BUSY;
}
context.loadType = Resource::feeHighBurdenRPC;
context_.loadType = Resource::feeHighBurdenRPC;
}
lpLedger->addJson (jvResult, iOptions);
return jvResult;
std::cerr << "!!! LedgerHandler::check FIVE - true\n";
return Status::OK;
}
} // RPC
} // ripple

View File

@@ -0,0 +1,102 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLED_RIPPLE_RPC_HANDLERS_LEDGER_H
#define RIPPLED_RIPPLE_RPC_HANDLERS_LEDGER_H
#include <ripple/app/ledger/LedgerToJson.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/rpc/impl/JsonObject.h>
#include <ripple/server/Role.h>
namespace ripple {
namespace RPC {
class Object;
// ledger [id|index|current|closed] [full]
// {
// ledger: 'current' | 'closed' | <uint256> | <number>, // optional
// full: true | false // optional, defaults to false.
// }
class LedgerHandler {
public:
explicit LedgerHandler (Context&);
Status check (Json::Value& error);
template <class Object>
void writeResult (Object&);
static const char* const name()
{
return "ledger";
}
static Role role()
{
return Role::USER;
}
static Condition condition()
{
return NEEDS_NETWORK_CONNECTION;
}
private:
Context& context_;
Ledger::pointer ledger_;
Json::Value lookupResult_;
int options_;
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Implementation.
template <class Object>
void LedgerHandler::writeResult (Object& value)
{
if (ledger_)
{
RPC::copyFrom (value, lookupResult_);
addJson (*ledger_, value, options_, context_.yield);
}
else
{
auto& master = getApp().getLedgerMaster ();
auto& yield = context_.yield;
{
auto&& closed = RPC::addObject (value, jss::closed);
addJson (*master.getClosedLedger(), closed, 0, yield);
}
{
auto&& open = RPC::addObject (value, jss::open);
addJson (*master.getCurrentLedger(), open, 0, yield);
}
}
}
} // RPC
} // ripple
#endif

View File

@@ -21,6 +21,7 @@
#define RIPPLE_RPC_CONTEXT
#include <ripple/core/Config.h>
#include <ripple/rpc/Yield.h>
#include <ripple/server/ServerHandler.h>
namespace ripple {
@@ -34,6 +35,7 @@ struct Context
NetworkOPs& netOps;
InfoSub::pointer infoSub;
Role role;
RPC::Yield yield;
};
} // RPC

View File

@@ -24,11 +24,28 @@ namespace ripple {
namespace RPC {
namespace {
/** Adjust an old-style handler to be call-by-reference. */
template <typename Function>
Handler::Method<Json::Value> byRef (Function const& f)
{
return [f] (Context& context, Json::Value& result)
{
result = f (context);
return Status();
};
}
class HandlerTable {
public:
HandlerTable(std::vector<Handler> const& entries) {
HandlerTable (std::vector<Handler> const& entries) {
for (auto& entry: entries)
{
assert (table_.find(entry.name_) == table_.end());
table_[entry.name_] = entry;
}
// This is where the new-style handlers are added.
addHandler<LedgerHandler>();
}
const Handler* getHandler(std::string name) {
@@ -38,75 +55,109 @@ class HandlerTable {
private:
std::map<std::string, Handler> table_;
template <class Impl>
void addHandler()
{
assert (table_.find(Impl::name()) == table_.end());
auto valueMethod = [] (Context& context, Json::Value& object)
{
Impl handler (context);
auto status = handler.check (object);
if (!status)
handler.writeResult (object);
return status;
};
auto objectMethod = [] (Context& context, Object& object)
{
Impl handler (context);
Json::Value error;
auto status = handler.check (error);
if (!status)
handler.writeResult (object);
else
RPC::copyFrom (object, error);
return status;
};
auto handler = Handler {
Impl::name(),
valueMethod,
Impl::role(),
Impl::condition(),
objectMethod};
table_[Impl::name()] = handler;
};
};
HandlerTable HANDLERS({
// Request-response methods
{ "account_info", &doAccountInfo, Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_currencies", &doAccountCurrencies, Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_lines", &doAccountLines, Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_offers", &doAccountOffers, Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_tx", &doAccountTxSwitch, Role::USER, NEEDS_NETWORK_CONNECTION },
{ "blacklist", &doBlackList, Role::ADMIN, NO_CONDITION },
{ "book_offers", &doBookOffers, Role::USER, NEEDS_CURRENT_LEDGER },
{ "can_delete", &doCanDelete, Role::ADMIN, NO_CONDITION },
{ "connect", &doConnect, Role::ADMIN, NO_CONDITION },
{ "consensus_info", &doConsensusInfo, Role::ADMIN, NO_CONDITION },
{ "get_counts", &doGetCounts, Role::ADMIN, NO_CONDITION },
{ "internal", &doInternal, Role::ADMIN, NO_CONDITION },
{ "feature", &doFeature, Role::ADMIN, NO_CONDITION },
{ "fetch_info", &doFetchInfo, Role::ADMIN, NO_CONDITION },
{ "ledger", &doLedger, Role::USER, NEEDS_NETWORK_CONNECTION },
{ "ledger_accept", &doLedgerAccept, Role::ADMIN, NEEDS_CURRENT_LEDGER },
{ "ledger_cleaner", &doLedgerCleaner, Role::ADMIN, NEEDS_NETWORK_CONNECTION },
{ "ledger_closed", &doLedgerClosed, Role::USER, NEEDS_CLOSED_LEDGER },
{ "ledger_current", &doLedgerCurrent, Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_data", &doLedgerData, Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_entry", &doLedgerEntry, Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_header", &doLedgerHeader, Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_request", &doLedgerRequest, Role::ADMIN, NO_CONDITION },
{ "log_level", &doLogLevel, Role::ADMIN, NO_CONDITION },
{ "logrotate", &doLogRotate, Role::ADMIN, NO_CONDITION },
{ "owner_info", &doOwnerInfo, Role::USER, NEEDS_CURRENT_LEDGER },
{ "peers", &doPeers, Role::ADMIN, NO_CONDITION },
{ "path_find", &doPathFind, Role::USER, NEEDS_CURRENT_LEDGER },
{ "ping", &doPing, Role::USER, NO_CONDITION },
{ "print", &doPrint, Role::ADMIN, NO_CONDITION },
// { "profile", &doProfile, Role::USER, NEEDS_CURRENT_LEDGER },
{ "proof_create", &doProofCreate, Role::ADMIN, NO_CONDITION },
{ "proof_solve", &doProofSolve, Role::ADMIN, NO_CONDITION },
{ "proof_verify", &doProofVerify, Role::ADMIN, NO_CONDITION },
{ "random", &doRandom, Role::USER, NO_CONDITION },
{ "ripple_path_find", &doRipplePathFind, Role::USER, NEEDS_CURRENT_LEDGER },
{ "sign", &doSign, Role::USER, NO_CONDITION },
{ "submit", &doSubmit, Role::USER, NEEDS_CURRENT_LEDGER },
{ "server_info", &doServerInfo, Role::USER, NO_CONDITION },
{ "server_state", &doServerState, Role::USER, NO_CONDITION },
{ "sms", &doSMS, Role::ADMIN, NO_CONDITION },
{ "stop", &doStop, Role::ADMIN, NO_CONDITION },
{ "transaction_entry", &doTransactionEntry, Role::USER, NEEDS_CURRENT_LEDGER },
{ "tx", &doTx, Role::USER, NEEDS_NETWORK_CONNECTION },
{ "tx_history", &doTxHistory, Role::USER, NO_CONDITION },
{ "unl_add", &doUnlAdd, Role::ADMIN, NO_CONDITION },
{ "unl_delete", &doUnlDelete, Role::ADMIN, NO_CONDITION },
{ "unl_list", &doUnlList, Role::ADMIN, NO_CONDITION },
{ "unl_load", &doUnlLoad, Role::ADMIN, NO_CONDITION },
{ "unl_network", &doUnlNetwork, Role::ADMIN, NO_CONDITION },
{ "unl_reset", &doUnlReset, Role::ADMIN, NO_CONDITION },
{ "unl_score", &doUnlScore, Role::ADMIN, NO_CONDITION },
{ "validation_create", &doValidationCreate, Role::ADMIN, NO_CONDITION },
{ "validation_seed", &doValidationSeed, Role::ADMIN, NO_CONDITION },
{ "wallet_accounts", &doWalletAccounts, Role::USER, NEEDS_CURRENT_LEDGER },
{ "wallet_propose", &doWalletPropose, Role::ADMIN, NO_CONDITION },
{ "wallet_seed", &doWalletSeed, Role::ADMIN, NO_CONDITION },
{ "account_info", byRef (&doAccountInfo), Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_currencies", byRef (&doAccountCurrencies), Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_lines", byRef (&doAccountLines), Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_offers", byRef (&doAccountOffers), Role::USER, NEEDS_CURRENT_LEDGER },
{ "account_tx", byRef (&doAccountTxSwitch), Role::USER, NEEDS_NETWORK_CONNECTION },
{ "blacklist", byRef (&doBlackList), Role::ADMIN, NO_CONDITION },
{ "book_offers", byRef (&doBookOffers), Role::USER, NEEDS_CURRENT_LEDGER },
{ "can_delete", byRef (&doCanDelete), Role::ADMIN, NO_CONDITION },
{ "connect", byRef (&doConnect), Role::ADMIN, NO_CONDITION },
{ "consensus_info", byRef (&doConsensusInfo), Role::ADMIN, NO_CONDITION },
{ "get_counts", byRef (&doGetCounts), Role::ADMIN, NO_CONDITION },
{ "internal", byRef (&doInternal), Role::ADMIN, NO_CONDITION },
{ "feature", byRef (&doFeature), Role::ADMIN, NO_CONDITION },
{ "fetch_info", byRef (&doFetchInfo), Role::ADMIN, NO_CONDITION },
{ "ledger_accept", byRef (&doLedgerAccept), Role::ADMIN, NEEDS_CURRENT_LEDGER },
{ "ledger_cleaner", byRef (&doLedgerCleaner), Role::ADMIN, NEEDS_NETWORK_CONNECTION },
{ "ledger_closed", byRef (&doLedgerClosed), Role::USER, NEEDS_CLOSED_LEDGER },
{ "ledger_current", byRef (&doLedgerCurrent), Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_data", byRef (&doLedgerData), Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_entry", byRef (&doLedgerEntry), Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_header", byRef (&doLedgerHeader), Role::USER, NEEDS_CURRENT_LEDGER },
{ "ledger_request", byRef (&doLedgerRequest), Role::ADMIN, NO_CONDITION },
{ "log_level", byRef (&doLogLevel), Role::ADMIN, NO_CONDITION },
{ "logrotate", byRef (&doLogRotate), Role::ADMIN, NO_CONDITION },
{ "owner_info", byRef (&doOwnerInfo), Role::USER, NEEDS_CURRENT_LEDGER },
{ "peers", byRef (&doPeers), Role::ADMIN, NO_CONDITION },
{ "path_find", byRef (&doPathFind), Role::USER, NEEDS_CURRENT_LEDGER },
{ "ping", byRef (&doPing), Role::USER, NO_CONDITION },
{ "print", byRef (&doPrint), Role::ADMIN, NO_CONDITION },
// { "profile", byRef (&doProfile), Role::USER, NEEDS_CURRENT_LEDGER },
{ "proof_create", byRef (&doProofCreate), Role::ADMIN, NO_CONDITION },
{ "proof_solve", byRef (&doProofSolve), Role::ADMIN, NO_CONDITION },
{ "proof_verify", byRef (&doProofVerify), Role::ADMIN, NO_CONDITION },
{ "random", byRef (&doRandom), Role::USER, NO_CONDITION },
{ "ripple_path_find", byRef (&doRipplePathFind), Role::USER, NEEDS_CURRENT_LEDGER },
{ "sign", byRef (&doSign), Role::USER, NO_CONDITION },
{ "submit", byRef (&doSubmit), Role::USER, NEEDS_CURRENT_LEDGER },
{ "server_info", byRef (&doServerInfo), Role::USER, NO_CONDITION },
{ "server_state", byRef (&doServerState), Role::USER, NO_CONDITION },
{ "sms", byRef (&doSMS), Role::ADMIN, NO_CONDITION },
{ "stop", byRef (&doStop), Role::ADMIN, NO_CONDITION },
{ "transaction_entry", byRef (&doTransactionEntry), Role::USER, NEEDS_CURRENT_LEDGER },
{ "tx", byRef (&doTx), Role::USER, NEEDS_NETWORK_CONNECTION },
{ "tx_history", byRef (&doTxHistory), Role::USER, NO_CONDITION },
{ "unl_add", byRef (&doUnlAdd), Role::ADMIN, NO_CONDITION },
{ "unl_delete", byRef (&doUnlDelete), Role::ADMIN, NO_CONDITION },
{ "unl_list", byRef (&doUnlList), Role::ADMIN, NO_CONDITION },
{ "unl_load", byRef (&doUnlLoad), Role::ADMIN, NO_CONDITION },
{ "unl_network", byRef (&doUnlNetwork), Role::ADMIN, NO_CONDITION },
{ "unl_reset", byRef (&doUnlReset), Role::ADMIN, NO_CONDITION },
{ "unl_score", byRef (&doUnlScore), Role::ADMIN, NO_CONDITION },
{ "validation_create", byRef (&doValidationCreate), Role::ADMIN, NO_CONDITION },
{ "validation_seed", byRef (&doValidationSeed), Role::ADMIN, NO_CONDITION },
{ "wallet_accounts", byRef (&doWalletAccounts), Role::USER, NEEDS_CURRENT_LEDGER },
{ "wallet_propose", byRef (&doWalletPropose), Role::ADMIN, NO_CONDITION },
{ "wallet_seed", byRef (&doWalletSeed), Role::ADMIN, NO_CONDITION },
// Evented methods
{ "subscribe", &doSubscribe, Role::USER, NO_CONDITION },
{ "unsubscribe", &doUnsubscribe, Role::USER, NO_CONDITION },
{ "subscribe", byRef (&doSubscribe), Role::USER, NO_CONDITION },
{ "unsubscribe", byRef (&doUnsubscribe), Role::USER, NO_CONDITION },
});
} // namespace
const Handler* getHandler(std::string name) {
const Handler* getHandler(std::string const& name) {
return HANDLERS.getHandler(name);
}

View File

@@ -22,10 +22,13 @@
#include <ripple/core/Config.h>
#include <ripple/rpc/RPCHandler.h>
#include <ripple/rpc/Status.h>
namespace ripple {
namespace RPC {
class Object;
// Under what condition can we call this RPC?
enum Condition {
NO_CONDITION = 0,
@@ -36,15 +39,17 @@ enum Condition {
struct Handler
{
typedef Json::Value (*Method) (Context&);
template <class JsonValue>
using Method = std::function <Status (Context&, JsonValue&)>;
const char* name_;
Method method_;
Method<Json::Value> valueMethod_;
Role role_;
RPC::Condition condition_;
Method<Object> objectMethod_;
};
const Handler* getHandler(std::string name);
const Handler* getHandler (std::string const&);
/** Return a Json::objectValue with a single entry. */
template <class Value>

View File

@@ -43,7 +43,8 @@ Json::Value RPCHandler::doRpcCommand (
const std::string& strMethod,
Json::Value const& jvParams,
Role role,
Resource::Charge& loadType)
Resource::Charge& loadType,
Yield yield)
{
WriteLog (lsTRACE, RPCHandler)
<< "doRpcCommand:" << strMethod << ":" << jvParams;
@@ -60,7 +61,7 @@ Json::Value RPCHandler::doRpcCommand (
// Provide the JSON-RPC method as the field "command" in the request.
params[jss::command] = strMethod;
Json::Value jvResult = doCommand (params, role, loadType);
Json::Value jvResult = doCommand (params, role, loadType, yield);
// Always report "status". On an error report the request as received.
if (jvResult.isMember ("error"))
@@ -79,7 +80,8 @@ Json::Value RPCHandler::doRpcCommand (
Json::Value RPCHandler::doCommand (
const Json::Value& params,
Role role,
Resource::Charge& loadType)
Resource::Charge& loadType,
Yield yield)
{
if (role != Role::ADMIN)
{
@@ -139,9 +141,10 @@ Json::Value RPCHandler::doCommand (
{
LoadEvent::autoptr ev = getApp().getJobQueue().getLoadEventAP(
jtGENERIC, "cmd:" + strCommand);
RPC::Context context {params, loadType, netOps_, infoSub_, role_};
auto result = handler->method_(context);
assert (result.isObject());
RPC::Context context {
params, loadType, netOps_, infoSub_, role_, yield};
Json::Value result (Json::objectValue);
handler->valueMethod_ (context, result);
return result;
}
catch (std::exception& e)