void LoopThread(unsigned int n, uint64_t eta50, string* ppattern, string* pmaster_seed, string* pmaster_seed_hex, string* paccount_id) { RippleAddress naSeed; RippleAddress naAccount; string pattern = *ppattern; string account_id; uint128 key; getRand(key.begin(), key.size()); uint64_t count = 0; uint64_t last_count = 0; do { naSeed.setSeed(key); RippleAddress naGenerator = createGeneratorPublic(naSeed); naAccount.setAccountPublic(naGenerator.getAccountPublic(), 0); account_id = naAccount.humanAccountID(); count++; if (count % UPDATE_ITERATIONS == 0) { boost::unique_lock<boost::mutex> lock(mutex); total_searched += count - last_count; last_count = count; uint64_t nSecs = time(NULL) - start_time; double speed = (1.0 * total_searched)/nSecs; const char* unit = "seconds"; double eta50f = eta50/speed; if (eta50f > 100) { unit = "minutes"; eta50f /= 60; if (eta50f > 100) { unit = "hours"; eta50f /= 60; if (eta50f > 48) { unit = "days"; eta50f /= 24; } } } cout << "# Thread " << n << ": " << count << " seeds." << endl << "#" << endl << "# Total Speed: " << speed << " seeds/second" << endl << "# Total Searched: " << total_searched << endl << "# Total Time: " << nSecs << " seconds" << endl << "# ETA 50%: " << eta50f << " " << unit << endl << "# Last: " << account_id << endl << "# Pattern: " << pattern << endl << "#" << endl; } key++; boost::this_thread::yield(); } while ((account_id.substr(0, pattern.size()) != pattern) && !fDone); boost::unique_lock<boost::mutex> lock(mutex); if (fDone) return; fDone = true; cout << "# *** Found by thread " << n << ". ***" << endl << "#" << endl; *pmaster_seed = naSeed.humanSeed(); *pmaster_seed_hex = naSeed.getSeed().ToString(); *paccount_id = account_id; }
std::pair<protocol::TMHello, bool> parseHello (beast::http::message const& m, beast::Journal journal) { auto const& h = m.headers; std::pair<protocol::TMHello, bool> result = { {}, false }; protocol::TMHello& hello = result.first; // protocol version in TMHello is obsolete, // it is supplanted by the values in the headers. { // Required auto const iter = h.find ("Upgrade"); if (iter == h.end()) return result; auto const versions = parse_ProtocolVersions(iter->second); if (versions.empty()) return result; hello.set_protoversion( (static_cast<std::uint32_t>(versions.back().first) << 16) | (static_cast<std::uint32_t>(versions.back().second))); hello.set_protoversionmin( (static_cast<std::uint32_t>(versions.front().first) << 16) | (static_cast<std::uint32_t>(versions.front().second))); } { // Required auto const iter = h.find ("Public-Key"); if (iter == h.end()) return result; RippleAddress addr; addr.setNodePublic (iter->second); if (! addr.isValid()) return result; hello.set_nodepublic (iter->second); } { // Required auto const iter = h.find ("Session-Signature"); if (iter == h.end()) return result; // TODO Security Review hello.set_nodeproof (beast::base64_decode (iter->second)); } { auto const iter = h.find (m.request() ? "User-Agent" : "Server"); if (iter != h.end()) hello.set_fullversion (iter->second); } { auto const iter = h.find ("Network-Time"); if (iter != h.end()) { std::uint64_t nettime; if (! beast::lexicalCastChecked(nettime, iter->second)) return result; hello.set_nettime (nettime); } } { auto const iter = h.find ("Ledger"); if (iter != h.end()) { LedgerIndex ledgerIndex; if (! beast::lexicalCastChecked(ledgerIndex, iter->second)) return result; hello.set_ledgerindex (ledgerIndex); } } { auto const iter = h.find ("Closed-Ledger"); if (iter != h.end()) hello.set_ledgerclosed (beast::base64_decode (iter->second)); } { auto const iter = h.find ("Previous-Ledger"); if (iter != h.end()) hello.set_ledgerprevious (beast::base64_decode (iter->second)); } result.second = true; return result; }
void STAccount::setValueNCA(const RippleAddress& nca) { setValueH160(nca.getAccountID()); }
Json::Value doSubscribe (RPC::Context& context) { auto lock = getApp().masterLock(); // FIXME: This needs to release the master lock immediately // Subscriptions need to be protected by their own lock InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); std::uint32_t uLedgerIndex = context.params_.isMember (jss::ledger_index) && context.params_[jss::ledger_index].isNumeric () ? context.params_[jss::ledger_index].asUInt () : 0; if (!context.infoSub_ && !context.params_.isMember ("url")) { // Must be a JSON-RPC call. WriteLog (lsINFO, RPCHandler) << "doSubscribe: RPC subscribe requires a url"; return rpcError (rpcINVALID_PARAMS); } if (context.params_.isMember ("url")) { if (context.role_ != Config::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params_["url"].asString (); std::string strUsername = context.params_.isMember ("url_username") ? context.params_["url_username"].asString () : ""; std::string strPassword = context.params_.isMember ("url_password") ? context.params_["url_password"].asString () : ""; // DEPRECATED if (context.params_.isMember ("username")) strUsername = context.params_["username"].asString (); // DEPRECATED if (context.params_.isMember ("password")) strPassword = context.params_["password"].asString (); ispSub = context.netOps_.findRpcSub (strUrl); if (!ispSub) { WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: building: " << strUrl; RPCSub::pointer rspSub = RPCSub::New (getApp ().getOPs (), getApp ().getIOService (), getApp ().getJobQueue (), strUrl, strUsername, strPassword); ispSub = context.netOps_.addRpcSub ( strUrl, std::dynamic_pointer_cast<InfoSub> (rspSub)); } else { WriteLog (lsTRACE, RPCHandler) << "doSubscribe: reusing: " << strUrl; if (context.params_.isMember ("username")) dynamic_cast<RPCSub*> (&*ispSub)->setUsername (strUsername); if (context.params_.isMember ("password")) dynamic_cast<RPCSub*> (&*ispSub)->setPassword (strPassword); } } else { ispSub = context.infoSub_; } if (!context.params_.isMember ("streams")) { } else if (!context.params_["streams"].isArray ()) { WriteLog (lsINFO, RPCHandler) << "doSubscribe: streams requires an array."; return rpcError (rpcINVALID_PARAMS); } else { for (auto& it: context.params_["streams"]) { if (it.isString ()) { std::string streamName = it.asString (); if (streamName == "server") { context.netOps_.subServer (ispSub, jvResult, context.role_ == Config::ADMIN); } else if (streamName == "ledger") { context.netOps_.subLedger (ispSub, jvResult); } else if (streamName == "transactions") { context.netOps_.subTransactions (ispSub); } else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED { context.netOps_.subRTTransactions (ispSub); } else { jvResult[jss::error] = "unknownStream"; } } else { jvResult[jss::error] = "malformedStream"; } } } std::string strAccountsProposed = context.params_.isMember ("accounts_proposed") ? "accounts_proposed" : "rt_accounts"; // DEPRECATED if (!context.params_.isMember (strAccountsProposed)) { } else if (!context.params_[strAccountsProposed].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params_[strAccountsProposed]); if (ids.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps_.subAccount (ispSub, ids, uLedgerIndex, true); } if (!context.params_.isMember ("accounts")) { } else if (!context.params_["accounts"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params_["accounts"]); if (ids.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { context.netOps_.subAccount (ispSub, ids, uLedgerIndex, false); WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: accounts: " << ids.size (); } } bool bHaveMasterLock = true; if (!context.params_.isMember ("books")) { } else if (!context.params_["books"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (auto& j: context.params_["books"]) { if (!j.isObject () || !j.isMember (jss::taker_pays) || !j.isMember (jss::taker_gets) || !j[jss::taker_pays].isObject () || !j[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); Book book; bool bBoth = (j.isMember ("both") && j["both"].asBool ()) || (j.isMember ("both_sides") && j["both_sides"].asBool ()); bool bSnapshot = (j.isMember ("snapshot") && j["snapshot"].asBool ()) || (j.isMember ("state_now") && j["state_now"].asBool ()); // TODO(tom): both_sides and state_now are apparently deprecated... // where is this documented? Json::Value taker_pays = j[jss::taker_pays]; Json::Value taker_gets = j[jss::taker_gets]; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency (book.in.currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer (book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.in.currency != !book.in.account) || noAccount() == book.in.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.out.currency != !book.out.account) || noAccount() == book.out.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in.currency == book.out.currency && book.in.account == book.out.account) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } RippleAddress raTakerID; if (!j.isMember ("taker")) raTakerID.setAccountID (noAccount()); else if (!raTakerID.setAccountID (j["taker"].asString ())) return rpcError (rpcBAD_ISSUER); if (!isConsistent (book) || !isConsistentVBC(book)) { WriteLog (lsWARNING, RPCHandler) << "Bad market: " << book; return rpcError (rpcBAD_MARKET); } context.netOps_.subBook (ispSub, book); if (bBoth) context.netOps_.subBook (ispSub, book); if (bSnapshot) { if (bHaveMasterLock) { lock->unlock (); bHaveMasterLock = false; } context.loadType_ = Resource::feeMediumBurdenRPC; auto lpLedger = getApp().getLedgerMaster (). getPublishedLedger (); if (lpLedger) { const Json::Value jvMarker = Json::Value (Json::nullValue); if (bBoth) { Json::Value jvBids (Json::objectValue); Json::Value jvAsks (Json::objectValue); context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvBids); if (jvBids.isMember (jss::offers)) jvResult[jss::bids] = jvBids[jss::offers]; context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvAsks); if (jvAsks.isMember (jss::offers)) jvResult[jss::asks] = jvAsks[jss::offers]; } else { context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvResult); } } } } } return jvResult; }
Json::Value doGatewayBalances (RPC::Context& context) { auto& params = context.params; // Get the current ledger std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. AccountID accountID; auto jvAccepted = RPC::accountFromString (accountID, strIdent, bStrict); if (jvAccepted) return jvAccepted; context.loadType = Resource::feeHighBurdenRPC; result[jss::account] = getApp().accountIDCache().toBase58 (accountID); // Parse the specified hotwallet(s), if any std::set <AccountID> hotWallets; if (params.isMember ("hotwallet")) { Json::Value const& hw = params["hotwallet"]; bool valid = true; auto addHotWallet = [&valid, &hotWallets](Json::Value const& j) { if (j.isString()) { RippleAddress ra; if (ra.setAccountPublic (j.asString ())) { hotWallets.insert(calcAccountID(ra)); } else { auto const a =parseBase58<AccountID>(j.asString()); if (! a) valid = false; else hotWallets.insert(*a); } } else { valid = false; } }; if (hw.isArray()) { for (unsigned i = 0; i < hw.size(); ++i) addHotWallet (hw[i]); } else if (hw.isString()) { addHotWallet (hw); } else { valid = false; } if (! valid) { result[jss::error] = "invalidHotWallet"; return result; } } std::map <Currency, STAmount> sums; std::map <AccountID, std::vector <STAmount>> hotBalances; std::map <AccountID, std::vector <STAmount>> assets; // Traverse the cold wallet's trust lines { forEachItem(*ledger, accountID, [&](std::shared_ptr<SLE const> const& sle) { auto rs = RippleState::makeItem (accountID, sle); if (!rs) return; int balSign = rs->getBalance().signum(); if (balSign == 0) return; auto const& peer = rs->getAccountIDPeer(); // Here, a negative balance means the cold wallet owes (normal) // A positive balance means the cold wallet has an asset (unusual) if (hotWallets.count (peer) > 0) { // This is a specified hot wallt hotBalances[peer].push_back (-rs->getBalance ()); } else if (balSign > 0) { // This is a gateway asset assets[peer].push_back (rs->getBalance ()); } else { // normal negative balance, obligation to customer auto& bal = sums[rs->getBalance().getCurrency()]; if (bal == zero) { // This is needed to set the currency code correctly bal = -rs->getBalance(); } else bal -= rs->getBalance(); } }); } if (! sums.empty()) { Json::Value& j = (result [jss::obligations] = Json::objectValue); for (auto const& e : sums) { j[to_string (e.first)] = e.second.getText (); } } if (! hotBalances.empty()) { Json::Value& j = (result [jss::balances] = Json::objectValue); for (auto const& account : hotBalances) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } if (! assets.empty()) { Json::Value& j = (result [jss::assets] = Json::objectValue); for (auto const& account : assets) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } return result; }
Json::Value doAccountCurrencies (RPC::Context& context) { auto& params = context.params_; // Get the current ledger 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 const strIdent (params.isMember ("account") ? params["account"].asString () : params["ident"].asString ()); int const iIndex (params.isMember ("account_index") ? params["account_index"].asUInt () : 0); bool const bStrict = params.isMember ("strict") && params["strict"].asBool (); // Get info on account. bool bIndex; // out param RippleAddress naAccount; // out param Json::Value jvAccepted (RPC::accountFromString ( ledger, naAccount, bIndex, strIdent, iIndex, bStrict, context.netOps_)); if (!jvAccepted.empty ()) return jvAccepted; std::set<Currency> send, receive; AccountItems rippleLines ( naAccount.getAccountID (), ledger, AccountItem::pointer (new RippleState ())); for (auto item: rippleLines.getItems ()) { RippleState* rspEntry = (RippleState*) item.get (); STAmount const& saBalance = rspEntry->getBalance (); if (saBalance < rspEntry->getLimit ()) receive.insert (saBalance.getCurrency ()); if ((-saBalance) < rspEntry->getLimitPeer ()) send.insert (saBalance.getCurrency ()); } send.erase (badCurrency()); receive.erase (badCurrency()); Json::Value& sendCurrencies = (result["send_currencies"] = Json::arrayValue); for (auto const& c: send) sendCurrencies.append (to_string (c)); Json::Value& recvCurrencies = (result["receive_currencies"] = Json::arrayValue); for (auto const& c: receive) recvCurrencies.append (to_string (c)); return result; }
void run() { // Construct a seed. RippleAddress naSeed; expect (naSeed.setSeedGeneric ("masterpassphrase")); expect (naSeed.humanSeed () == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", naSeed.humanSeed ()); // Create node public/private key pair RippleAddress naNodePublic = RippleAddress::createNodePublic (naSeed); RippleAddress naNodePrivate = RippleAddress::createNodePrivate (naSeed); expect (naNodePublic.humanNodePublic () == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", naNodePublic.humanNodePublic ()); expect (naNodePrivate.humanNodePrivate () == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate ()); // Check node signing. Blob vucTextSrc = strCopy ("Hello, nurse!"); uint256 uHash = Serializer::getSHA512Half (vucTextSrc); Blob vucTextSig; naNodePrivate.signNodePrivate (uHash, vucTextSig); expect (naNodePublic.verifyNodePublic (uHash, vucTextSig, ECDSA::strict), "Verify failed."); // Construct a public generator from the seed. RippleAddress generator = RippleAddress::createGeneratorPublic (naSeed); expect (generator.humanGenerator () == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", generator.humanGenerator ()); // Create account #0 public/private key pair. RippleAddress naAccountPublic0 = RippleAddress::createAccountPublic (generator, 0); RippleAddress naAccountPrivate0 = RippleAddress::createAccountPrivate (generator, naSeed, 0); expect (naAccountPublic0.humanAccountID () == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", naAccountPublic0.humanAccountID ()); expect (naAccountPublic0.humanAccountPublic () == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw", naAccountPublic0.humanAccountPublic ()); // Create account #1 public/private key pair. RippleAddress naAccountPublic1 = RippleAddress::createAccountPublic (generator, 1); RippleAddress naAccountPrivate1 = RippleAddress::createAccountPrivate (generator, naSeed, 1); expect (naAccountPublic1.humanAccountID () == "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", naAccountPublic1.humanAccountID ()); expect (naAccountPublic1.humanAccountPublic () == "aBPXpTfuLy1Bhk3HnGTTAqnovpKWQ23NpFMNkAF6F1Atg5vDyPrw", naAccountPublic1.humanAccountPublic ()); // Check account signing. expect (naAccountPrivate0.accountPrivateSign (uHash, vucTextSig), "Signing failed."); expect (naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Anti-verify failed."); expect (naAccountPrivate1.accountPrivateSign (uHash, vucTextSig), "Signing failed."); expect (naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Anti-verify failed."); // Check account encryption. Blob vucTextCipher = naAccountPrivate0.accountPrivateEncrypt (naAccountPublic1, vucTextSrc); Blob vucTextRecovered = naAccountPrivate1.accountPrivateDecrypt (naAccountPublic0, vucTextCipher); expect (vucTextSrc == vucTextRecovered, "Encrypt-decrypt failed."); { RippleAddress nSeed; uint128 seed1, seed2; seed1.SetHex ("71ED064155FFADFA38782C5E0158CB26"); nSeed.setSeed (seed1); expect (nSeed.humanSeed() == "shHM53KPZ87Gwdqarm1bAmPeXg8Tn", "Incorrect human seed"); expect (nSeed.humanSeed1751() == "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH", "Incorrect 1751 seed"); } }
void RippleAddress::setAccountPublic (const RippleAddress& seed ) { EdKeyPair pubkey(seed.getSeed()); setAccountPublic (pubkey.getPubKey ()); }
void run() { testBase58(RippleAddress::VER_NODE_PUBLIC, 'n'); testBase58(RippleAddress::VER_NODE_PRIVATE, 'h'); testBase58(RippleAddress::VER_ACCOUNT_PUBLIC, 'p'); testBase58(RippleAddress::VER_ACCOUNT_PRIVATE, 'h'); testBase58(RippleAddress::VER_SEED, 's'); // check pass phrase std::string strPass("paysharesmaster"); std::string strBase58Seed("s3q5ZGX2ScQK2rJ4JATp7rND6X5npG3De8jMbB7tuvm2HAVHcCN"); std::string strBase58NodePublic("nfbbWHgJqzqfH1cfRpMdPRkJ19cxTsdHkBtz1SLJJQfyf9Ax6vd"); std::string strBase58AccountPublic("pGreoXKYybde1keKZwDCv8m5V1kT6JH37pgnTUVzdMkdygTixG8"); AccountPrivateKey accountPrivateKey; NodePrivateKey nodePrivateKey; accountPrivateKey.fromPassPhrase(strPass); nodePrivateKey.fromPassPhrase(strPass); expect(accountPrivateKey.base58Seed() == "s3q5ZGX2ScQK2rJ4JATp7rND6X5npG3De8jMbB7tuvm2HAVHcCN", accountPrivateKey.base58Seed()); expect(accountPrivateKey.base58AccountID() == "ganVp9o5emfzpwrG5QVUXqMv8AgLcdvySb", accountPrivateKey.base58AccountID()); expect(accountPrivateKey.base58PublicKey() == strBase58AccountPublic, accountPrivateKey.base58PublicKey()); expect(nodePrivateKey.base58PublicKey() == strBase58NodePublic, nodePrivateKey.base58PublicKey()); Blob sig; uint256 message; accountPrivateKey.sign(message, sig); PaysharesPublicKey publicKey(accountPrivateKey.getPublicKey(), RippleAddress::VER_NODE_PUBLIC); expect(publicKey.verifySignature(message, sig), "Signature didn't verify"); expect(publicKey.getAccountID() == accountPrivateKey.getAccountID(), "Account Id's mis match"); expect(publicKey.base58AccountID() == accountPrivateKey.base58AccountID(), "Account Id's mis match"); Blob nonCanonicalSig(sig); add_l(nonCanonicalSig.data() + 32); expect(sig != nonCanonicalSig, "Non-canonical signature equal to canonical signature"); expect(crypto_sign_verify_detached(nonCanonicalSig.data(), message.data(), message.bytes, publicKey.vchData.data()) == 0, "Non-canonical signature didn't verify (ignoring canonical-ness)"); expect(!publicKey.verifySignature(message, nonCanonicalSig), "Non-canonical signature verified"); AccountPrivateKey privateKey2; privateKey2.fromString(strBase58Seed); // key from base58seed expect(privateKey2.base58Seed() == "s3q5ZGX2ScQK2rJ4JATp7rND6X5npG3De8jMbB7tuvm2HAVHcCN", privateKey2.base58Seed()); expect(privateKey2.base58AccountID() == "ganVp9o5emfzpwrG5QVUXqMv8AgLcdvySb", privateKey2.base58AccountID()); expect(privateKey2.base58PublicKey() == strBase58AccountPublic, privateKey2.base58PublicKey()); privateKey2.sign(message, sig); expect(publicKey.verifySignature(message, sig), "Signature didn't verify"); // check with the previous pubkey // check random /// ======= OLD ==== // Construct a seed. RippleAddress naSeed; expect(naSeed.setSeedGeneric("masterpassphrase")); expect(naSeed.humanSeed() == "s3q5ZGX2ScQK2rJ4JATp7rND6X5npG3De8jMbB7tuvm2HAVHcCN", naSeed.humanSeed()); // Create node public/private key pair RippleAddress naNodePublic = RippleAddress::createNodePublic(naSeed); expect(naNodePublic.verifySignature(message, sig), "Signature didn't verify"); expect(naNodePublic.humanNodePublic() == strBase58NodePublic, naNodePublic.humanNodePublic()); naNodePublic.setNodePublic(strBase58NodePublic); expect(naNodePublic.verifySignature(message, sig), "Signature didn't verify"); expect(naNodePublic.humanNodePublic() == strBase58NodePublic, naNodePublic.humanNodePublic()); RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naSeed); expect(naAccountPublic.humanAccountID() == "ganVp9o5emfzpwrG5QVUXqMv8AgLcdvySb", naAccountPublic.humanAccountID()); expect(naAccountPublic.verifySignature(message, sig), "Signature didn't verify"); expect(naAccountPublic.humanAccountPublic() == strBase58AccountPublic, naAccountPublic.humanAccountPublic()); naAccountPublic.setAccountPublic(strBase58AccountPublic); expect(naAccountPublic.humanAccountID() == "ganVp9o5emfzpwrG5QVUXqMv8AgLcdvySb", naAccountPublic.humanAccountID()); expect(naAccountPublic.verifySignature(message, sig), "Signature didn't verify"); expect(naAccountPublic.humanAccountPublic() == strBase58AccountPublic, naAccountPublic.humanAccountPublic()); Blob rippleSig; RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naSeed); naAccountPrivate.sign(message, rippleSig); expect(rippleSig==sig, "Signature don't match"); RippleAddress naNodePrivate = RippleAddress::createNodePrivate(naSeed); naNodePrivate.sign(message, rippleSig); expect(rippleSig == sig, "Signature don't match"); std::string strPrivateKey("ssQMHypYAPSPgniSyvJQccuL1dJUbXJWVgAPV5QcAuBVEWsZTVQwffsnwTY6Mivoy3NRSVR28ZaCW74F67VSq4VRC4zY1XR"); expect(naNodePrivate.humanNodePrivate() == strPrivateKey, naNodePrivate.humanNodePrivate()); expect(naNodePrivate.setNodePrivate(strPrivateKey),"couldn't create private node"); expect(naNodePrivate.humanNodePrivate() == strPrivateKey, naNodePrivate.humanNodePrivate()); naNodePrivate.sign(message, rippleSig); expect(rippleSig == sig, "Signature don't match"); /* RippleAddress naNodePrivate = RippleAddress::createNodePrivate(naSeed); expect(naNodePrivate.humanNodePrivate() == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate()); // Check node signing. Blob vucTextSrc = strCopy("Hello, nurse!"); uint256 uHash = Serializer::getSHA512Half(vucTextSrc); Blob vucTextSig; naNodePrivate.signNodePrivate(uHash, vucTextSig); expect(naNodePublic.verifyNodePublic(uHash, vucTextSig, ECDSA::strict), "Verify failed."); */ }
static Json::Value signPayment( Json::Value const& params, Json::Value& tx_json, RippleAddress const& raSrcAddressID, RPCDetail::LedgerFacade& ledgerFacade, Role role) { RippleAddress dstAccountID; if (!tx_json.isMember ("Amount")) return RPC::missing_field_error ("tx_json.Amount"); STAmount amount; if (! amountFromJsonNoThrow (amount, tx_json ["Amount"])) return RPC::invalid_field_error ("tx_json.Amount"); if (!tx_json.isMember ("Destination")) return RPC::missing_field_error ("tx_json.Destination"); if (!dstAccountID.setAccountID (tx_json["Destination"].asString ())) return RPC::invalid_field_error ("tx_json.Destination"); if (tx_json.isMember ("Paths") && params.isMember ("build_path")) return RPC::make_error (rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'build_path'"); if (!tx_json.isMember ("Paths") && tx_json.isMember ("Amount") && params.isMember ("build_path")) { // Need a ripple path. Currency uSrcCurrencyID; Account uSrcIssuerID; STAmount saSendMax; if (tx_json.isMember ("SendMax")) { if (! amountFromJsonNoThrow (saSendMax, tx_json ["SendMax"])) return RPC::invalid_field_error ("tx_json.SendMax"); } else { // If no SendMax, default to Amount with sender as issuer. saSendMax = amount; saSendMax.setIssuer (raSrcAddressID.getAccountID ()); } if (saSendMax.isNative () && amount.isNative ()) return RPC::make_error (rpcINVALID_PARAMS, "Cannot build XRP to XRP paths."); { LegacyPathFind lpf (role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); STPathSet spsPaths; STPath fullLiquidityPath; bool valid = ledgerFacade.findPathsForOneIssuer ( dstAccountID, saSendMax.issue (), amount, getConfig ().PATH_SEARCH_OLD, 4, // iMaxPaths spsPaths, fullLiquidityPath); if (!valid) { WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: No paths found."; return rpcError (rpcNO_PATH); } WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: " << spsPaths.getJson (0); if (!spsPaths.empty ()) tx_json["Paths"] = spsPaths.getJson (0); } } return Json::Value(); }
// 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 transaction. // Json::Value transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, RPCDetail::LedgerFacade& ledgerFacade, Role role) { Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << params; if (! params.isMember ("secret")) return RPC::missing_field_error ("secret"); if (! params.isMember ("tx_json")) return RPC::missing_field_error ("tx_json"); RippleAddress naSeed; if (! naSeed.setSeedGeneric (params["secret"].asString ())) return RPC::make_error (rpcBAD_SEED, RPC::invalid_field_message ("secret")); Json::Value& tx_json (params ["tx_json"]); if (! tx_json.isObject ()) return RPC::object_field_error ("tx_json"); if (! tx_json.isMember ("TransactionType")) return RPC::missing_field_error ("tx_json.TransactionType"); std::string const sType = tx_json ["TransactionType"].asString (); if (! tx_json.isMember ("Account")) return RPC::make_error (rpcSRC_ACT_MISSING, RPC::missing_field_message ("tx_json.Account")); RippleAddress raSrcAddressID; if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ())) return RPC::make_error (rpcSRC_ACT_MALFORMED, RPC::invalid_field_message ("tx_json.Account")); bool const verify = !(params.isMember ("offline") && params["offline"].asBool ()); if (!tx_json.isMember ("Sequence") && !verify) return RPC::missing_field_error ("tx_json.Sequence"); // Check for current ledger. if (verify && !getConfig ().RUN_STANDALONE && (ledgerFacade.getValidatedLedgerAge () > 120)) return rpcError (rpcNO_CURRENT); // Check for load. if (ledgerFacade.isLoadedCluster () && (role != Role::ADMIN)) return rpcError (rpcTOO_BUSY); ledgerFacade.snapshotAccountState (raSrcAddressID); if (verify) { if (!ledgerFacade.isValidAccount ()) { // If not offline and did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSign: Failed to find source account " << "in current ledger: " << raSrcAddressID.humanAccountID (); return rpcError (rpcSRC_ACT_NOT_FOUND); } } autofill_fee (params, ledgerFacade, jvResult, role == Role::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; if ("Payment" == sType) { auto e = signPayment( params, tx_json, raSrcAddressID, ledgerFacade, role); if (contains_error(e)) return e; } if (!tx_json.isMember ("Sequence")) tx_json["Sequence"] = ledgerFacade.getSeq (); if (!tx_json.isMember ("Flags")) tx_json["Flags"] = tfFullyCanonicalSig; if (verify) { if (!ledgerFacade.hasAccountRoot ()) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); } RippleAddress secret = RippleAddress::createSeedGeneric ( params["secret"].asString ()); RippleAddress masterGenerator = RippleAddress::createGeneratorPublic ( secret); RippleAddress masterAccountPublic = RippleAddress::createAccountPublic ( masterGenerator, 0); if (verify) { WriteLog (lsTRACE, RPCHandler) << "verify: " << masterAccountPublic.humanAccountID () << " : " << raSrcAddressID.humanAccountID (); auto const secretAccountID = masterAccountPublic.getAccountID(); if (raSrcAddressID.getAccountID () == secretAccountID) { if (ledgerFacade.accountMasterDisabled ()) return rpcError (rpcMASTER_DISABLED); } else if (!ledgerFacade.accountMatchesRegularKey (secretAccountID)) { return rpcError (rpcBAD_SECRET); } } STParsedJSONObject parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"]; jvResult ["error_code"] = parsed.error ["error_code"]; jvResult ["error_message"] = parsed.error ["error_message"]; return jvResult; } std::unique_ptr<STObject> sopTrans = std::move(parsed.object); sopTrans->setFieldVL ( sfSigningPubKey, masterAccountPublic.getAccountPublic ()); STTx::pointer stpTrans; try { stpTrans = std::make_shared<STTx> (*sopTrans); //WriteLog(lsINFO, RPCHandler) << "radar: before sign " << stpTrans->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } std::string reason; if (!passesLocalChecks (*stpTrans, reason)) return RPC::make_error (rpcINVALID_PARAMS, reason); if (params.isMember ("debug_signing")) { jvResult["tx_unsigned"] = strHex ( stpTrans->getSerializer ().peekData ()); jvResult["tx_signing_hash"] = to_string (stpTrans->getSigningHash ()); } // FIXME: For performance, transactions should not be signed in this code // path. RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate ( masterGenerator, secret, 0); stpTrans->sign (naAccountPrivate); Transaction::pointer tpTrans; try { //WriteLog(lsINFO, RPCHandler) << "radar: after sign " << stpTrans->getFieldAmount(sfAmount); tpTrans = std::make_shared<Transaction>(stpTrans, Validate::NO); //WriteLog(lsINFO, RPCHandler) << "radar: after copy" << tpTrans->getSTransaction()->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } try { // FIXME: For performance, should use asynch interface. tpTrans = ledgerFacade.submitTransactionSync (tpTrans, role == Role::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { return RPC::make_error (rpcINTERNAL, "Unable to sterilize transaction."); } } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction submission."); } try { jvResult["tx_json"] = tpTrans->getJson (0); jvResult["tx_blob"] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult["engine_result"] = sToken; jvResult["engine_result_code"] = tpTrans->getResult (); jvResult["engine_result_message"] = sHuman; } return jvResult; } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during JSON handling."); } }
/** 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, RPCDetail::LedgerFacade& ledgerFacade, Json::Value& result, bool admin) { Json::Value& tx (request["tx_json"]); if (tx.isMember ("Fee")) return; std::uint64_t feeByTrans = 0; if (tx.isMember("TransactionType") && tx["TransactionType"].asString() == "Payment") { if (!tx.isMember("Destination")) { RPC::inject_error (rpcINVALID_PARAMS, "no destination account", result); return; } Config d; std::string dstAccountID = tx["Destination"].asString(); RippleAddress dstAddress; if (!dstAddress.setAccountID(dstAccountID)) { RPC::inject_error (rpcINVALID_PARAMS, "invalid account id", result); return; } //dst account not exist yet, charge a fix amount of fee(0.01) for creating if (!ledgerFacade.isAccountExist(dstAddress.getAccountID())) { feeByTrans = d.FEE_DEFAULT_CREATE; } //if currency is native(VRP/VBC), charge 1/1000 of transfer amount, //otherwise charge a fix amount of fee(0.001) if (tx.isMember("Amount")) { STAmount amount; if (!amountFromJsonNoThrow(amount, tx["Amount"])) { RPC::inject_error (rpcINVALID_PARAMS, "wrong amount format", result); return; } feeByTrans += amount.isNative() ? amount.getNValue() * d.FEE_DEFAULT_RATE_NATIVE : d.FEE_DEFAULT_NONE_NATIVE; } } int mult = Tuning::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; } } // Default fee in fee units. std::uint64_t const feeDefault = getConfig().TRANSACTION_FEE_BASE; // Administrative endpoints are exempt from local fees. std::uint64_t const fee = ledgerFacade.scaleFeeLoad (feeDefault, admin); std::uint64_t const limit = mult * ledgerFacade.scaleFeeBase (feeDefault); 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; } std::stringstream ss; ss << std::max(fee, feeByTrans); tx["Fee"] = ss.str(); }
// This interface is deprecated. Json::Value doRipplePathFind (RPC::Context& context) { RPC::LegacyPathFind lpf (context.role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); context.loadType = Resource::feeHighBurdenRPC; RippleAddress raSrc; RippleAddress raDst; STAmount saDstAmount; Ledger::pointer lpLedger; Json::Value jvResult; if (getConfig().RUN_STANDALONE || context.params.isMember(jss::ledger) || context.params.isMember(jss::ledger_index) || context.params.isMember(jss::ledger_hash)) { // The caller specified a ledger jvResult = RPC::lookupLedger ( context.params, lpLedger, context.netOps); if (!lpLedger) return jvResult; } if (!context.params.isMember (jss::source_account)) { jvResult = rpcError (rpcSRC_ACT_MISSING); } else if (!context.params[jss::source_account].isString () || !raSrc.setAccountID ( context.params[jss::source_account].asString ())) { jvResult = rpcError (rpcSRC_ACT_MALFORMED); } else if (!context.params.isMember (jss::destination_account)) { jvResult = rpcError (rpcDST_ACT_MISSING); } else if (!context.params[jss::destination_account].isString () || !raDst.setAccountID ( context.params[jss::destination_account].asString ())) { jvResult = rpcError (rpcDST_ACT_MALFORMED); } else if ( // Parse saDstAmount. !context.params.isMember (jss::destination_amount) || ! amountFromJsonNoThrow(saDstAmount, context.params[jss::destination_amount]) || saDstAmount <= zero || (!isXRP(saDstAmount.getCurrency ()) && (!saDstAmount.getIssuer () || noAccount() == saDstAmount.getIssuer ()))) { WriteLog (lsINFO, RPCHandler) << "Bad destination_amount."; jvResult = rpcError (rpcINVALID_PARAMS); } else if ( // Checks on source_currencies. context.params.isMember (jss::source_currencies) && (!context.params[jss::source_currencies].isArray () || !context.params[jss::source_currencies].size ()) // Don't allow empty currencies. ) { WriteLog (lsINFO, RPCHandler) << "Bad source_currencies."; jvResult = rpcError (rpcINVALID_PARAMS); } else { context.loadType = Resource::feeHighBurdenRPC; RippleLineCache::pointer cache; if (lpLedger) { // The caller specified a ledger lpLedger = std::make_shared<Ledger> (std::ref (*lpLedger), false); cache = std::make_shared<RippleLineCache>(lpLedger); } else { // The closed ledger is recent and any nodes made resident // have the best chance to persist lpLedger = context.netOps.getClosedLedger(); cache = getApp().getPathRequests().getLineCache(lpLedger, false); } Json::Value jvSrcCurrencies; if (context.params.isMember (jss::source_currencies)) { jvSrcCurrencies = context.params[jss::source_currencies]; } else { jvSrcCurrencies = buildSrcCurrencies(raSrc, cache); } // Fill in currencies destination will accept Json::Value jvDestCur (Json::arrayValue); // TODO(tom): this could be optimized the same way that // PathRequest::doUpdate() is - if we don't obsolete this code first. auto usDestCurrID = accountDestCurrencies (raDst, cache, true); for (auto const& uCurrency: usDestCurrID) jvDestCur.append (to_string (uCurrency)); jvResult[jss::destination_currencies] = jvDestCur; jvResult[jss::destination_account] = raDst.humanAccountID (); int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) && !getApp().getFeeTrack().isLoadedLocal()) { ++level; } if (context.params.isMember(jss::search_depth) && context.params[jss::search_depth].isIntegral()) { int rLev = context.params[jss::search_depth].asInt (); if ((rLev < level) || (context.role == Role::ADMIN)) level = rLev; } auto contextPaths = context.params.isMember(jss::paths) ? boost::optional<Json::Value>(context.params[jss::paths]) : boost::optional<Json::Value>(boost::none); auto pathFindResult = ripplePathFind(cache, raSrc, raDst, saDstAmount, lpLedger, jvSrcCurrencies, contextPaths, level); if (!pathFindResult.first) return pathFindResult.second; // Each alternative differs by source currency. jvResult[jss::alternatives] = pathFindResult.second; } WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("ripple_path_find< %s") % jvResult); return jvResult; }
std::pair<bool, Json::Value> ripplePathFind(RippleLineCache::pointer const& cache, RippleAddress const& raSrc, RippleAddress const& raDst, STAmount const& saDstAmount, Ledger::pointer const& lpLedger, Json::Value const& jvSrcCurrencies, boost::optional<Json::Value> const& contextPaths, int const& level) { FindPaths fp( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths Json::Value jvArray(Json::arrayValue); for (unsigned int i = 0; i != jvSrcCurrencies.size(); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject()) return std::make_pair(false, rpcError(rpcINVALID_PARAMS)); // Parse mandatory currency. if (!jvSource.isMember(jss::currency) || !to_currency( uSrcCurrencyID, jvSource[jss::currency].asString())) { WriteLog(lsINFO, RPCHandler) << "Bad currency."; return std::make_pair(false, rpcError(rpcSRC_CUR_MALFORMED)); } if (uSrcCurrencyID.isNonZero()) uSrcIssuerID = raSrc.getAccountID(); // Parse optional issuer. if (jvSource.isMember(jss::issuer) && ((!jvSource[jss::issuer].isString() || !to_issuer(uSrcIssuerID, jvSource[jss::issuer].asString())) || (uSrcIssuerID.isZero() != uSrcCurrencyID.isZero()) || (noAccount() == uSrcIssuerID))) { WriteLog(lsINFO, RPCHandler) << "Bad issuer."; return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); } STPathSet spsComputed; if (contextPaths) { Json::Value pathSet = Json::objectValue; pathSet[jss::Paths] = contextPaths.get(); STParsedJSONObject paths("pathSet", pathSet); if (paths.object.get() == nullptr) return std::make_pair(false, paths.error); else { spsComputed = paths.object.get()->getFieldPathSet(sfPaths); WriteLog(lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson(0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue( { uSrcCurrencyID, uSrcIssuerID }, spsComputed, fullLiquidityPath); if (!valid) { WriteLog(lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP(uSrcIssuerID) ? isXRP(uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account(raSrc.getAccountID()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1); saMaxAmount.negate(); LedgerEntrySet lesSandbox(lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog(lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); lesSandbox.clear(); rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsDEBUG, PathRequest) << "Extra path element gives " << transHuman(rc.result()); } if (rc.result() == tesSUCCESS) { Json::Value jvEntry(Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(0); jvEntry[jss::paths_canonical] = Json::arrayValue; jvEntry[jss::paths_computed] = spsComputed.getJson(0); jvArray.append(jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo(rc.result(), strToken, strHuman); WriteLog(lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson(0); } } } return std::make_pair(true, jvArray); }
// This interface is deprecated. Json::Value doRipplePathFind (RPC::Context& context) { RPC::LegacyPathFind lpf (context.role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); context.loadType = Resource::feeHighBurdenRPC; RippleAddress raSrc; RippleAddress raDst; STAmount saDstAmount; Ledger::pointer lpLedger; Json::Value jvResult; if (getConfig().RUN_STANDALONE || context.params.isMember(jss::ledger) || context.params.isMember(jss::ledger_index) || context.params.isMember(jss::ledger_hash)) { // The caller specified a ledger jvResult = RPC::lookupLedger ( context.params, lpLedger, context.netOps); if (!lpLedger) return jvResult; } if (!context.params.isMember ("source_account")) { jvResult = rpcError (rpcSRC_ACT_MISSING); } else if (!context.params["source_account"].isString () || !raSrc.setAccountID ( context.params["source_account"].asString ())) { jvResult = rpcError (rpcSRC_ACT_MALFORMED); } else if (!context.params.isMember ("destination_account")) { jvResult = rpcError (rpcDST_ACT_MISSING); } else if (!context.params["destination_account"].isString () || !raDst.setAccountID ( context.params["destination_account"].asString ())) { jvResult = rpcError (rpcDST_ACT_MALFORMED); } else if ( // Parse saDstAmount. !context.params.isMember ("destination_amount") || ! amountFromJsonNoThrow(saDstAmount, context.params["destination_amount"]) || saDstAmount <= zero || (!isXRP(saDstAmount.getCurrency ()) && (!saDstAmount.getIssuer () || noAccount() == saDstAmount.getIssuer ()))) { WriteLog (lsINFO, RPCHandler) << "Bad destination_amount."; jvResult = rpcError (rpcINVALID_PARAMS); } else if ( // Checks on source_currencies. context.params.isMember ("source_currencies") && (!context.params["source_currencies"].isArray () || !context.params["source_currencies"].size ()) // Don't allow empty currencies. ) { WriteLog (lsINFO, RPCHandler) << "Bad source_currencies."; jvResult = rpcError (rpcINVALID_PARAMS); } else { context.loadType = Resource::feeHighBurdenRPC; RippleLineCache::pointer cache; if (lpLedger) { // The caller specified a ledger lpLedger = std::make_shared<Ledger> (std::ref (*lpLedger), false); cache = std::make_shared<RippleLineCache>(lpLedger); } else { // The closed ledger is recent and any nodes made resident // have the best chance to persist lpLedger = context.netOps.getClosedLedger(); cache = getApp().getPathRequests().getLineCache(lpLedger, false); } Json::Value jvSrcCurrencies; if (context.params.isMember ("source_currencies")) { jvSrcCurrencies = context.params["source_currencies"]; } else { auto currencies = accountSourceCurrencies (raSrc, cache, true); jvSrcCurrencies = Json::Value (Json::arrayValue); for (auto const& uCurrency: currencies) { Json::Value jvCurrency (Json::objectValue); jvCurrency["currency"] = to_string(uCurrency); jvSrcCurrencies.append (jvCurrency); } } // Fill in currencies destination will accept Json::Value jvDestCur (Json::arrayValue); // TODO(tom): this could be optimized the same way that // PathRequest::doUpdate() is - if we don't obsolete this code first. auto usDestCurrID = accountDestCurrencies (raDst, cache, true); for (auto const& uCurrency: usDestCurrID) jvDestCur.append (to_string (uCurrency)); jvResult["destination_currencies"] = jvDestCur; jvResult["destination_account"] = raDst.humanAccountID (); Json::Value jvArray (Json::arrayValue); int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) && !getApp().getFeeTrack().isLoadedLocal()) { ++level; } if (context.params.isMember("search_depth") && context.params["search_depth"].isIntegral()) { int rLev = context.params["search_depth"].asInt (); if ((rLev < level) || (context.role == Role::ADMIN)) level = rLev; } FindPaths fp ( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths for (unsigned int i = 0; i != jvSrcCurrencies.size (); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject ()) return rpcError (rpcINVALID_PARAMS); // Parse mandatory currency. if (!jvSource.isMember ("currency") || !to_currency ( uSrcCurrencyID, jvSource["currency"].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad currency."; return rpcError (rpcSRC_CUR_MALFORMED); } if (uSrcCurrencyID.isNonZero ()) uSrcIssuerID = raSrc.getAccountID (); // Parse optional issuer. if (jvSource.isMember ("issuer") && ((!jvSource["issuer"].isString () || !to_issuer (uSrcIssuerID, jvSource["issuer"].asString ())) || (uSrcIssuerID.isZero () != uSrcCurrencyID.isZero ()) || (noAccount() == uSrcIssuerID))) { WriteLog (lsINFO, RPCHandler) << "Bad issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } STPathSet spsComputed; if (context.params.isMember("paths")) { Json::Value pathSet = Json::objectValue; pathSet["Paths"] = context.params["paths"]; STParsedJSONObject paths ("pathSet", pathSet); if (paths.object.get() == nullptr) return paths.error; else { spsComputed = paths.object.get()->getFieldPathSet (sfPaths); WriteLog (lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson (0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue ( {uSrcCurrencyID, uSrcIssuerID}, spsComputed, fullLiquidityPath); if (!valid) { WriteLog (lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP (uSrcIssuerID) ? isXRP (uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account (raSrc.getAccountID ()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount ({uSrcCurrencyID, issuer}, 1); saMaxAmount.negate (); LedgerEntrySet lesSandbox (lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back (fullLiquidityPath); lesSandbox.clear (); rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (rc.result ()); } if (rc.result () == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry["source_amount"] = rc.actualAmountIn.getJson (0); jvEntry["paths_canonical"] = Json::arrayValue; jvEntry["paths_computed"] = spsComputed.getJson (0); jvArray.append (jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo (rc.result (), strToken, strHuman); WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson (0); } } } // Each alternative differs by source currency. jvResult["alternatives"] = jvArray; } WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("ripple_path_find< %s") % jvResult); return jvResult; }
bool STTx::checkMultiSign () const { // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad SigningPubKey. if (!isFieldPresent (sfSigners)) return false; STArray const& signers {getFieldArray (sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners) return false; // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer // with the stuff that stays constant from signature to signature. Serializer const dataStart {startMultiSigningData (*this)}; // We also use the sfAccount field inside the loop. Get it once. auto const txnAccountID = getAccountID (sfAccount); // Determine whether signatures must be full canonical. ECDSA const fullyCanonical = (getFlags() & tfFullyCanonicalSig) ? ECDSA::strict : ECDSA::not_strict; // Signers must be in sorted order by AccountID. AccountID lastAccountID (beast::zero); for (auto const& signer : signers) { auto const accountID = signer.getAccountID (sfAccount); // The account owner may not multisign for themselves. if (accountID == txnAccountID) return false; // Accounts must be in order by account ID. No duplicates allowed. if (lastAccountID >= accountID) return false; // The next signature must be greater than this one. lastAccountID = accountID; // Verify the signature. bool validSig = false; try { Serializer s = dataStart; finishMultiSigningData (accountID, s); RippleAddress const pubKey = RippleAddress::createAccountPublic ( signer.getFieldVL (sfSigningPubKey)); Blob const signature = signer.getFieldVL (sfTxnSignature); validSig = pubKey.accountPublicVerify ( s.getData(), signature, fullyCanonical); } catch (...) { // We assume any problem lies with the signature. validSig = false; } if (!validSig) return false; } // All signatures verified. return true; }
// --> 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 { // We allow the use of the seeds to access #0. // This is poor practice and merely for debuging convenience. RippleAddress naRegular0Public; RippleAddress naRegular0Private; RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naSeed); naRegular0Public.setAccountPublic (naGenerator, 0); naRegular0Private.setAccountPrivate (naGenerator, naSeed, 0); // Account uGeneratorID = naRegular0Public.getAccountID(); SLE::pointer sleGen = netOps.getGenerator (lrLedger, naRegular0Public.getAccountID ()); if (!sleGen) { // Didn't find a generator map, assume it is a master generator. } else { // Found master public key. Blob vucCipher = sleGen->getFieldVL (sfGenerator); Blob vucMasterGenerator = naRegular0Private.accountPrivateDecrypt (naRegular0Public, vucCipher); if (vucMasterGenerator.empty ()) { rpcError (rpcNO_GEN_DECRYPT); } naGenerator.setGenerator (vucMasterGenerator); } bIndex = !iIndex; naAccount.setAccountPublic (naGenerator, iIndex); } return Json::Value (Json::objectValue); }
Json::Value doAccountObjects (RPC::Context& context) { auto const& params = context.params; if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); Ledger::pointer ledger; auto result = RPC::lookupLedger (params, ledger, context.netOps); if (ledger == nullptr) return result; RippleAddress raAccount; { bool bIndex; auto const strIdent = params[jss::account].asString (); auto iIndex = context.params.isMember (jss::account_index) ? context.params[jss::account_index].asUInt () : 0; auto jv = RPC::accountFromString ( raAccount, bIndex, strIdent, iIndex, false); if (! jv.empty ()) { for (auto it = jv.begin (); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } } if (! ledger->exists(getAccountRootIndex( raAccount.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); auto type = ltINVALID; if (params.isMember (jss::type)) { using filter_types = std::vector <std::pair <std::string, LedgerEntryType>>; static beast::static_initializer <filter_types> const types ([]() -> filter_types { return { { "account", ltACCOUNT_ROOT }, { "amendments", ltAMENDMENTS }, { "directory", ltDIR_NODE }, { "fee", ltFEE_SETTINGS }, { "hashes", ltLEDGER_HASHES }, { "offer", ltOFFER }, { "state", ltRIPPLE_STATE }, { "ticket", ltTICKET } }; }()); auto const& p = params[jss::type]; if (! p.isString ()) return RPC::expected_field_error (jss::type, "string"); auto const filter = p.asString (); auto iter = std::find_if (types->begin (), types->end (), [&filter](decltype (types->front ())& t) { return t.first == filter; }); if (iter == types->end ()) return RPC::invalid_field_error (jss::type); type = iter->second; } auto limit = RPC::Tuning::defaultObjectsPerRequest; if (params.isMember (jss::limit)) { auto const& jvLimit = params[jss::limit]; if (! jvLimit.isIntegral ()) return RPC::expected_field_error (jss::limit, "unsigned integer"); limit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); if (context.role != Role::ADMIN) { limit = std::max (RPC::Tuning::minObjectsPerRequest, std::min (limit, RPC::Tuning::maxObjectsPerRequest)); } } uint256 dirIndex; uint256 entryIndex; if (params.isMember (jss::marker)) { auto const& marker = params[jss::marker]; if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); std::stringstream ss (marker.asString ()); std::string s; if (!std::getline(ss, s, ',')) return RPC::invalid_field_error (jss::marker); if (! dirIndex.SetHex (s)) return RPC::invalid_field_error (jss::marker); if (! std::getline (ss, s, ',')) return RPC::invalid_field_error (jss::marker); if (! entryIndex.SetHex (s)) return RPC::invalid_field_error (jss::marker); } if (! RPC::getAccountObjects (*ledger, raAccount.getAccountID (), type, dirIndex, entryIndex, limit, result)) { return RPC::invalid_field_error (jss::marker); } result[jss::account] = raAccount.humanAccountID (); context.loadType = Resource::feeMediumBurdenRPC; return result; }
void run () { testcase ("Seed"); RippleAddress seed; expect (seed.setSeedGeneric ("masterpassphrase")); expect (seed.humanSeed () == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", seed.humanSeed ()); testcase ("RipplePublicKey"); RippleAddress deprecatedPublicKey (RippleAddress::createNodePublic (seed)); expect (deprecatedPublicKey.humanNodePublic () == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", deprecatedPublicKey.humanNodePublic ()); RipplePublicKey publicKey = deprecatedPublicKey.toPublicKey(); expect (publicKey.to_string() == deprecatedPublicKey.humanNodePublic(), publicKey.to_string()); testcase ("Generator"); RippleAddress generator (RippleAddress::createGeneratorPublic (seed)); expect (generator.humanGenerator () == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", generator.humanGenerator ()); }
bool STParsedJSON::parse (std::string const& json_name, Json::Value const& json, SField::ref inName, int depth, std::unique_ptr <STObject>& sub_object) { if (! json.isObject ()) { error = not_an_object (json_name); return false; } SField::ptr name (&inName); boost::ptr_vector<SerializedType> data; Json::Value::Members members (json.getMemberNames ()); for (Json::Value::Members::iterator it (members.begin ()); it != members.end (); ++it) { std::string const& fieldName = *it; Json::Value const& value = json [fieldName]; SField::ref field = SField::getField (fieldName); if (field == sfInvalid) { error = unknown_field (json_name, fieldName); return false; } switch (field.fieldType) { case STI_UINT8: try { if (value.isString ()) { // VFALCO TODO wtf? } else if (value.isInt ()) { if (value.asInt () < 0 || value.asInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asInt (), 0, 255))); } else if (value.isUInt ()) { if (value.asUInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asUInt (), 0, 255))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT16: try { if (value.isString ()) { std::string strValue = value.asString (); if (! strValue.empty () && ((strValue[0] < '0') || (strValue[0] > '9'))) { if (field == sfTransactionType) { TxType const txType (TxFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (txType))); if (*name == sfGeneric) name = &sfTransaction; } else if (field == sfLedgerEntryType) { LedgerEntryType const type (LedgerFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (type))); if (*name == sfGeneric) name = &sfLedgerEntry; } else { error = invalid_data (json_name, fieldName); return false; } } else { data.push_back (new STUInt16 (field, beast::lexicalCastThrow <std::uint16_t> (strValue))); } } else if (value.isInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asInt (), 0, 65535))); } else if (value.isUInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asUInt (), 0, 65535))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT32: try { if (value.isString ()) { data.push_back (new STUInt32 (field, beast::lexicalCastThrow <std::uint32_t> (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt32 (field, range_check_cast <std::uint32_t> (value.asInt (), 0u, 4294967295u))); } else if (value.isUInt ()) { data.push_back (new STUInt32 (field, static_cast <std::uint32_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT64: try { if (value.isString ()) { data.push_back (new STUInt64 (field, uintFromHex (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt64 (field, range_check_cast<std::uint64_t> ( value.asInt (), 0, 18446744073709551615ull))); } else if (value.isUInt ()) { data.push_back (new STUInt64 (field, static_cast <std::uint64_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH128: try { if (value.isString ()) { data.push_back (new STHash128 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH160: try { if (value.isString ()) { data.push_back (new STHash160 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH256: try { if (value.isString ()) { data.push_back (new STHash256 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VL: if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } try { std::pair<Blob, bool> ret(strUnHex (value.asString ())); if (!ret.second) throw std::invalid_argument ("invalid data"); data.push_back (new STVariableLength (field, ret.first)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_AMOUNT: try { data.push_back (new STAmount (field, value)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VECTOR256: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STVector256 (field)); STVector256* tail (dynamic_cast <STVector256*> (&data.back ())); assert (tail); for (Json::UInt i = 0; !json.isValidIndex (i); ++i) { uint256 s; s.SetHex (json[i].asString ()); tail->addValue (s); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_PATHSET: if (!value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STPathSet (field)); STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { STPath p; if (!value[i].isArray ()) { std::stringstream ss; ss << fieldName << "[" << i << "]"; error = array_expected (json_name, ss.str ()); return false; } for (Json::UInt j = 0; value[i].isValidIndex (j); ++j) { std::stringstream ss; ss << fieldName << "[" << i << "][" << j << "]"; std::string const element_name ( json_name + "." + ss.str()); // each element in this path has some combination of account, // currency, or issuer Json::Value pathEl = value[i][j]; if (!pathEl.isObject ()) { error = not_an_object (element_name); return false; } const Json::Value& account = pathEl["account"]; const Json::Value& currency = pathEl["currency"]; const Json::Value& issuer = pathEl["issuer"]; bool hasCurrency = false; uint160 uAccount, uCurrency, uIssuer; if (! account.isNull ()) { // human account id if (! account.isString ()) { error = string_expected (element_name, "account"); return false; } std::string const strValue (account.asString ()); if (value.size () == 40) // 160-bit hex account value uAccount.SetHex (strValue); { RippleAddress a; if (! a.setAccountID (strValue)) { error = invalid_data (element_name, "account"); return false; } uAccount = a.getAccountID (); } } if (!currency.isNull ()) { // human currency if (!currency.isString ()) { error = string_expected (element_name, "currency"); return false; } hasCurrency = true; if (currency.asString ().size () == 40) { uCurrency.SetHex (currency.asString ()); } else if (!STAmount::currencyFromString ( uCurrency, currency.asString ())) { error = invalid_data (element_name, "currency"); return false; } } if (!issuer.isNull ()) { // human account id if (!issuer.isString ()) { error = string_expected (element_name, "issuer"); return false; } if (issuer.asString ().size () == 40) { uIssuer.SetHex (issuer.asString ()); } else { RippleAddress a; if (!a.setAccountID (issuer.asString ())) { error = invalid_data (element_name, "issuer"); return false; } uIssuer = a.getAccountID (); } } p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency)); } tail->addPath (p); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ACCOUNT: { if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } std::string strValue = value.asString (); try { if (value.size () == 40) // 160-bit hex account value { uint160 v; v.SetHex (strValue); data.push_back (new STAccount (field, v)); } else { // ripple address RippleAddress a; if (!a.setAccountID (strValue)) { error = invalid_data (json_name, fieldName); return false; } data.push_back (new STAccount (field, a.getAccountID ())); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } } break; case STI_OBJECT: case STI_TRANSACTION: case STI_LEDGERENTRY: case STI_VALIDATION: if (! value.isObject ()) { error = not_an_object (json_name, fieldName); return false; } if (depth > 64) { error = too_deep (json_name, fieldName); return false; } try { std::unique_ptr <STObject> sub_object_; bool const success (parse (json_name + "." + fieldName, value, field, depth + 1, sub_object_)); if (! success) return false; data.push_back (sub_object_.release ()); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ARRAY: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STArray (field)); STArray* tail = dynamic_cast<STArray*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { bool const isObject (value[i].isObject()); bool const singleKey (isObject ? value [i].size() == 1 : true); if (!isObject || !singleKey) { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]"; error = singleton_expected (ss.str ()); return false; } // TODO: There doesn't seem to be a nice way to get just the // first/only key in an object without copying all keys into // a vector std::string const objectName (value[i].getMemberNames()[0]);; SField::ref nameField (SField::getField(objectName)); if (nameField == sfInvalid) { error = unknown_field (json_name, objectName); return false; } Json::Value const objectFields (value[i][objectName]); std::unique_ptr <STObject> sub_object_; { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]." << objectName; bool const success (parse (ss.str (), objectFields, nameField, depth + 1, sub_object_)); if (! success) return false; } tail->push_back (*sub_object_); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; default: error = bad_type (json_name, fieldName); return false; } } sub_object.reset (new STObject (*name, data)); return true; }
Json::Value doAccountCurrencies (RPC::Context& context) { auto& params = context.params; // Get the current ledger Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); int iIndex = 0; if (params.isMember (jss::account_index)) { auto const& accountIndex = params[jss::account_index]; if (!accountIndex.isUInt() && !accountIndex.isInt ()) return RPC::invalid_field_message (jss::account_index); iIndex = accountIndex.asUInt (); } bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. bool bIndex; // out param RippleAddress naAccount; // out param Json::Value jvAccepted (RPC::accountFromString ( ledger, naAccount, bIndex, strIdent, iIndex, bStrict, context.netOps)); if (!jvAccepted.empty ()) return jvAccepted; std::set<Currency> send, receive; for (auto const& item : getRippleStateItems (naAccount.getAccountID (), ledger)) { RippleState* rspEntry = (RippleState*) item.get (); STAmount const& saBalance = rspEntry->getBalance (); if (saBalance < rspEntry->getLimit ()) receive.insert (saBalance.getCurrency ()); if ((-saBalance) < rspEntry->getLimitPeer ()) send.insert (saBalance.getCurrency ()); } send.erase (badCurrency()); receive.erase (badCurrency()); Json::Value& sendCurrencies = (result[jss::send_currencies] = Json::arrayValue); for (auto const& c: send) sendCurrencies.append (to_string (c)); Json::Value& recvCurrencies = (result[jss::receive_currencies] = Json::arrayValue); for (auto const& c: receive) recvCurrencies.append (to_string (c)); return result; }
// { // ledger_hash : <ledger> // ledger_index : <ledger_index> // ... // } Json::Value RPCHandler::doLedgerEntry (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { masterLockHolder.unlock (); Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger (params, lpLedger, *mNetOps); if (!lpLedger) return jvResult; uint256 uNodeIndex; bool bNodeBinary = false; if (params.isMember ("index")) { // XXX Needs to provide proof. uNodeIndex.SetHex (params["index"].asString ()); bNodeBinary = true; } else if (params.isMember ("account_root")) { RippleAddress naAccount; if (!naAccount.setAccountID (params["account_root"].asString ()) || !naAccount.getAccountID ()) { jvResult["error"] = "malformedAddress"; } else { uNodeIndex = Ledger::getAccountRootIndex (naAccount.getAccountID ()); } } else if (params.isMember ("directory")) { if (!params["directory"].isObject ()) { uNodeIndex.SetHex (params["directory"].asString ()); } else if (params["directory"].isMember ("sub_index") && !params["directory"]["sub_index"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else { std::uint64_t uSubIndex = params["directory"].isMember ("sub_index") ? params["directory"]["sub_index"].asUInt () : 0; if (params["directory"].isMember ("dir_root")) { uint256 uDirRoot; uDirRoot.SetHex (params["dir_root"].asString ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } else if (params["directory"].isMember ("owner")) { RippleAddress naOwnerID; if (!naOwnerID.setAccountID (params["directory"]["owner"].asString ())) { jvResult["error"] = "malformedAddress"; } else { uint256 uDirRoot = Ledger::getOwnerDirIndex (naOwnerID.getAccountID ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } } else { jvResult["error"] = "malformedRequest"; } } } else if (params.isMember ("generator")) { RippleAddress naGeneratorID; if (!params["generator"].isObject ()) { uNodeIndex.SetHex (params["generator"].asString ()); } else if (!params["generator"].isMember ("regular_seed")) { jvResult["error"] = "malformedRequest"; } else if (!naGeneratorID.setSeedGeneric (params["generator"]["regular_seed"].asString ())) { jvResult["error"] = "malformedAddress"; } else { RippleAddress na0Public; // To find the generator's index. RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naGeneratorID); na0Public.setAccountPublic (naGenerator, 0); uNodeIndex = Ledger::getGeneratorIndex (na0Public.getAccountID ()); } } else if (params.isMember ("offer")) { RippleAddress naAccountID; if (!params["offer"].isObject ()) { uNodeIndex.SetHex (params["offer"].asString ()); } else if (!params["offer"].isMember ("account") || !params["offer"].isMember ("seq") || !params["offer"]["seq"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else if (!naAccountID.setAccountID (params["offer"]["account"].asString ())) { jvResult["error"] = "malformedAddress"; } else { std::uint32_t uSequence = params["offer"]["seq"].asUInt (); uNodeIndex = Ledger::getOfferIndex (naAccountID.getAccountID (), uSequence); } } else if (params.isMember ("ripple_state")) { RippleAddress naA; RippleAddress naB; uint160 uCurrency; Json::Value jvRippleState = params["ripple_state"]; if (!jvRippleState.isObject () || !jvRippleState.isMember ("currency") || !jvRippleState.isMember ("accounts") || !jvRippleState["accounts"].isArray () || 2 != jvRippleState["accounts"].size () || !jvRippleState["accounts"][0u].isString () || !jvRippleState["accounts"][1u].isString () || jvRippleState["accounts"][0u].asString () == jvRippleState["accounts"][1u].asString () ) { jvResult["error"] = "malformedRequest"; } else if (!naA.setAccountID (jvRippleState["accounts"][0u].asString ()) || !naB.setAccountID (jvRippleState["accounts"][1u].asString ())) { jvResult["error"] = "malformedAddress"; } else if (!STAmount::currencyFromString (uCurrency, jvRippleState["currency"].asString ())) { jvResult["error"] = "malformedCurrency"; } else { uNodeIndex = Ledger::getRippleStateIndex (naA, naB, uCurrency); } } else { jvResult["error"] = "unknownOption"; } if (uNodeIndex.isNonZero ()) { SLE::pointer sleNode = mNetOps->getSLEi (lpLedger, uNodeIndex); if (params.isMember("binary")) bNodeBinary = params["binary"].asBool(); if (!sleNode) { // Not found. // XXX Should also provide proof. jvResult["error"] = "entryNotFound"; } else if (bNodeBinary) { // XXX Should also provide proof. Serializer s; sleNode->add (s); jvResult["node_binary"] = strHex (s.peekData ()); jvResult["index"] = uNodeIndex.ToString (); } else { jvResult["node"] = sleNode->getJson (0); jvResult["index"] = uNodeIndex.ToString (); } } return jvResult; }
Json::Value doSubscribe (RPC::Context& context) { InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); if (!context.infoSub && !context.params.isMember (jss::url)) { // Must be a JSON-RPC call. WriteLog (lsINFO, RPCHandler) << "doSubscribe: RPC subscribe requires a url"; return rpcError (rpcINVALID_PARAMS); } if (context.params.isMember (jss::url)) { if (context.role != Role::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params[jss::url].asString (); std::string strUsername = context.params.isMember (jss::url_username) ? context.params[jss::url_username].asString () : ""; std::string strPassword = context.params.isMember (jss::url_password) ? context.params[jss::url_password].asString () : ""; // DEPRECATED if (context.params.isMember (jss::username)) strUsername = context.params[jss::username].asString (); // DEPRECATED if (context.params.isMember (jss::password)) strPassword = context.params[jss::password].asString (); ispSub = context.netOps.findRpcSub (strUrl); if (!ispSub) { WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: building: " << strUrl; RPCSub::pointer rspSub = RPCSub::New (getApp ().getOPs (), getApp ().getIOService (), getApp ().getJobQueue (), strUrl, strUsername, strPassword); ispSub = context.netOps.addRpcSub ( strUrl, std::dynamic_pointer_cast<InfoSub> (rspSub)); } else { WriteLog (lsTRACE, RPCHandler) << "doSubscribe: reusing: " << strUrl; if (context.params.isMember (jss::username)) dynamic_cast<RPCSub*> (&*ispSub)->setUsername (strUsername); if (context.params.isMember (jss::password)) dynamic_cast<RPCSub*> (&*ispSub)->setPassword (strPassword); } } else { ispSub = context.infoSub; } if (!context.params.isMember (jss::streams)) { } else if (!context.params[jss::streams].isArray ()) { WriteLog (lsINFO, RPCHandler) << "doSubscribe: streams requires an array."; return rpcError (rpcINVALID_PARAMS); } else { for (auto& it: context.params[jss::streams]) { if (it.isString ()) { std::string streamName = it.asString (); if (streamName == "server") { context.netOps.subServer (ispSub, jvResult, context.role == Role::ADMIN); } else if (streamName == "ledger") { context.netOps.subLedger (ispSub, jvResult); } else if (streamName == "transactions") { context.netOps.subTransactions (ispSub); } else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED { context.netOps.subRTTransactions (ispSub); } else { jvResult[jss::error] = "unknownStream"; } } else { jvResult[jss::error] = "malformedStream"; } } } auto strAccountsProposed = context.params.isMember (jss::accounts_proposed) ? jss::accounts_proposed : jss::rt_accounts; // DEPRECATED if (!context.params.isMember (strAccountsProposed)) { } else if (!context.params[strAccountsProposed].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params[strAccountsProposed]); if (ids.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps.subAccount (ispSub, ids, true); } if (!context.params.isMember (jss::accounts)) { } else if (!context.params[jss::accounts].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params[jss::accounts]); if (ids.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { context.netOps.subAccount (ispSub, ids, false); WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: accounts: " << ids.size (); } } if (!context.params.isMember (jss::books)) { } else if (!context.params[jss::books].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (auto& j: context.params[jss::books]) { if (!j.isObject () || !j.isMember (jss::taker_pays) || !j.isMember (jss::taker_gets) || !j[jss::taker_pays].isObject () || !j[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); Book book; bool bBoth = (j.isMember (jss::both) && j[jss::both].asBool ()) || (j.isMember (jss::both_sides) && j[jss::both_sides].asBool ()); bool bSnapshot = (j.isMember (jss::snapshot) && j[jss::snapshot].asBool ()) || (j.isMember (jss::state_now) && j[jss::state_now].asBool ()); // TODO(tom): both_sides and state_now are apparently deprecated... // where is this documented? Json::Value taker_pays = j[jss::taker_pays]; Json::Value taker_gets = j[jss::taker_gets]; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency (book.in.currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer (book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.in.currency != !book.in.account) || noAccount() == book.in.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.out.currency != !book.out.account) || noAccount() == book.out.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in.currency == book.out.currency && book.in.account == book.out.account) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } RippleAddress raTakerID; if (!j.isMember (jss::taker)) raTakerID.setAccountID (noAccount()); else if (!raTakerID.setAccountID (j[jss::taker].asString ())) return rpcError (rpcBAD_ISSUER); if (!isConsistent (book)) { WriteLog (lsWARNING, RPCHandler) << "Bad market: " << book; return rpcError (rpcBAD_MARKET); } context.netOps.subBook (ispSub, book); if (bBoth) context.netOps.subBook (ispSub, reversed (book)); if (bSnapshot) { context.loadType = Resource::feeMediumBurdenRPC; auto lpLedger = getApp().getLedgerMaster (). getPublishedLedger (); if (lpLedger) { const Json::Value jvMarker = Json::Value (Json::nullValue); Json::Value jvOffers (Json::objectValue); auto add = [&](Json::StaticString field) { context.netOps.getBookPage (context.role == Role::ADMIN, lpLedger, field == jss::asks ? reversed (book) : book, raTakerID.getAccountID(), false, 0, jvMarker, jvOffers); if (jvResult.isMember (field)) { Json::Value& results (jvResult[field]); for (auto const& e : jvOffers[jss::offers]) results.append (e); } else { jvResult[field] = jvOffers[jss::offers]; } }; if (bBoth) { add (jss::bids); add (jss::asks); } else { add (jss::offers); } } } } } return jvResult; }
void STTx::sign (RippleAddress const& private_key) { Blob const signature = private_key.accountPrivateSign (getSigningData (*this)); setFieldVL (sfTxnSignature, signature); }
// TODO(tom): what is that "default"? 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 (jss::account) && !params.isMember (jss::ident)) return RPC::missing_field_error (jss::account); std::string strIdent = params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString (); bool bIndex; int iIndex = params.isMember (jss::account_index) ? params[jss::account_index].asUInt () : 0; bool bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); RippleAddress naAccount; // Get info on account. auto jvAccepted = RPC::accountFromString ( naAccount, bIndex, strIdent, iIndex, bStrict); if (!jvAccepted.empty ()) return jvAccepted; AccountState::pointer asAccepted = getAccountState(*ledger, naAccount, getApp().getSLECache()); if (asAccepted) { asAccepted->addJson (jvAccepted); // See if there's a SignerEntries for this account. AccountID const account = naAccount.getAccountID (); uint256 const signerListIndex = getSignerListIndex (account); auto const signerList = fetch(*ledger, signerListIndex, getApp().getSLECache()); if (signerList) { // Return multi-signing information if there are multi-signers. static const Json::StaticString multiSignersName("multisigners"); jvAccepted[multiSignersName] = signerList->getJson (0); Json::Value& multiSignerJson = jvAccepted[multiSignersName]; // Remove unwanted fields. multiSignerJson.removeMember (sfFlags.getName ()); multiSignerJson.removeMember (sfLedgerEntryType.getName ()); multiSignerJson.removeMember (sfOwnerNode.getName ()); multiSignerJson.removeMember ("index"); } result[jss::account_data] = jvAccepted; } else { result[jss::account] = naAccount.humanAccountID (); RPC::inject_error (rpcACT_NOT_FOUND, result); } return result; }
void STTx::setSigningPubKey (RippleAddress const& naSignPubKey) { setFieldVL (sfSigningPubKey, naSignPubKey.getAccountPublic ()); }
RippleAddress SerializedValidation::getSignerPublic() const { RippleAddress a; a.setNodePublic(getFieldVL(sfSigningPubKey)); return a; }
// { // 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; }
Handoff OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& ssl_bundle, beast::http::message&& request, endpoint_type remote_endpoint) { auto const id = next_id_++; beast::WrappedSink sink (deprecatedLogs()["Peer"], makePrefix(id)); beast::Journal journal (sink); Handoff handoff; if (processRequest(request, handoff)) return handoff; if (! isPeerUpgrade(request)) return handoff; handoff.moved = true; if (journal.trace) journal.trace << "Peer connection upgrade from " << remote_endpoint; error_code ec; auto const local_endpoint (ssl_bundle->socket.local_endpoint(ec)); if (ec) { if (journal.trace) journal.trace << remote_endpoint << " failed: " << ec.message(); return handoff; } auto consumer = m_resourceManager.newInboundEndpoint( beast::IPAddressConversion::from_asio(remote_endpoint)); if (consumer.disconnect()) return handoff; auto const slot = m_peerFinder->new_inbound_slot ( beast::IPAddressConversion::from_asio(local_endpoint), beast::IPAddressConversion::from_asio(remote_endpoint)); if (slot == nullptr) { // self-connect, close handoff.moved = false; return handoff; } // TODO Validate HTTP request { auto const types = beast::rfc2616::split_commas( request.headers["Connect-As"]); if (std::find_if(types.begin(), types.end(), [](std::string const& s) { return beast::ci_equal(s, "peer"); }) == types.end()) { handoff.moved = false; handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address()); handoff.keep_alive = request.keep_alive(); return handoff; } } handoff.moved = true; bool success = true; protocol::TMHello hello; std::tie(hello, success) = parseHello (request, journal); if(! success) return handoff; uint256 sharedValue; std::tie(sharedValue, success) = makeSharedValue( ssl_bundle->stream.native_handle(), journal); if(! success) return handoff; RippleAddress publicKey; std::tie(publicKey, success) = verifyHello (hello, sharedValue, journal, getApp()); if(! success) return handoff; std::string name; bool const cluster = getApp().getUNL().nodeInCluster( publicKey, name); auto const result = m_peerFinder->activate (slot, publicKey.toPublicKey(), cluster); if (result != PeerFinder::Result::success) { if (journal.trace) journal.trace << "Peer " << remote_endpoint << " redirected, slots full"; handoff.moved = false; handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address()); handoff.keep_alive = request.keep_alive(); return handoff; } auto const peer = std::make_shared<PeerImp>(id, remote_endpoint, slot, std::move(request), hello, publicKey, consumer, std::move(ssl_bundle), *this); { // As we are not on the strand, run() must be called // while holding the lock, otherwise new I/O can be // queued after a call to stop(). std::lock_guard <decltype(mutex_)> lock (mutex_); add(peer); peer->run(); } handoff.moved = true; return handoff; }
RippleAddress RippleAddress::createGeneratorPublic (RippleAddress const& naSeed) { RippleAddress naNew; naNew.setGenerator (generateRootDeterministicPublicKey (naSeed.getSeed())); return naNew; }