#include #include #include #include #include // { // ledger_hash : // ledger_index : // ... // } namespace RPC { using boost::json::value_to; Result doLedgerEntry(Context const& context) { auto request = context.params; boost::json::object response = {}; bool const binary = getBool(request, "binary", false); auto v = ledgerInfoFromRequest(context); if (auto status = std::get_if(&v)) return *status; auto lgrInfo = std::get(v); ripple::uint256 key; if (request.contains(JS(index))) { if (!request.at(JS(index)).is_string()) return Status{RippledError::rpcINVALID_PARAMS, "indexNotString"}; if (!key.parseHex(request.at(JS(index)).as_string().c_str())) return Status{RippledError::rpcINVALID_PARAMS, "malformedIndex"}; } else if (request.contains(JS(account_root))) { if (!request.at(JS(account_root)).is_string()) return Status{ RippledError::rpcINVALID_PARAMS, "account_rootNotString"}; auto const account = ripple::parseBase58( request.at(JS(account_root)).as_string().c_str()); if (!account || account->isZero()) return Status{RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else key = ripple::keylet::account(*account).key; } else if (request.contains(JS(check))) { if (!request.at(JS(check)).is_string()) return Status{RippledError::rpcINVALID_PARAMS, "checkNotString"}; if (!key.parseHex(request.at(JS(check)).as_string().c_str())) { return Status{RippledError::rpcINVALID_PARAMS, "checkMalformed"}; } } else if (request.contains(JS(deposit_preauth))) { if (!request.at(JS(deposit_preauth)).is_object()) { if (!request.at(JS(deposit_preauth)).is_string() || !key.parseHex( request.at(JS(deposit_preauth)).as_string().c_str())) { return Status{ RippledError::rpcINVALID_PARAMS, "deposit_preauthMalformed"}; } } else if ( !request.at(JS(deposit_preauth)).as_object().contains(JS(owner)) || !request.at(JS(deposit_preauth)) .as_object() .at(JS(owner)) .is_string()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(deposit_preauth)) .as_object() .contains(JS(authorized)) || !request.at(JS(deposit_preauth)) .as_object() .at(JS(authorized)) .is_string()) { return Status{ RippledError::rpcINVALID_PARAMS, "authorizedNotString"}; } else { boost::json::object const& deposit_preauth = request.at(JS(deposit_preauth)).as_object(); auto const owner = ripple::parseBase58( deposit_preauth.at(JS(owner)).as_string().c_str()); auto const authorized = ripple::parseBase58( deposit_preauth.at(JS(authorized)).as_string().c_str()); if (!owner) return Status{ RippledError::rpcINVALID_PARAMS, "malformedOwner"}; else if (!authorized) return Status{ RippledError::rpcINVALID_PARAMS, "malformedAuthorized"}; else key = ripple::keylet::depositPreauth(*owner, *authorized).key; } } else if (request.contains(JS(directory))) { if (!request.at(JS(directory)).is_object()) { if (!request.at(JS(directory)).is_string()) return Status{ RippledError::rpcINVALID_PARAMS, "directoryNotString"}; if (!key.parseHex(request.at(JS(directory)).as_string().c_str())) { return Status{ RippledError::rpcINVALID_PARAMS, "malformedDirectory"}; } } else if ( request.at(JS(directory)).as_object().contains(JS(sub_index)) && !request.at(JS(directory)).as_object().at(JS(sub_index)).is_int64()) { return Status{RippledError::rpcINVALID_PARAMS, "sub_indexNotInt"}; } else { auto directory = request.at(JS(directory)).as_object(); std::uint64_t subIndex = directory.contains(JS(sub_index)) ? boost::json::value_to( directory.at(JS(sub_index))) : 0; if (directory.contains(JS(dir_root))) { ripple::uint256 uDirRoot; if (directory.contains(JS(owner))) { // May not specify both dir_root and owner. return Status{ RippledError::rpcINVALID_PARAMS, "mayNotSpecifyBothDirRootAndOwner"}; } else if (!uDirRoot.parseHex( directory.at(JS(dir_root)).as_string().c_str())) { return Status{ RippledError::rpcINVALID_PARAMS, "malformedDirRoot"}; } else { key = ripple::keylet::page(uDirRoot, subIndex).key; } } else if (directory.contains(JS(owner))) { auto const ownerID = ripple::parseBase58( directory.at(JS(owner)).as_string().c_str()); if (!ownerID) { return Status{ RippledError::rpcINVALID_PARAMS, "malformedAddress"}; } else { key = ripple::keylet::page( ripple::keylet::ownerDir(*ownerID), subIndex) .key; } } else { return Status{ RippledError::rpcINVALID_PARAMS, "missingOwnerOrDirRoot"}; } } } else if (request.contains(JS(escrow))) { if (!request.at(JS(escrow)).is_object()) { if (!key.parseHex(request.at(JS(escrow)).as_string().c_str())) return Status{ RippledError::rpcINVALID_PARAMS, "malformedEscrow"}; } else if ( !request.at(JS(escrow)).as_object().contains(JS(owner)) || !request.at(JS(escrow)).as_object().at(JS(owner)).is_string()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(escrow)).as_object().contains(JS(seq)) || !request.at(JS(escrow)).as_object().at(JS(seq)).is_int64()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"}; } else { auto const id = ripple::parseBase58(request.at(JS(escrow)) .as_object() .at(JS(owner)) .as_string() .c_str()); if (!id) return Status{ RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else { std::uint32_t seq = request.at(JS(escrow)).as_object().at(JS(seq)).as_int64(); key = ripple::keylet::escrow(*id, seq).key; } } } else if (request.contains(JS(offer))) { if (!request.at(JS(offer)).is_object()) { if (!key.parseHex(request.at(JS(offer)).as_string().c_str())) return Status{ RippledError::rpcINVALID_PARAMS, "malformedOffer"}; } else if ( !request.at(JS(offer)).as_object().contains(JS(account)) || !request.at(JS(offer)).as_object().at(JS(account)).is_string()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedAccount"}; } else if ( !request.at(JS(offer)).as_object().contains(JS(seq)) || !request.at(JS(offer)).as_object().at(JS(seq)).is_int64()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"}; } else { auto offer = request.at(JS(offer)).as_object(); auto const id = ripple::parseBase58( offer.at(JS(account)).as_string().c_str()); if (!id) return Status{ RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else { std::uint32_t seq = boost::json::value_to(offer.at(JS(seq))); key = ripple::keylet::offer(*id, seq).key; } } } else if (request.contains(JS(payment_channel))) { if (!request.at(JS(payment_channel)).is_string()) return Status{ RippledError::rpcINVALID_PARAMS, "paymentChannelNotString"}; if (!key.parseHex(request.at(JS(payment_channel)).as_string().c_str())) return Status{ RippledError::rpcINVALID_PARAMS, "malformedPaymentChannel"}; } else if (request.contains(JS(ripple_state))) { if (!request.at(JS(ripple_state)).is_object()) return Status{ RippledError::rpcINVALID_PARAMS, "rippleStateNotObject"}; ripple::Currency currency; boost::json::object const& state = request.at(JS(ripple_state)).as_object(); if (!state.contains(JS(currency)) || !state.at(JS(currency)).is_string()) { return Status{RippledError::rpcINVALID_PARAMS, "currencyNotString"}; } if (!state.contains(JS(accounts)) || !state.at(JS(accounts)).is_array() || 2 != state.at(JS(accounts)).as_array().size() || !state.at(JS(accounts)).as_array().at(0).is_string() || !state.at(JS(accounts)).as_array().at(1).is_string() || (state.at(JS(accounts)).as_array().at(0).as_string() == state.at(JS(accounts)).as_array().at(1).as_string())) { return Status{RippledError::rpcINVALID_PARAMS, "malformedAccounts"}; } auto const id1 = ripple::parseBase58( state.at(JS(accounts)).as_array().at(0).as_string().c_str()); auto const id2 = ripple::parseBase58( state.at(JS(accounts)).as_array().at(1).as_string().c_str()); if (!id1 || !id2) return Status{ RippledError::rpcINVALID_PARAMS, "malformedAddresses"}; else if (!ripple::to_currency( currency, state.at(JS(currency)).as_string().c_str())) return Status{ ClioError::rpcMALFORMED_CURRENCY, "malformedCurrency"}; key = ripple::keylet::line(*id1, *id2, currency).key; } else if (request.contains(JS(ticket))) { if (!request.at(JS(ticket)).is_object()) { if (!request.at(JS(ticket)).is_string()) return Status{ RippledError::rpcINVALID_PARAMS, "ticketNotString"}; if (!key.parseHex(request.at(JS(ticket)).as_string().c_str())) return Status{ RippledError::rpcINVALID_PARAMS, "malformedTicket"}; } else if ( !request.at(JS(ticket)).as_object().contains(JS(owner)) || !request.at(JS(ticket)).as_object().at(JS(owner)).is_string()) { return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) || !request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64()) { return Status{ RippledError::rpcINVALID_PARAMS, "malformedTicketSeq"}; } else { auto const id = ripple::parseBase58(request.at(JS(ticket)) .as_object() .at(JS(owner)) .as_string() .c_str()); if (!id) return Status{ RippledError::rpcINVALID_PARAMS, "malformedOwner"}; else { std::uint32_t seq = request.at(JS(offer)) .as_object() .at(JS(ticket_seq)) .as_int64(); key = ripple::getTicketIndex(*id, seq); } } } else { return Status{RippledError::rpcINVALID_PARAMS, "unknownOption"}; } auto dbResponse = context.backend->fetchLedgerObject(key, lgrInfo.seq, context.yield); if (!dbResponse or dbResponse->size() == 0) return Status{"entryNotFound"}; response[JS(index)] = ripple::strHex(key); response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash); response[JS(ledger_index)] = lgrInfo.seq; if (binary) { response[JS(node_binary)] = ripple::strHex(*dbResponse); } else { ripple::STLedgerEntry sle{ ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key}; response[JS(node)] = toJson(sle); } return response; } } // namespace RPC