mirror of
https://github.com/Xahau/xahaud.git
synced 2026-10-25 05:07:17 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac1e2356e5 | ||
|
|
5de98a6775 |
No files matched your search
@@ -157,7 +157,12 @@ enum error_code_i {
|
||||
// Simulate
|
||||
rpcTX_SIGNED = 97,
|
||||
|
||||
rpcLAST = rpcTX_SIGNED // rpcLAST should always equal the last code.
|
||||
// ledger_entry
|
||||
rpcENTRY_NOT_FOUND = 98,
|
||||
rpcUNEXPECTED_LEDGER_TYPE = 99,
|
||||
|
||||
rpcLAST =
|
||||
rpcUNEXPECTED_LEDGER_TYPE // rpcLAST should always equal the last code.
|
||||
};
|
||||
|
||||
/** Codes returned in the `warnings` array of certain RPC commands.
|
||||
|
||||
@@ -84,6 +84,9 @@ std::uint16_t constexpr maxTransferFee = 50000;
|
||||
/** The maximum length of a URI inside an NFT */
|
||||
std::size_t constexpr maxTokenURILength = 256;
|
||||
|
||||
/** The maximum length of a URI inside an URIToken */
|
||||
std::size_t constexpr maxURITokenURILength = 256;
|
||||
|
||||
/** The maximum length of a Data element inside a DID */
|
||||
std::size_t constexpr maxDIDDocumentLength = 256;
|
||||
|
||||
|
||||
@@ -100,11 +100,15 @@ JSS(isVLEncoded); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(Invalid); //
|
||||
JSS(Issuer); // in: Credential transactions
|
||||
JSS(IssuingChainDoor); // field.
|
||||
JSS(IssuingChainIssue); // field.
|
||||
JSS(InvoiceID); // field
|
||||
JSS(LastLedgerSequence); // in: TransactionSign; field
|
||||
JSS(FirstLedgerSequence); // in: TransactionSign; field
|
||||
JSS(LastUpdateTime); // field.
|
||||
JSS(LimitAmount); // field.
|
||||
JSS(LockingChainDoor); // field.
|
||||
JSS(LockingChainIssue); // field.
|
||||
JSS(NetworkID); // field.
|
||||
JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens
|
||||
JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/beast/core/detect_ssl.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
@@ -61,9 +60,6 @@ private:
|
||||
std::vector<char> recv_buffer_;
|
||||
endpoint_type local_endpoint_; // Store TCP-style endpoint
|
||||
|
||||
// Large enough for one maximum-size datagram (see do_send).
|
||||
static constexpr int minSendBufferSize = 256 * 1024;
|
||||
|
||||
public:
|
||||
UDPDoor(
|
||||
Handler& handler,
|
||||
@@ -101,27 +97,6 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// Some platforms (macOS: net.inet.udp.maxdgram = 9216) cap outgoing
|
||||
// datagrams at SO_SNDBUF, so a full-size fragment fails with
|
||||
// EMSGSIZE unless the send buffer is at least that large.
|
||||
{
|
||||
boost::asio::socket_base::send_buffer_size current;
|
||||
socket_.get_option(current, ec);
|
||||
if (!ec && current.value() < minSendBufferSize)
|
||||
{
|
||||
socket_.set_option(
|
||||
boost::asio::socket_base::send_buffer_size(
|
||||
minSendBufferSize),
|
||||
ec);
|
||||
if (ec)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "UDP set send_buffer_size failed: " << ec.message();
|
||||
}
|
||||
}
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
socket_.bind(udp_endpoint, ec);
|
||||
if (ec)
|
||||
{
|
||||
@@ -199,50 +174,22 @@ private:
|
||||
std::string(recv_buffer_.data(), bytes_transferred),
|
||||
tcp_endpoint,
|
||||
port_,
|
||||
[self = this->shared_from_this(),
|
||||
tcp_endpoint](std::string const& response) {
|
||||
self->do_send(response, tcp_endpoint);
|
||||
[this, tcp_endpoint](std::string const& response) {
|
||||
do_send(response, tcp_endpoint);
|
||||
});
|
||||
|
||||
do_receive();
|
||||
}
|
||||
|
||||
// The payload must stay alive until the send completes, and the socket
|
||||
// is only touched on the strand.
|
||||
void
|
||||
send_datagram(
|
||||
std::shared_ptr<std::string const> payload,
|
||||
boost::asio::ip::udp::endpoint const& udp_endpoint)
|
||||
{
|
||||
boost::asio::post(
|
||||
strand_,
|
||||
[this,
|
||||
self = this->shared_from_this(),
|
||||
payload = std::move(payload),
|
||||
udp_endpoint]() {
|
||||
if (!socket_.is_open())
|
||||
return;
|
||||
|
||||
socket_.async_send_to(
|
||||
boost::asio::buffer(*payload),
|
||||
udp_endpoint,
|
||||
boost::asio::bind_executor(
|
||||
strand_,
|
||||
[self, payload](
|
||||
error_code ec, std::size_t bytes_transferred) {
|
||||
if (ec &&
|
||||
ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
JLOG(self->j_.error())
|
||||
<< "UDP send failed: " << ec.message();
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
do_send(std::string const& response, endpoint_type const& tcp_endpoint)
|
||||
{
|
||||
if (!socket_.is_open())
|
||||
{
|
||||
std::cout << "UDP SOCKET NOT OPEN WHEN SENDING\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t HEADER_SIZE = 16;
|
||||
const size_t MAX_DATAGRAM_SIZE =
|
||||
65487; // Allow for ipv6 header 40 bytes + 8 bytes of udp header
|
||||
@@ -255,8 +202,19 @@ private:
|
||||
// If message fits in single datagram, send normally
|
||||
if (response.length() <= MAX_DATAGRAM_SIZE)
|
||||
{
|
||||
send_datagram(
|
||||
std::make_shared<std::string const>(response), udp_endpoint);
|
||||
socket_.async_send_to(
|
||||
boost::asio::buffer(response),
|
||||
udp_endpoint,
|
||||
boost::asio::bind_executor(
|
||||
strand_,
|
||||
[this, self = this->shared_from_this()](
|
||||
error_code ec, std::size_t bytes_transferred) {
|
||||
if (ec && ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
JLOG(j_.error())
|
||||
<< "UDP send failed: " << ec.message();
|
||||
}
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,39 +233,51 @@ private:
|
||||
// Send fragmented packets
|
||||
for (uint16_t packet_num = 0; packet_num < total_packets; packet_num++)
|
||||
{
|
||||
auto fragment = std::make_shared<std::string>();
|
||||
fragment->reserve(MAX_DATAGRAM_SIZE);
|
||||
std::string fragment;
|
||||
fragment.reserve(MAX_DATAGRAM_SIZE);
|
||||
|
||||
// Add header - 4 bytes of zeros
|
||||
fragment->push_back(0);
|
||||
fragment->push_back(0);
|
||||
fragment->push_back(0);
|
||||
fragment->push_back(0);
|
||||
fragment.push_back(0);
|
||||
fragment.push_back(0);
|
||||
fragment.push_back(0);
|
||||
fragment.push_back(0);
|
||||
|
||||
// Add packet number (little endian)
|
||||
fragment->push_back(packet_num & 0xFF);
|
||||
fragment->push_back((packet_num >> 8) & 0xFF);
|
||||
fragment.push_back(packet_num & 0xFF);
|
||||
fragment.push_back((packet_num >> 8) & 0xFF);
|
||||
|
||||
// Add total packets (little endian)
|
||||
fragment->push_back(total_packets & 0xFF);
|
||||
fragment->push_back((total_packets >> 8) & 0xFF);
|
||||
fragment.push_back(total_packets & 0xFF);
|
||||
fragment.push_back((total_packets >> 8) & 0xFF);
|
||||
|
||||
// Add timestamp (8 bytes, little endian)
|
||||
fragment->push_back(timestamp & 0xFF);
|
||||
fragment->push_back((timestamp >> 8) & 0xFF);
|
||||
fragment->push_back((timestamp >> 16) & 0xFF);
|
||||
fragment->push_back((timestamp >> 24) & 0xFF);
|
||||
fragment->push_back((timestamp >> 32) & 0xFF);
|
||||
fragment->push_back((timestamp >> 40) & 0xFF);
|
||||
fragment->push_back((timestamp >> 48) & 0xFF);
|
||||
fragment->push_back((timestamp >> 56) & 0xFF);
|
||||
fragment.push_back(timestamp & 0xFF);
|
||||
fragment.push_back((timestamp >> 8) & 0xFF);
|
||||
fragment.push_back((timestamp >> 16) & 0xFF);
|
||||
fragment.push_back((timestamp >> 24) & 0xFF);
|
||||
fragment.push_back((timestamp >> 32) & 0xFF);
|
||||
fragment.push_back((timestamp >> 40) & 0xFF);
|
||||
fragment.push_back((timestamp >> 48) & 0xFF);
|
||||
fragment.push_back((timestamp >> 56) & 0xFF);
|
||||
|
||||
// Calculate payload slice
|
||||
size_t start = packet_num * payload_size;
|
||||
size_t length = std::min(payload_size, response.length() - start);
|
||||
fragment->append(response.substr(start, length));
|
||||
fragment.append(response.substr(start, length));
|
||||
|
||||
send_datagram(std::move(fragment), udp_endpoint);
|
||||
socket_.async_send_to(
|
||||
boost::asio::buffer(fragment),
|
||||
udp_endpoint,
|
||||
boost::asio::bind_executor(
|
||||
strand_,
|
||||
[this, self = this->shared_from_this()](
|
||||
error_code ec, std::size_t bytes_transferred) {
|
||||
if (ec && ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
JLOG(j_.error())
|
||||
<< "UDP send failed: " << ec.message();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <xrpl/json/json_writer.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Json {
|
||||
|
||||
const Value Value::null;
|
||||
@@ -672,7 +674,9 @@ Value::isConvertibleTo(ValueType other) const
|
||||
(other == intValue && value_.real_ >= minInt &&
|
||||
value_.real_ <= maxInt) ||
|
||||
(other == uintValue && value_.real_ >= 0 &&
|
||||
value_.real_ <= maxUInt) ||
|
||||
value_.real_ <= maxUInt &&
|
||||
std::fabs(round(value_.real_) - value_.real_) <
|
||||
std::numeric_limits<double>::epsilon()) ||
|
||||
other == realValue || other == stringValue ||
|
||||
other == booleanValue;
|
||||
|
||||
|
||||
@@ -113,7 +113,10 @@ constexpr static ErrorInfo unorderedErrorInfos[]{
|
||||
{rpcLEDGER_MISSING, "ledgerMissing", "One or more ledgers in the specified range is missing", 406},
|
||||
{rpcORACLE_MALFORMED, "oracleMalformed", "Oracle request is malformed.", 400},
|
||||
{rpcBAD_CREDENTIALS, "badCredentials", "Credentials do not exist, are not accepted, or have expired.", 400},
|
||||
{rpcTX_SIGNED, "transactionSigned", "Transaction should not be signed.", 400}};
|
||||
{rpcTX_SIGNED, "transactionSigned", "Transaction should not be signed.", 400},
|
||||
{rpcENTRY_NOT_FOUND, "entryNotFound", "Entry not found.", 400},
|
||||
{rpcUNEXPECTED_LEDGER_TYPE, "unexpectedLedgerType", "Unexpected ledger type.", 400},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Sort and validate unorderedErrorInfos at compile time. Should be
|
||||
|
||||
@@ -95,12 +95,10 @@ STXChainBridge::STXChainBridge(SField const& name, Json::Value const& v)
|
||||
};
|
||||
checkExtra(v);
|
||||
|
||||
Json::Value const& lockingChainDoorStr =
|
||||
v[sfLockingChainDoor.getJsonName()];
|
||||
Json::Value const& lockingChainIssue = v[sfLockingChainIssue.getJsonName()];
|
||||
Json::Value const& issuingChainDoorStr =
|
||||
v[sfIssuingChainDoor.getJsonName()];
|
||||
Json::Value const& issuingChainIssue = v[sfIssuingChainIssue.getJsonName()];
|
||||
Json::Value const& lockingChainDoorStr = v[jss::LockingChainDoor];
|
||||
Json::Value const& lockingChainIssue = v[jss::LockingChainIssue];
|
||||
Json::Value const& issuingChainDoorStr = v[jss::IssuingChainDoor];
|
||||
Json::Value const& issuingChainIssue = v[jss::IssuingChainIssue];
|
||||
|
||||
if (!lockingChainDoorStr.isString())
|
||||
{
|
||||
@@ -158,10 +156,10 @@ Json::Value
|
||||
STXChainBridge::getJson(JsonOptions jo) const
|
||||
{
|
||||
Json::Value v;
|
||||
v[sfLockingChainDoor.getJsonName()] = lockingChainDoor_.getJson(jo);
|
||||
v[sfLockingChainIssue.getJsonName()] = lockingChainIssue_.getJson(jo);
|
||||
v[sfIssuingChainDoor.getJsonName()] = issuingChainDoor_.getJson(jo);
|
||||
v[sfIssuingChainIssue.getJsonName()] = issuingChainIssue_.getJson(jo);
|
||||
v[jss::LockingChainDoor] = lockingChainDoor_.getJson(jo);
|
||||
v[jss::LockingChainIssue] = lockingChainIssue_.getJson(jo);
|
||||
v[jss::IssuingChainDoor] = issuingChainDoor_.getJson(jo);
|
||||
v[jss::IssuingChainIssue] = issuingChainIssue_.getJson(jo);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ bridge(
|
||||
Issue const& issuingChainIssue)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
||||
jv[jss::LockingChainDoor] = lockingChainDoor.human();
|
||||
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
|
||||
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
|
||||
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
|
||||
return jv;
|
||||
}
|
||||
|
||||
@@ -60,10 +60,10 @@ bridge_rpc(
|
||||
Issue const& issuingChainIssue)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
||||
jv[jss::LockingChainDoor] = lockingChainDoor.human();
|
||||
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
|
||||
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
|
||||
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
|
||||
return jv;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+4
-3230
File diff suppressed because it is too large.
Load diff
@@ -37,8 +37,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -500,151 +498,6 @@ public:
|
||||
run(false);
|
||||
}
|
||||
|
||||
// Answers every datagram through sendResponse: "big" gets a reply too
|
||||
// large for one datagram, anything else gets "pong".
|
||||
struct UDPEchoHandler : TestHandler
|
||||
{
|
||||
// Two fragments: small enough not to overflow a default socket buffer.
|
||||
static constexpr std::size_t bigSize = 100000;
|
||||
|
||||
void
|
||||
onUDPMessage(
|
||||
std::string const& message,
|
||||
boost::asio::ip::tcp::endpoint const& remoteEndpoint,
|
||||
Port const& port,
|
||||
std::function<void(std::string const&)> sendResponse)
|
||||
{
|
||||
if (message == "big")
|
||||
{
|
||||
std::string big(bigSize, '\0');
|
||||
for (std::size_t i = 0; i < big.size(); ++i)
|
||||
big[i] = static_cast<char>('a' + (i % 26));
|
||||
sendResponse(big);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendResponse("pong");
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
expectedBig()
|
||||
{
|
||||
std::string big(bigSize, '\0');
|
||||
for (std::size_t i = 0; i < big.size(); ++i)
|
||||
big[i] = static_cast<char>('a' + (i % 26));
|
||||
return big;
|
||||
}
|
||||
};
|
||||
|
||||
// Exercises UDPDoor::do_send / send_datagram: a short reply goes out as
|
||||
// one datagram, a long one is split into headered fragments.
|
||||
void
|
||||
udpResponseTests()
|
||||
{
|
||||
testcase("UDP RPC door responses");
|
||||
using namespace std::chrono_literals;
|
||||
using udp = boost::asio::ip::udp;
|
||||
|
||||
TestSink sink{*this};
|
||||
TestThread thread;
|
||||
sink.threshold(beast::severities::Severity::kAll);
|
||||
beast::Journal journal{sink};
|
||||
|
||||
auto const addr = boost::asio::ip::make_address(getEnvLocalhostAddr());
|
||||
|
||||
// UDPDoor reports the configured port, not the bound one, so pick a
|
||||
// free port up front.
|
||||
std::uint16_t port = 0;
|
||||
{
|
||||
boost::asio::io_context io;
|
||||
udp::socket probe(io, udp::endpoint(addr, 0));
|
||||
port = probe.local_endpoint().port();
|
||||
}
|
||||
|
||||
UDPEchoHandler handler;
|
||||
auto s = make_Server(handler, thread.get_io_context(), journal);
|
||||
|
||||
std::vector<Port> ports(1);
|
||||
ports.back().name = "udp_rpc";
|
||||
ports.back().ip = addr;
|
||||
ports.back().port = port;
|
||||
ports.back().protocol.insert("udp");
|
||||
s->ports(ports);
|
||||
|
||||
boost::asio::io_context io;
|
||||
udp::socket client(io, udp::endpoint(addr, 0));
|
||||
// Room for the full fragmented reply on platforms with small
|
||||
// default socket buffers.
|
||||
client.set_option(udp::socket::receive_buffer_size(512 * 1024));
|
||||
udp::endpoint const server(addr, port);
|
||||
|
||||
// Receive one datagram, or nothing if the timeout expires.
|
||||
auto receive = [&](std::chrono::milliseconds timeout)
|
||||
-> std::optional<std::string> {
|
||||
std::vector<char> buf(70000);
|
||||
udp::endpoint from;
|
||||
std::optional<std::string> result;
|
||||
client.async_receive_from(
|
||||
boost::asio::buffer(buf),
|
||||
from,
|
||||
[&](boost::system::error_code ec, std::size_t n) {
|
||||
if (!ec)
|
||||
result.emplace(buf.data(), n);
|
||||
});
|
||||
io.restart();
|
||||
io.run_for(timeout);
|
||||
if (!result)
|
||||
{
|
||||
client.cancel();
|
||||
io.restart();
|
||||
io.run();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Single datagram reply
|
||||
client.send_to(boost::asio::buffer(std::string("ping")), server);
|
||||
if (auto const r = receive(5s); BEAST_EXPECT(r))
|
||||
BEAST_EXPECT(*r == "pong");
|
||||
|
||||
// Fragmented reply
|
||||
client.send_to(boost::asio::buffer(std::string("big")), server);
|
||||
std::size_t constexpr headerSize = 16;
|
||||
std::size_t constexpr payloadSize = 65487 - headerSize;
|
||||
std::size_t const expectedPackets =
|
||||
(UDPEchoHandler::bigSize + payloadSize - 1) / payloadSize;
|
||||
|
||||
std::map<std::uint16_t, std::string> fragments;
|
||||
std::uint16_t total = 0;
|
||||
while (fragments.size() < expectedPackets)
|
||||
{
|
||||
auto const r = receive(5s);
|
||||
if (!BEAST_EXPECT(r) || !BEAST_EXPECT(r->size() > headerSize))
|
||||
break;
|
||||
auto const byte = [&](std::size_t i) {
|
||||
return static_cast<std::uint16_t>(
|
||||
static_cast<unsigned char>((*r)[i]));
|
||||
};
|
||||
BEAST_EXPECT(byte(0) == 0 && byte(1) == 0);
|
||||
BEAST_EXPECT(byte(2) == 0 && byte(3) == 0);
|
||||
std::uint16_t const num = byte(4) | (byte(5) << 8);
|
||||
total = byte(6) | (byte(7) << 8);
|
||||
fragments[num] = r->substr(headerSize);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(total == expectedPackets);
|
||||
if (BEAST_EXPECT(fragments.size() == expectedPackets))
|
||||
{
|
||||
std::string reassembled;
|
||||
for (auto const& [num, data] : fragments)
|
||||
reassembled += data;
|
||||
BEAST_EXPECT(reassembled == UDPEchoHandler::expectedBig());
|
||||
}
|
||||
|
||||
s = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
testBadConfig()
|
||||
{
|
||||
@@ -789,7 +642,6 @@ public:
|
||||
basicTests();
|
||||
stressTest();
|
||||
udpHighwayTests();
|
||||
udpResponseTests();
|
||||
testBadConfig();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ URIToken::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const uri = ctx.tx.getFieldVL(sfURI);
|
||||
|
||||
if (uri.size() < 1 || uri.size() > 256)
|
||||
if (uri.size() < 1 || uri.size() > maxURITokenURILength)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Malformed transaction. URI must be at least 1 "
|
||||
|
||||
@@ -193,7 +193,7 @@ getAccountObjects(
|
||||
|
||||
auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue);
|
||||
|
||||
// this is a mutable version of limit, used to seemlessly switch
|
||||
// this is a mutable version of limit, used to seamlessly switch
|
||||
// to iterating directory entries when nftokenpages are exhausted
|
||||
uint32_t mlimit = limit;
|
||||
|
||||
@@ -459,7 +459,7 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
||||
indexValue = legacyLedger;
|
||||
}
|
||||
|
||||
if (hashValue)
|
||||
if (!hashValue.isNull())
|
||||
{
|
||||
if (!hashValue.isString())
|
||||
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||
@@ -470,6 +470,9 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
||||
return getLedger(ledger, ledgerHash, context);
|
||||
}
|
||||
|
||||
if (!indexValue.isConvertibleTo(Json::stringValue))
|
||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
|
||||
auto const index = indexValue.asString();
|
||||
|
||||
if (index == "current" || index.empty())
|
||||
@@ -481,11 +484,11 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
||||
if (index == "closed")
|
||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||
|
||||
std::uint32_t iVal;
|
||||
if (beast::lexicalCastChecked(iVal, index))
|
||||
return getLedger(ledger, iVal, context);
|
||||
std::uint32_t val;
|
||||
if (!beast::lexicalCastChecked(val, index))
|
||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
|
||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
return getLedger(ledger, val, context);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -701,7 +704,7 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// Explicit instantiaion of above three functions
|
||||
// Explicit instantiation of above three functions
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context&);
|
||||
|
||||
|
||||
@@ -1126,10 +1126,6 @@ ServerHandler::onUDPMessage(
|
||||
if (port.has_peer())
|
||||
return app_.overlay().processXUSH(message, remoteEndpoint);
|
||||
|
||||
if (!ipAllowed(
|
||||
remoteEndpoint.address(), port.admin_nets_v4, port.admin_nets_v6))
|
||||
return;
|
||||
|
||||
Json::Value jv;
|
||||
if (message.size() > RPC::Tuning::maxRequestSize ||
|
||||
!Json::Reader{}.parse(message, jv) || !jv.isObject())
|
||||
|
||||
+727
-1004
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,299 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2025 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/json/json_errors.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace LedgerEntryHelpers {
|
||||
|
||||
Unexpected<Json::Value>
|
||||
missingFieldError(
|
||||
Json::StaticString const field,
|
||||
std::optional<std::string> err = std::nullopt)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
auto error = RPC::missing_field_message(std::string(field.c_str()));
|
||||
json[jss::error] = err.value_or("malformedRequest");
|
||||
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||
json[jss::error_message] = std::move(error);
|
||||
return Unexpected(json);
|
||||
}
|
||||
|
||||
Unexpected<Json::Value>
|
||||
invalidFieldError(
|
||||
std::string const& err,
|
||||
Json::StaticString const field,
|
||||
std::string const& type)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
auto error = RPC::expected_field_message(field, type);
|
||||
json[jss::error] = err;
|
||||
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||
json[jss::error_message] = std::move(error);
|
||||
return Unexpected(json);
|
||||
}
|
||||
|
||||
Unexpected<Json::Value>
|
||||
malformedError(std::string const& err, std::string const& message)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
json[jss::error] = err;
|
||||
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||
json[jss::error_message] = message;
|
||||
return Unexpected(json);
|
||||
}
|
||||
|
||||
Expected<bool, Json::Value>
|
||||
hasRequired(
|
||||
Json::Value const& params,
|
||||
std::initializer_list<Json::StaticString> fields,
|
||||
std::optional<std::string> err = std::nullopt)
|
||||
{
|
||||
for (auto const field : fields)
|
||||
{
|
||||
if (!params.isMember(field) || params[field].isNull())
|
||||
{
|
||||
return missingFieldError(field, err);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<T>
|
||||
parse(Json::Value const& param);
|
||||
|
||||
template <class T>
|
||||
Expected<T, Json::Value>
|
||||
required(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::string const& err,
|
||||
std::string const& expectedType)
|
||||
{
|
||||
if (!params.isMember(fieldName) || params[fieldName].isNull())
|
||||
{
|
||||
return missingFieldError(fieldName);
|
||||
}
|
||||
if (auto obj = parse<T>(params[fieldName]))
|
||||
{
|
||||
return *obj;
|
||||
}
|
||||
return invalidFieldError(err, fieldName, expectedType);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<AccountID>
|
||||
parse(Json::Value const& param)
|
||||
{
|
||||
if (!param.isString())
|
||||
return std::nullopt;
|
||||
|
||||
auto const account = parseBase58<AccountID>(param.asString());
|
||||
if (!account || account->isZero())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
Expected<AccountID, Json::Value>
|
||||
requiredAccountID(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::string const& err)
|
||||
{
|
||||
return required<AccountID>(params, fieldName, err, "AccountID");
|
||||
}
|
||||
|
||||
std::optional<Blob>
|
||||
parseHexBlob(Json::Value const& param, std::size_t maxLength)
|
||||
{
|
||||
if (!param.isString())
|
||||
return std::nullopt;
|
||||
|
||||
auto const blob = strUnHex(param.asString());
|
||||
if (!blob || blob->empty() || blob->size() > maxLength)
|
||||
return std::nullopt;
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
Expected<Blob, Json::Value>
|
||||
requiredHexBlob(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::size_t maxLength,
|
||||
std::string const& err)
|
||||
{
|
||||
if (!params.isMember(fieldName) || params[fieldName].isNull())
|
||||
{
|
||||
return missingFieldError(fieldName);
|
||||
}
|
||||
if (auto blob = parseHexBlob(params[fieldName], maxLength))
|
||||
{
|
||||
return *blob;
|
||||
}
|
||||
return invalidFieldError(err, fieldName, "hex string");
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<std::uint32_t>
|
||||
parse(Json::Value const& param)
|
||||
{
|
||||
if (param.isUInt() || (param.isInt() && param.asInt() >= 0))
|
||||
return param.asUInt();
|
||||
|
||||
if (param.isString())
|
||||
{
|
||||
std::uint32_t v;
|
||||
if (beast::lexicalCastChecked(v, param.asString()))
|
||||
return v;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Expected<std::uint32_t, Json::Value>
|
||||
requiredUInt32(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::string const& err)
|
||||
{
|
||||
return required<std::uint32_t>(params, fieldName, err, "number");
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<uint256>
|
||||
parse(Json::Value const& param)
|
||||
{
|
||||
uint256 uNodeIndex;
|
||||
if (!param.isString() || !uNodeIndex.parseHex(param.asString()))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return uNodeIndex;
|
||||
}
|
||||
|
||||
Expected<uint256, Json::Value>
|
||||
requiredUInt256(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::string const& err)
|
||||
{
|
||||
return required<uint256>(params, fieldName, err, "hex string");
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<uint192>
|
||||
parse(Json::Value const& param)
|
||||
{
|
||||
uint192 field;
|
||||
if (!param.isString() || !field.parseHex(param.asString()))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<uint192, Json::Value>
|
||||
requiredUInt192(
|
||||
Json::Value const& params,
|
||||
Json::StaticString const fieldName,
|
||||
std::string const& err)
|
||||
{
|
||||
return required<uint192>(params, fieldName, err, "Hash192");
|
||||
}
|
||||
|
||||
Expected<STXChainBridge, Json::Value>
|
||||
parseBridgeFields(Json::Value const& params)
|
||||
{
|
||||
if (auto const value = hasRequired(
|
||||
params,
|
||||
{jss::LockingChainDoor,
|
||||
jss::LockingChainIssue,
|
||||
jss::IssuingChainDoor,
|
||||
jss::IssuingChainIssue});
|
||||
!value)
|
||||
{
|
||||
return Unexpected(value.error());
|
||||
}
|
||||
|
||||
auto const lockingChainDoor = requiredAccountID(
|
||||
params, jss::LockingChainDoor, "malformedLockingChainDoor");
|
||||
if (!lockingChainDoor)
|
||||
{
|
||||
return Unexpected(lockingChainDoor.error());
|
||||
}
|
||||
|
||||
auto const issuingChainDoor = requiredAccountID(
|
||||
params, jss::IssuingChainDoor, "malformedIssuingChainDoor");
|
||||
if (!issuingChainDoor)
|
||||
{
|
||||
return Unexpected(issuingChainDoor.error());
|
||||
}
|
||||
|
||||
Issue lockingChainIssue;
|
||||
try
|
||||
{
|
||||
lockingChainIssue = issueFromJson(params[jss::LockingChainIssue]);
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
return invalidFieldError(
|
||||
"malformedIssue", jss::LockingChainIssue, "Issue");
|
||||
}
|
||||
|
||||
Issue issuingChainIssue;
|
||||
try
|
||||
{
|
||||
issuingChainIssue = issueFromJson(params[jss::IssuingChainIssue]);
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
return invalidFieldError(
|
||||
"malformedIssue", jss::IssuingChainIssue, "Issue");
|
||||
}
|
||||
|
||||
return STXChainBridge(
|
||||
*lockingChainDoor,
|
||||
lockingChainIssue,
|
||||
*issuingChainDoor,
|
||||
issuingChainIssue);
|
||||
}
|
||||
|
||||
} // namespace LedgerEntryHelpers
|
||||
|
||||
} // namespace ripple
|
||||
Reference in new issue
Block a user