// Look up the master public generator for a regular seed so we may index source accounts ids. // --> naRegularSeed // <-- naMasterGenerator Json::Value getMasterGenerator ( Ledger::ref lrLedger, const RippleAddress& naRegularSeed, RippleAddress& naMasterGenerator, NetworkOPs& netOps) { RippleAddress na0Public; // To find the generator's index. RippleAddress na0Private; // To decrypt the master generator's cipher. RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naRegularSeed); na0Public.setAccountPublic (naGenerator, 0); na0Private.setAccountPrivate (naGenerator, naRegularSeed, 0); SLE::pointer sleGen = netOps.getGenerator (lrLedger, na0Public.getAccountID ()); if (!sleGen) { // No account has been claimed or has had it password set for seed. return rpcError (rpcNO_ACCOUNT); } Blob vucCipher = sleGen->getFieldVL (sfGenerator); Blob vucMasterGenerator = na0Private.accountPrivateDecrypt (na0Public, vucCipher); if (vucMasterGenerator.empty ()) { return rpcError (rpcFAIL_GEN_DECRYPT); } naMasterGenerator.setGenerator (vucMasterGenerator); return Json::Value (Json::objectValue); }
// Provide the JSON-RPC "result" value. // // JSON-RPC provides a method and an array of params. JSON-RPC is used as a transport for a command and a request object. The // command is the method. The request object is supplied as the first element of the params. Json::Value RPCHandler::doRpcCommand (const std::string& strMethod, Json::Value const& jvParams, int iRole, Resource::Charge& loadType) { WriteLog (lsTRACE, RPCHandler) << "doRpcCommand:" << strMethod << ":" << jvParams; if (!jvParams.isArray () || jvParams.size () > 1) return logRPCError (rpcError (rpcINVALID_PARAMS)); Json::Value params = jvParams.size () ? jvParams[0u] : Json::Value (Json::objectValue); if (!params.isObject ()) return logRPCError (rpcError (rpcINVALID_PARAMS)); // Provide the JSON-RPC method as the field "command" in the request. params[jss::command] = strMethod; Json::Value jvResult = doCommand (params, iRole, loadType); // Always report "status". On an error report the request as received. if (jvResult.isMember ("error")) { jvResult[jss::status] = jss::error; jvResult[jss::request] = params; } else { jvResult[jss::status] = jss::success; } return logRPCError (jvResult); }
Json::Value doFeature (RPC::Context& context) { if (!context.params.isMember (jss::feature)) { Json::Value jvReply = Json::objectValue; jvReply[jss::features] = getApp().getAmendmentTable ().getJson(0); return jvReply; } uint256 uFeature = getApp().getAmendmentTable ().get( context.params[jss::feature].asString()); if (uFeature.isZero ()) { uFeature.SetHex (context.params[jss::feature].asString ()); if (uFeature.isZero ()) return rpcError (rpcBAD_FEATURE); } if (!context.params.isMember (jss::vote)) return getApp().getAmendmentTable ().getJson(uFeature); // WRITEME return rpcError (rpcNOT_SUPPORTED); }
// { // node: <node_public>, // comment: <comment> // optional // } Json::Value doUnlAdd (RPC::Context& context) { auto lock = make_lock(context.app.getMasterMutex()); if (!context.params.isMember (jss::node)) return rpcError (rpcINVALID_PARAMS); auto const id = parseBase58<PublicKey>( TokenType::TOKEN_NODE_PUBLIC, context.params[jss::node].asString ()); if (!id) return rpcError (rpcINVALID_PARAMS); auto const added = context.app.validators().insertPermanentKey ( *id, context.params.isMember (jss::comment) ? context.params[jss::comment].asString () : ""); Json::Value ret (Json::objectValue); ret[jss::pubkey_validator] = context.params[jss::node]; ret[jss::status] = added ? "added" : "already present"; return ret; }
// --> strIdent: public key, account ID, or regular seed. // --> bStrict: Only allow account id or public key. // <-- bIndex: true if iIndex > 0 and used the index. Json::Value accountFromString (Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict, NetworkOPs& netOps) { RippleAddress naSeed; if (naAccount.setAccountPublic (strIdent) || naAccount.setAccountID (strIdent)) { // Got the account. bIndex = false; } else if (bStrict) { return naAccount.setAccountID (strIdent, Base58::getBitcoinAlphabet ()) ? rpcError (rpcACT_BITCOIN) : rpcError (rpcACT_MALFORMED); } // Must be a seed. else if (!naSeed.setSeedGeneric (strIdent)) { return rpcError (rpcBAD_SEED); } else { rpcError(rpcACT_MALFORMED); } return Json::Value (Json::objectValue); }
Json::Value doTx (RPC::Context& context) { if (!context.params.isMember (jss::transaction)) return rpcError (rpcINVALID_PARAMS); bool binary = context.params.isMember (jss::binary) && context.params[jss::binary].asBool (); auto const txid = context.params[jss::transaction].asString (); if (!isHexTxID (txid)) return rpcError (rpcNOT_IMPL); auto txn = getApp().getMasterTransaction ().fetch (uint256 (txid), true); if (!txn) return rpcError (rpcTXN_NOT_FOUND); Json::Value ret = txn->getJson (1, binary); if (txn->getLedger () == 0) return ret; if (auto lgr = context.netOps.getLedgerBySeq (txn->getLedger ())) { bool okay = false; if (binary) { std::string meta; if (lgr->getMetaHex (txn->getID (), meta)) { ret[jss::meta] = meta; okay = true; } } else { TransactionMetaSet::pointer txMeta; if (lgr->getTransactionMeta (txn->getID (), txMeta)) { okay = true; auto meta = txMeta->getJson (0); addPaymentDeliveredAmount (meta, context, txn, txMeta); ret[jss::meta] = meta; } } if (okay) ret[jss::validated] = context.netOps.isValidated (lgr); } return ret; }
// { // start: <index> // } Json::Value doTxHistory (RPC::Context& context) { context.loadType = Resource::feeMediumBurdenRPC; if (!context.params.isMember (jss::start)) return rpcError (rpcINVALID_PARAMS); unsigned int startIndex = context.params[jss::start].asUInt (); if ((startIndex > 10000) && (! isUnlimited (context.role))) return rpcError (rpcNO_PERMISSION); Json::Value obj; Json::Value txs; obj[jss::index] = startIndex; std::string sql = boost::str (boost::format ( "SELECT LedgerSeq, Status, RawTxn " "FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20;") % startIndex); { auto db = context.app.getTxnDB ().checkoutDb (); boost::optional<std::uint64_t> ledgerSeq; boost::optional<std::string> status; soci::blob sociRawTxnBlob (*db); soci::indicator rti; Blob rawTxn; soci::statement st = (db->prepare << sql, soci::into (ledgerSeq), soci::into (status), soci::into (sociRawTxnBlob, rti)); st.execute (); while (st.fetch ()) { if (soci::i_ok == rti) convert(sociRawTxnBlob, rawTxn); else rawTxn.clear (); if (auto trans = Transaction::transactionFromSQL ( ledgerSeq, status, rawTxn, context.app)) txs.append (trans->getJson (0)); } } obj[jss::txs] = txs; return obj; }
Json::Value doLogLevel (RPC::Context& context) { // log_level if (!context.params.isMember (jss::severity)) { // get log severities Json::Value ret (Json::objectValue); Json::Value lev (Json::objectValue); lev[jss::base] = Logs::toString(Logs::fromSeverity(deprecatedLogs().severity())); std::vector< std::pair<std::string, std::string> > logTable ( deprecatedLogs().partition_severities()); typedef std::map<std::string, std::string>::value_type stringPair; for (auto const& it : logTable) lev[it.first] = it.second; ret[jss::levels] = lev; return ret; } LogSeverity const sv ( Logs::fromString (context.params[jss::severity].asString ())); if (sv == lsINVALID) return rpcError (rpcINVALID_PARAMS); auto severity = Logs::toSeverity(sv); // log_level severity if (!context.params.isMember (jss::partition)) { // set base log severity deprecatedLogs().severity(severity); return Json::objectValue; } // log_level partition severity base? if (context.params.isMember (jss::partition)) { // set partition severity std::string partition (context.params[jss::partition].asString ()); if (boost::iequals (partition, "base")) deprecatedLogs().severity (severity); else deprecatedLogs().get(partition).severity(severity); return Json::objectValue; } return rpcError (rpcINVALID_PARAMS); }
// TODO: Get index from an alternate syntax: rXYZ:<index> Json::Value parseAccountRaw (const Json::Value& jvParams, bool bPeer) { std::string strIdent = jvParams[0u].asString (); unsigned int iCursor = jvParams.size (); bool bStrict = false; std::string strPeer; if (!bPeer && iCursor >= 2 && jvParams[iCursor - 1] == "strict") { bStrict = true; --iCursor; } if (bPeer && iCursor >= 2) strPeer = jvParams[iCursor].asString (); int iIndex = 0; // int iIndex = jvParams.size() >= 2 ? lexicalCast <int>(jvParams[1u].asString()) : 0; RippleAddress raAddress; if (!raAddress.setAccountPublic (strIdent) && !raAddress.setAccountID (strIdent) && !raAddress.setSeedGeneric (strIdent)) return rpcError (rpcACT_MALFORMED); // Get info on account. Json::Value jvRequest (Json::objectValue); jvRequest["account"] = strIdent; if (bStrict) jvRequest["strict"] = 1; if (iIndex) jvRequest["account_index"] = iIndex; if (!strPeer.empty ()) { RippleAddress raPeer; if (!raPeer.setAccountPublic (strPeer) && !raPeer.setAccountID (strPeer) && !raPeer.setSeedGeneric (strPeer)) return rpcError (rpcACT_MALFORMED); jvRequest["peer"] = strPeer; } if (iCursor == (2 + bPeer) && !jvParseLedger (jvRequest, jvParams[1u + bPeer].asString ())) return rpcError (rpcLGR_IDX_MALFORMED); return jvRequest; }
// sign/submit any transaction to the network // // sign <private_key> <json> offline // submit <private_key> <json> // submit <tx_blob> Json::Value parseSignSubmit (const Json::Value& jvParams) { Json::Value txJSON; Json::Reader reader; bool bOffline = 3 == jvParams.size () && jvParams[2u].asString () == "offline"; if (1 == jvParams.size ()) { // Submitting tx_blob Json::Value jvRequest; jvRequest["tx_blob"] = jvParams[0u].asString (); return jvRequest; } else if ((2 == jvParams.size () || bOffline) && reader.parse (jvParams[1u].asString (), txJSON)) { // Signing or submitting tx_json. Json::Value jvRequest; jvRequest["secret"] = jvParams[0u].asString (); jvRequest["tx_json"] = txJSON; if (bOffline) jvRequest["offline"] = true; return jvRequest; } return rpcError (rpcINVALID_PARAMS); }
// { // passphrase: <string> // } Json::Value doWalletPropose (RPC::Context& context) { context.lock_.unlock (); RippleAddress naSeed; RippleAddress naAccount; if (!context.params_.isMember ("passphrase")) naSeed.setSeedRandom (); else if (!naSeed.setSeedGeneric (context.params_["passphrase"].asString ())) return rpcError(rpcBAD_SEED); RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naSeed); naAccount.setAccountPublic (naGenerator, 0); Json::Value obj (Json::objectValue); obj["master_seed"] = naSeed.humanSeed (); obj["master_seed_hex"] = to_string (naSeed.getSeed ()); obj["master_key"] = naSeed.humanSeed1751(); obj["account_id"] = naAccount.humanAccountID (); obj["public_key"] = naAccount.humanAccountPublic(); auto acct = naAccount.getAccountPublic(); obj["public_key_hex"] = strHex(acct.begin(), acct.size()); return obj; }
// Build a object { "currency" : "XYZ", "issuer" : "rXYX" } static Json::Value jvParseCurrencyIssuer (const std::string& strCurrencyIssuer) { static boost::regex reCurIss ("\\`([[:alpha:]]{3})(?:/(.+))?\\'"); boost::smatch smMatch; if (boost::regex_match (strCurrencyIssuer, smMatch, reCurIss)) { Json::Value jvResult (Json::objectValue); std::string strCurrency = smMatch[1]; std::string strIssuer = smMatch[2]; jvResult["currency"] = strCurrency; if (strIssuer.length ()) { // Could confirm issuer is a valid Ripple address. jvResult["issuer"] = strIssuer; } return jvResult; } else { return rpcError (rpcINVALID_PARAMS); } }
// { // ip: <string>, // port: <number> // } // XXX Might allow domain for manual connections. Json::Value doConnect (RPC::Context& context) { auto lock = beast::make_lock(getApp().getMasterMutex()); if (getConfig ().RUN_STANDALONE) return "cannot connect in standalone mode"; if (!context.params.isMember (jss::ip)) return RPC::missing_field_error (jss::ip); if (context.params.isMember (jss::port) && !context.params[jss::port].isConvertibleTo (Json::intValue)) { return rpcError (rpcINVALID_PARAMS); } int iPort; if(context.params.isMember (jss::port)) iPort = context.params[jss::port].asInt (); else iPort = 6561; auto ip = beast::IP::Endpoint::from_string( context.params[jss::ip].asString ()); if (! is_unspecified (ip)) getApp().overlay ().connect (ip.at_port(iPort)); return RPC::makeObjectValue ("connecting"); }
Json::Value RPCHandler::doInternal (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { // Used for debug or special-purpose RPC commands if (!params.isMember ("internal_command")) return rpcError (rpcINVALID_PARAMS); return RPCInternalHandler::runHandler (params["internal_command"].asString (), params["params"]); }
Json::Value doPathFind (RPC::Context& context) { auto lpLedger = context.ledgerMaster.getClosedLedger(); if (!context.params.isMember (jss::subcommand) || !context.params[jss::subcommand].isString ()) { return rpcError (rpcINVALID_PARAMS); } if (!context.infoSub) return rpcError (rpcNO_EVENTS); auto sSubCommand = context.params[jss::subcommand].asString (); if (sSubCommand == "create") { context.loadType = Resource::feeHighBurdenRPC; context.infoSub->clearPathRequest (); return context.app.getPathRequests().makePathRequest ( context.infoSub, lpLedger, context.params); } if (sSubCommand == "close") { PathRequest::pointer request = context.infoSub->getPathRequest (); if (!request) return rpcError (rpcNO_PF_REQUEST); context.infoSub->clearPathRequest (); return request->doClose (context.params); } if (sSubCommand == "status") { PathRequest::pointer request = context.infoSub->getPathRequest (); if (!request) return rpcError (rpcNO_PF_REQUEST); return request->doStatus (context.params); } return rpcError (rpcINVALID_PARAMS); }
// Populate the UNL from a local validators.txt file. Json::Value RPCHandler::doUnlLoad (Json::Value, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { if (getConfig ().VALIDATORS_FILE.empty () || !getApp().getUNL ().nodeLoad (getConfig ().VALIDATORS_FILE)) { return rpcError (rpcLOAD_FAILED); } return "loading"; }
Json::Value doSMS (RPC::Context& context) { if (!context.params_.isMember ("text")) return rpcError (rpcINVALID_PARAMS); HTTPClient::sendSMS ( getApp().getIOService (), context.params_["text"].asString ()); return "sms dispatched"; }
// { // secret_key: <signing_secret_key> // channel_id: 256-bit channel id // drops: 64-bit uint (as string) // } Json::Value doChannelAuthorize (RPC::Context& context) { auto const& params (context.params); for (auto const& p : {jss::secret, jss::channel_id, jss::amount}) if (!params.isMember (p)) return RPC::missing_field_error (p); Json::Value result; auto const keypair = RPC::keypairForSignature (params, result); if (RPC::contains_error (result)) return result; uint256 channelId; if (!channelId.SetHexExact (params[jss::channel_id].asString ())) return rpcError (rpcCHANNEL_MALFORMED); std::uint64_t drops = 0; try { drops = std::stoul (params[jss::amount].asString ()); } catch (std::exception const&) { return rpcError (rpcCHANNEL_AMT_MALFORMED); } Serializer msg; serializePayChanAuthorization (msg, channelId, XRPAmount (drops)); try { auto const buf = sign (keypair.first, keypair.second, msg.slice ()); result[jss::signature] = strHex (buf); } catch (std::exception&) { result = RPC::make_error (rpcINTERNAL, "Exception occurred during signing."); } return result; }
// Populate the UNL from a local validators.txt file. Json::Value doUnlLoad (RPC::Context& context) { auto lock = getApp().masterLock(); if (getConfig ().VALIDATORS_FILE.empty () || !getApp().getUNL ().nodeLoad (getConfig ().VALIDATORS_FILE)) { return rpcError (rpcLOAD_FAILED); } return "loading"; }
// { // public_key: <public_key> // channel_id: 256-bit channel id // drops: 64-bit uint (as string) // signature: signature to verify // } Json::Value doChannelVerify (RPC::Context& context) { auto const& params (context.params); for (auto const& p : {jss::public_key, jss::channel_id, jss::amount, jss::signature}) if (!params.isMember (p)) return RPC::missing_field_error (p); std::string const strPk = params[jss::public_key].asString (); auto const pk = parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk); if (!pk) return rpcError (rpcPUBLIC_MALFORMED); uint256 channelId; if (!channelId.SetHexExact (params[jss::channel_id].asString ())) return rpcError (rpcCHANNEL_MALFORMED); std::uint64_t drops = 0; try { drops = std::stoul (params[jss::amount].asString ()); } catch (std::exception const&) { return rpcError (rpcCHANNEL_AMT_MALFORMED); } std::pair<Blob, bool> sig(strUnHex (params[jss::signature].asString ())); if (!sig.second || !sig.first.size ()) return rpcError (rpcINVALID_PARAMS); Serializer msg; serializePayChanAuthorization (msg, channelId, XRPAmount (drops)); Json::Value result; result[jss::signature_verified] = verify (*pk, msg.slice (), makeSlice (sig.first), /*canonical*/ true); return result; }
// { // feature : <feature> // vetoed : true/false // } Json::Value doFeature (RPC::Context& context) { // Get majority amendment status majorityAmendments_t majorities; if (auto const valLedger = context.ledgerMaster.getValidatedLedger()) majorities = getMajorityAmendments (*valLedger); auto& table = context.app.getAmendmentTable (); if (!context.params.isMember (jss::feature)) { auto features = table.getJson(0); for (auto const& m : majorities) { features[to_string(m.first)][jss::majority] = m.second.time_since_epoch().count(); } Json::Value jvReply = Json::objectValue; jvReply[jss::features] = features; return jvReply; } auto feature = table.find ( context.params[jss::feature].asString()); if (!feature && !feature.SetHexExact (context.params[jss::feature].asString ())) return rpcError (rpcBAD_FEATURE); if (context.params.isMember (jss::vetoed)) { if (context.params[jss::vetoed].asBool ()) context.app.getAmendmentTable().veto (feature); else context.app.getAmendmentTable().unVeto(feature); } Json::Value jvReply = table.getJson(feature); auto m = majorities.find (feature); if (m != majorities.end()) jvReply [jss::majority] = m->second.time_since_epoch().count(); return jvReply; }
// json <command> <json> Json::Value parseJson (const Json::Value& jvParams) { Json::Reader reader; Json::Value jvRequest; WriteLog (lsTRACE, RPCParser) << "RPC method: " << jvParams[0u]; WriteLog (lsTRACE, RPCParser) << "RPC json: " << jvParams[1u]; if (reader.parse (jvParams[1u].asString (), jvRequest)) { jvRequest["method"] = jvParams[0u]; return jvRequest; } return rpcError (rpcINVALID_PARAMS); }
// Result: // { // random: <uint256> // } Json::Value doRandom (RPC::Context& context) { uint256 rand; try { RandomNumbers::getInstance ().fillBytes (rand.begin (), rand.size ()); Json::Value jvResult; jvResult["random"] = to_string (rand); return jvResult; } catch (...) { return rpcError (rpcINTERNAL); } }
// Result: // { // random: <uint256> // } Json::Value doRandom (RPC::Context& context) { // TODO(tom): the try/catch is almost certainly redundant, we catch at the // top level too. try { uint256 rand; RandomNumbers::getInstance ().fillBytes (rand.begin (), rand.size ()); Json::Value jvResult; jvResult["random"] = to_string (rand); return jvResult; } catch (...) { return rpcError (rpcINTERNAL); } }
// { // account: <indent>, // account_index : <index> // optional // strict: <bool> // true, only allow public keys and addresses. false, default. // ledger_hash : <ledger> // ledger_index : <ledger_index> // } Json::Value doAccountInfo (RPC::Context& context) { auto& params = context.params_; Ledger::pointer ledger; Json::Value result = RPC::lookupLedger (params, ledger, context.netOps_); if (!ledger) return result; if (!params.isMember ("account") && !params.isMember ("ident")) return RPC::missing_field_error ("account"); std::string strIdent = params.isMember ("account") ? params["account"].asString () : params["ident"].asString (); bool bIndex; int iIndex = params.isMember ("account_index") ? params["account_index"].asUInt () : 0; bool bStrict = params.isMember ("strict") && params["strict"].asBool (); RippleAddress naAccount; // Get info on account. Json::Value jvAccepted = RPC::accountFromString ( ledger, naAccount, bIndex, strIdent, iIndex, bStrict, context.netOps_); if (!jvAccepted.empty ()) return jvAccepted; auto asAccepted = context.netOps_.getAccountState (ledger, naAccount); if (asAccepted) { asAccepted->addJson (jvAccepted); result["account_data"] = jvAccepted; } else { result["account"] = naAccount.humanAccountID (); result = rpcError (rpcACT_NOT_FOUND, result); } return result; }
Json::Value RPCInternalHandler::runHandler (const std::string& name, const Json::Value& params) { RPCInternalHandler* h = sHeadHandler; while (h != nullptr) { if (name == h->mName) { WriteLog (lsWARNING, RPCHandler) << "Internal command " << name << ": " << params; Json::Value ret = h->mHandler (params); WriteLog (lsWARNING, RPCHandler) << "Internal command returns: " << ret; return ret; } h = h->mNextHandler; } return rpcError (rpcBAD_SYNTAX); }
// ripple_path_find <json> [<ledger>] Json::Value parseRipplePathFind (const Json::Value& jvParams) { Json::Reader reader; Json::Value jvRequest; bool bLedger = 2 == jvParams.size (); WriteLog (lsTRACE, RPCParser) << "RPC json: " << jvParams[0u]; if (reader.parse (jvParams[0u].asString (), jvRequest)) { if (bLedger) { jvParseLedger (jvRequest, jvParams[1u].asString ()); } return jvRequest; } return rpcError (rpcINVALID_PARAMS); }
// { // node: <domain>|<public_key> // } Json::Value doUnlDelete (RPC::Context& context) { auto lock = beast::make_lock(context.app.getMasterMutex()); if (!context.params.isMember (jss::node)) return rpcError (rpcINVALID_PARAMS); auto strNode = context.params[jss::node].asString (); RippleAddress raNodePublic; if (raNodePublic.setNodePublic (strNode)) { context.app.getUNL ().nodeRemovePublic (raNodePublic); return RPC::makeObjectValue ("removing node by public key"); } else { context.app.getUNL ().nodeRemoveDomain (strNode); return RPC::makeObjectValue ("removing node by domain"); } }
// unl_add <domain>|<node_public> [<comment>] Json::Value parseUnlAdd (const Json::Value& jvParams) { std::string strNode = jvParams[0u].asString (); std::string strComment = (jvParams.size () == 2) ? jvParams[1u].asString () : ""; RippleAddress naNodePublic; if (strNode.length ()) { Json::Value jvRequest; jvRequest["node"] = strNode; if (strComment.length ()) jvRequest["comment"] = strComment; return jvRequest; } return rpcError (rpcINVALID_PARAMS); }
// { // node: <domain>|<public_key> // } Json::Value RPCHandler::doUnlDelete (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { if (!params.isMember ("node")) return rpcError (rpcINVALID_PARAMS); std::string strNode = params["node"].asString (); RippleAddress raNodePublic; if (raNodePublic.setNodePublic (strNode)) { getApp().getUNL ().nodeRemovePublic (raNodePublic); return "removing node by public key"; } else { getApp().getUNL ().nodeRemoveDomain (strNode); return "removing node by domain"; } }