#include #include std::optional accountFromStringStrict(std::string const& account) { auto blob = ripple::strUnHex(account); boost::optional publicKey = {}; if (blob && ripple::publicKeyType(ripple::makeSlice(*blob))) { publicKey = ripple::PublicKey( ripple::Slice{blob->data(), blob->size()}); } else { publicKey = ripple::parseBase58( ripple::TokenType::AccountPublic, account); } boost::optional result; if (publicKey) result = ripple::calcAccountID(*publicKey); else result = ripple::parseBase58(account); if (result) return result.value(); else return {}; } std::pair< std::shared_ptr, std::shared_ptr> deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs) { std::pair< std::shared_ptr, std::shared_ptr> result; { ripple::SerialIter s{ blobs.transaction.data(), blobs.transaction.size()}; result.first = std::make_shared(s); } { ripple::SerialIter s{blobs.metadata.data(), blobs.metadata.size()}; result.second = std::make_shared(s, ripple::sfMetadata); } return result; } std::pair< std::shared_ptr, std::shared_ptr> deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs, std::uint32_t seq) { auto [tx, meta] = deserializeTxPlusMeta(blobs); std::shared_ptr m = std::make_shared( tx->getTransactionID(), seq, *meta); return {tx, m}; } boost::json::object toJson(ripple::STBase const& obj) { auto start = std::chrono::system_clock::now(); boost::json::value value = boost::json::parse( obj.getJson(ripple::JsonOptions::none).toStyledString()); auto end = std::chrono::system_clock::now(); value.as_object()["deserialization_time_microseconds"] = std::chrono::duration_cast(end - start) .count(); return value.as_object(); } boost::json::object <<<<<<< HEAD getJson(ripple::TxMeta const& meta) { auto start = std::chrono::system_clock::now(); boost::json::value value = boost::json::parse( meta.getJson(ripple::JsonOptions::none).toStyledString()); auto end = std::chrono::system_clock::now(); value.as_object()["deserialization_time_microseconds"] = std::chrono::duration_cast(end - start) .count(); return value.as_object(); } boost::json::value getJson(Json::Value const& value) { boost::json::value boostValue = boost::json::parse(value.toStyledString()); return boostValue; } boost::json::object getJson(ripple::SLE const& sle) ======= toJson(ripple::SLE const& sle) >>>>>>> dev { auto start = std::chrono::system_clock::now(); boost::json::value value = boost::json::parse( sle.getJson(ripple::JsonOptions::none).toStyledString()); auto end = std::chrono::system_clock::now(); value.as_object()["deserialization_time_microseconds"] = std::chrono::duration_cast(end - start) .count(); return value.as_object(); } boost::json::object toJson(ripple::LedgerInfo const& lgrInfo) { boost::json::object header; header["ledger_sequence"] = lgrInfo.seq; header["ledger_hash"] = ripple::strHex(lgrInfo.hash); header["txns_hash"] = ripple::strHex(lgrInfo.txHash); header["state_hash"] = ripple::strHex(lgrInfo.accountHash); header["parent_hash"] = ripple::strHex(lgrInfo.parentHash); header["total_coins"] = ripple::to_string(lgrInfo.drops); header["close_flags"] = lgrInfo.closeFlags; // Always show fields that contribute to the ledger hash header["parent_close_time"] = lgrInfo.parentCloseTime.time_since_epoch().count(); header["close_time"] = lgrInfo.closeTime.time_since_epoch().count(); header["close_time_resolution"] = lgrInfo.closeTimeResolution.count(); return header; } std::optional ledgerSequenceFromRequest( boost::json::object const& request, BackendInterface const& backend) { if (not request.contains("ledger_index")) { return backend.fetchLatestLedgerSequence(); } else { return request.at("ledger_index").as_int64(); } } <<<<<<< HEAD std::optional traverseOwnedNodes( BackendInterface const& backend, ripple::AccountID const& accountID, std::uint32_t sequence, ripple::uint256 const& cursor, std::function atOwnedNode) { auto const rootIndex = ripple::keylet::ownerDir(accountID); auto currentIndex = rootIndex; std::vector keys; std::optional nextCursor = {}; auto start = std::chrono::system_clock::now(); for (;;) { auto ownedNode = backend.fetchLedgerObject(currentIndex.key, sequence); if (!ownedNode) { throw std::runtime_error("Could not find owned node"); } ripple::SerialIter it{ownedNode->data(), ownedNode->size()}; ripple::SLE dir{it, currentIndex.key}; for (auto const& key : dir.getFieldV256(ripple::sfIndexes)) { if (key >= cursor) keys.push_back(key); } auto const uNodeNext = dir.getFieldU64(ripple::sfIndexNext); if (uNodeNext == 0) break; currentIndex = ripple::keylet::page(rootIndex, uNodeNext); } auto end = std::chrono::system_clock::now(); BOOST_LOG_TRIVIAL(debug) << "Time loading owned directories: " << ((end - start).count() / 1000000000.0); start = std::chrono::system_clock::now(); auto objects = backend.fetchLedgerObjects(keys, sequence); end = std::chrono::system_clock::now(); BOOST_LOG_TRIVIAL(debug) << "Time loading owned entries: " << ((end - start).count() / 1000000000.0); for (auto i = 0; i < objects.size(); ++i) { ripple::SerialIter it{objects[i].data(), objects[i].size()}; ripple::SLE sle(it, keys[i]); if (!atOwnedNode(sle)) { nextCursor = keys[i+1]; break; } } return nextCursor; } boost::optional parseRippleLibSeed(boost::json::value const& value) { // ripple-lib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While rippled never encode seeds that way, we // try to detect such keys to avoid user confusion. if (!value.is_string()) return {}; auto const result = ripple::decodeBase58Token(value.as_string().c_str(), ripple::TokenType::None); if (result.size() == 18 && static_cast(result[0]) == std::uint8_t(0xE1) && static_cast(result[1]) == std::uint8_t(0x4B)) return ripple::Seed(ripple::makeSlice(result.substr(2))); return {}; } std::pair keypairFromRequst(boost::json::object const& request, boost::json::value& error) { bool const has_key_type = request.contains("key_type"); // All of the secret types we allow, but only one at a time. // The array should be constexpr, but that makes Visual Studio unhappy. static std::string const secretTypes[]{ "passphrase", "secret", "seed", "seed_hex"}; // Identify which secret type is in use. std::string secretType = ""; int count = 0; for (auto t : secretTypes) { if (request.contains(t)) { ++count; secretType = t; } } if (count == 0) { error = "missing field secret"; return {}; } if (count > 1) { error = "Exactly one of the following must be specified: " " passphrase, secret, seed, or seed_hex"; return {}; } boost::optional keyType; boost::optional seed; if (has_key_type) { if (!request.at("key_type").is_string()) { error = "key_type must be string"; return {}; } std::string key_type = request.at("key_type").as_string().c_str(); keyType = ripple::keyTypeFromString(key_type); if (!keyType) { error = "Invalid field key_type"; return {}; } if (secretType == "secret") { error = "The secret field is not allowed if key_type is used."; return {}; } } // ripple-lib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. if (secretType != "seed_hex") { seed = parseRippleLibSeed(request.at(secretType)); if (seed) { // If the user passed in an Ed25519 seed but *explicitly* // requested another key type, return an error. if (keyType.value_or(ripple::KeyType::ed25519) != ripple::KeyType::ed25519) { error = "Specified seed is for an Ed25519 wallet."; return {}; } keyType = ripple::KeyType::ed25519; } } if (!keyType) keyType = ripple::KeyType::secp256k1; if (!seed) { if (has_key_type) { if (!request.at(secretType).is_string()) { error = "secret value must be string"; return {}; } std::string key = request.at(secretType).as_string().c_str(); if (secretType == "seed") seed = ripple::parseBase58(key); else if (secretType == "passphrase") seed = ripple::parseGenericSeed(key); else if (secretType == "seed_hex") { ripple::uint128 s; if (s.parseHex(key)) seed.emplace(ripple::Slice(s.data(), s.size())); } } else { if (!request.at("secret").is_string()) { error = "field secret should be a string"; return {}; } std::string secret = request.at("secret").as_string().c_str(); seed = ripple::parseGenericSeed(secret); } } if (!seed) { error = "Bad Seed: invalid field message secretType"; return {}; } if (keyType != ripple::KeyType::secp256k1 && keyType != ripple::KeyType::ed25519) { error = "keypairForSignature: invalid key type"; return {}; } return generateKeyPair(*keyType, *seed); } std::vector getAccountsFromTransaction(boost::json::object const& transaction) { std::vector accounts = {}; for (auto const& [key, value] : transaction) { if (value.is_object()) { auto inObject = getAccountsFromTransaction(value.as_object()); accounts.insert(accounts.end(), inObject.begin(), inObject.end()); } else if (value.is_string()) { auto account = accountFromStringStrict(value.as_string().c_str()); if (account) { accounts.push_back(*account); } } } return accounts; ======= std::vector ledgerInfoToBlob(ripple::LedgerInfo const& info) { ripple::Serializer s; s.add32(info.seq); s.add64(info.drops.drops()); s.addBitString(info.parentHash); s.addBitString(info.txHash); s.addBitString(info.accountHash); s.add32(info.parentCloseTime.time_since_epoch().count()); s.add32(info.closeTime.time_since_epoch().count()); s.add8(info.closeTimeResolution.count()); s.add8(info.closeFlags); s.addBitString(info.hash); return s.peekData(); >>>>>>> dev }