From d62287d54b89ecc12bb6dad4ac9e297c1dc3e446 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 28 Jan 2014 10:28:24 -0800 Subject: [PATCH] Calculate correct fee in RPC sign/submit, error if over limit --- src/ripple/rpc/api/ErrorCodes.h | 1 + src/ripple/rpc/impl/ErrorCodes.cpp | 1 + src/ripple_app/rpc/RPCHandler.cpp | 144 ++++++++++++++++++++++++++--- 3 files changed, 132 insertions(+), 14 deletions(-) diff --git a/src/ripple/rpc/api/ErrorCodes.h b/src/ripple/rpc/api/ErrorCodes.h index ddca504d1..1d66c0c5a 100644 --- a/src/ripple/rpc/api/ErrorCodes.h +++ b/src/ripple/rpc/api/ErrorCodes.h @@ -44,6 +44,7 @@ enum error_code_i rpcNOT_STANDALONE, rpcTOO_BUSY, rpcSLOW_DOWN, + rpcHIGH_FEE, // Networking rpcNO_CLOSED, diff --git a/src/ripple/rpc/impl/ErrorCodes.cpp b/src/ripple/rpc/impl/ErrorCodes.cpp index 05ecf03ee..61fec983e 100644 --- a/src/ripple/rpc/impl/ErrorCodes.cpp +++ b/src/ripple/rpc/impl/ErrorCodes.cpp @@ -35,6 +35,7 @@ public: add (rpcACT_MALFORMED, "actMalformed", "Account malformed."); add (rpcACT_NOT_FOUND, "actNotFound", "Account not found."); add (rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string."); + add (rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit."); add (rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."); add (rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."); add (rpcBAD_MARKET, "badMarket", "No such market."); diff --git a/src/ripple_app/rpc/RPCHandler.cpp b/src/ripple_app/rpc/RPCHandler.cpp index 694cf1c85..cc18bde3d 100644 --- a/src/ripple_app/rpc/RPCHandler.cpp +++ b/src/ripple_app/rpc/RPCHandler.cpp @@ -87,9 +87,85 @@ private: std::atomic LegacyPathFind::inProgress (0); int LegacyPathFind::maxInProgress (2); +//------------------------------------------------------------------------------ -Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh) +// VFALCO TODO Move this to a Tuning.h or Defaults.h +enum { + defaultAutoFillFeeMultiplier = 10 +}; + +/** Fill in the fee on behalf of the client. + This is called when the client does not explicitly specify the fee. + The client may also put a ceiling on the amount of the fee. This ceiling + is expressed as a multiplier based on the current ledger's fee schedule. + + JSON fields + + "Fee" The fee paid by the transaction. Omitted when the client + wants the fee filled in. + + "fee_mult_max" A multiplier applied to the current ledger's transaction + fee that caps the maximum the fee server should auto fill. + If this optional field is not specified, then a default + multiplier is used. + + @param tx The JSON corresponding to the transaction to fill in + @param ledger A ledger for retrieving the current fee schedule + @param result A JSON object for injecting error results, if any + @param admin `true` if this is called by an administrative endpoint. +*/ +static void autofill_fee (Json::Value& request, + Ledger::pointer ledger, Json::Value& result, bool admin) +{ + Json::Value& tx (request["tx_json"]); + + if (tx.isMember ("Fee")) + return; + + int mult (defaultAutoFillFeeMultiplier); + if (request.isMember ("fee_mult_max")) + { + if (request["fee_mult_max"].isNumeric ()) + { + mult = request["fee_mult_max"].asInt(); + } + else + { + RPC::inject_error (rpcHIGH_FEE, RPC::expected_field_message ( + "fee_mult_max", "a number"), result); + return; + } + } + + // Administrative endpoints are exempt from local fees + uint64 fee = ledger->scaleFeeLoad ( + getConfig().FEE_DEFAULT, admin); + + uint64 const limit (mult * getConfig().FEE_DEFAULT); + if (fee > limit) + { + std::stringstream ss; + ss << + "Fee of " << fee << + " exceeds the requested tx limit of " << limit; + RPC::inject_error (rpcHIGH_FEE, ss.str(), result); + return; + } + + tx ["Fee"] = (int) fee; +} + +//------------------------------------------------------------------------------ + +// VFALCO TODO This function should take a reference to the params, modify it +// as needed, and then there should be a separate function to +// submit the tranaction +// +Json::Value RPCHandler::transactionSign (Json::Value params, + bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh) +{ + mlh.unlock(); Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("transactionSign: %s") % params); @@ -145,7 +221,6 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool AccountState::pointer asSrc = bOffline ? AccountState::pointer () // Don't look up address if offline. : mNetOps->getAccountState (lSnapshot, raSrcAddressID); - mlh.unlock(); if (!bOffline && !asSrc) { @@ -156,17 +231,9 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool return rpcError (rpcSRC_ACT_NOT_FOUND); } - if (! tx_json.isMember ("Fee") && ( - "AccountSet" == sType - || "Payment" == sType - || "OfferCreate" == sType - || "OfferCancel" == sType - || "TrustSet" == sType)) - { - // VFALCO TODO This needs to be fixed -// feeReq = lSnapshot->scaleFeeLoad(, - tx_json["Fee"] = (int) getConfig ().FEE_DEFAULT; - } + autofill_fee (params, lSnapshot, jvResult, mRole == Config::ADMIN); + if (RPC::contains_error (jvResult)) + return jvResult; if ("Payment" == sType) { @@ -4231,4 +4298,53 @@ Json::Value RPCInternalHandler::runHandler (const std::string& name, const Json: return rpcError (rpcBAD_SYNTAX); } -// vim:ts=4 +//------------------------------------------------------------------------------ + +class JSONRPCTests : public UnitTest +{ +public: + void testAutoFillFees () + { + beginTestCase ("autofill_fee"); + + RippleAddress rootSeedMaster = RippleAddress::createSeedGeneric ("masterpassphrase"); + RippleAddress rootGeneratorMaster = RippleAddress::createGeneratorPublic (rootSeedMaster); + RippleAddress rootAddress = RippleAddress::createAccountPublic (rootGeneratorMaster, 0); + uint64 startAmount (100000); + Ledger::pointer ledger (boost::make_shared ( + rootAddress, startAmount)); + + { + Json::Value req; + Json::Value result; + Json::Reader ().parse ( + "{ \"fee_mult_max\" : 1, \"tx_json\" : { } } " + , req); + autofill_fee (req, ledger, result, true); + + expect (! RPC::contains_error (result)); + } + + { + Json::Value req; + Json::Value result; + Json::Reader ().parse ( + "{ \"fee_mult_max\" : 0, \"tx_json\" : { } } " + , req); + autofill_fee (req, ledger, result, true); + + expect (RPC::contains_error (result)); + } + } + + void runTest () + { + testAutoFillFees (); + } + + JSONRPCTests () : UnitTest ("ripple", "JSONRPC") + { + } +}; + +static JSONRPCTests jsonRPCTests;