Add NFT RPC infrastructure

This commit is contained in:
Devon White
2022-02-24 15:22:14 -06:00
committed by CJ Cobb
parent a72aa73afe
commit 9939f6e6f4
30 changed files with 1095 additions and 734 deletions

View File

@@ -20,8 +20,7 @@ doLedgerEntry(Context const& context)
auto request = context.params;
boost::json::object response = {};
bool binary =
request.contains("binary") ? request.at("binary").as_bool() : false;
bool const binary = getBool(request, "binary", false);
auto v = ledgerInfoFromRequest(context);
if (auto status = std::get_if<Status>(&v))
@@ -30,59 +29,64 @@ doLedgerEntry(Context const& context)
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
ripple::uint256 key;
if (request.contains("index"))
if (request.contains(JS(index)))
{
if (!request.at("index").is_string())
if (!request.at(JS(index)).is_string())
return Status{Error::rpcINVALID_PARAMS, "indexNotString"};
if (!key.parseHex(request.at("index").as_string().c_str()))
if (!key.parseHex(request.at(JS(index)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedIndex"};
}
else if (request.contains("account_root"))
else if (request.contains(JS(account_root)))
{
if (!request.at("account_root").is_string())
if (!request.at(JS(account_root)).is_string())
return Status{Error::rpcINVALID_PARAMS, "account_rootNotString"};
auto const account = ripple::parseBase58<ripple::AccountID>(
request.at("account_root").as_string().c_str());
request.at(JS(account_root)).as_string().c_str());
if (!account || account->isZero())
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
else
key = ripple::keylet::account(*account).key;
}
else if (request.contains("check"))
else if (request.contains(JS(check)))
{
if (!request.at("check").is_string())
if (!request.at(JS(check)).is_string())
return Status{Error::rpcINVALID_PARAMS, "checkNotString"};
if (!key.parseHex(request.at("check").as_string().c_str()))
if (!key.parseHex(request.at(JS(check)).as_string().c_str()))
{
return Status{Error::rpcINVALID_PARAMS, "checkMalformed"};
}
}
else if (request.contains("deposit_preauth"))
else if (request.contains(JS(deposit_preauth)))
{
if (!request.at("deposit_preauth").is_object())
if (!request.at(JS(deposit_preauth)).is_object())
{
if (!request.at("deposit_preauth").is_string() ||
if (!request.at(JS(deposit_preauth)).is_string() ||
!key.parseHex(
request.at("deposit_preauth").as_string().c_str()))
request.at(JS(deposit_preauth)).as_string().c_str()))
{
return Status{
Error::rpcINVALID_PARAMS, "deposit_preauthMalformed"};
}
}
else if (
!request.at("deposit_preauth").as_object().contains("owner") ||
!request.at("deposit_preauth").as_object().at("owner").is_string())
!request.at(JS(deposit_preauth)).as_object().contains(JS(owner)) ||
!request.at(JS(deposit_preauth))
.as_object()
.at(JS(owner))
.is_string())
{
return Status{Error::rpcINVALID_PARAMS, "ownerNotString"};
}
else if (
!request.at("deposit_preauth").as_object().contains("authorized") ||
!request.at("deposit_preauth")
!request.at(JS(deposit_preauth))
.as_object()
.at("authorized")
.contains(JS(authorized)) ||
!request.at(JS(deposit_preauth))
.as_object()
.at(JS(authorized))
.is_string())
{
return Status{Error::rpcINVALID_PARAMS, "authorizedNotString"};
@@ -90,13 +94,13 @@ doLedgerEntry(Context const& context)
else
{
boost::json::object const& deposit_preauth =
request.at("deposit_preauth").as_object();
request.at(JS(deposit_preauth)).as_object();
auto const owner = ripple::parseBase58<ripple::AccountID>(
deposit_preauth.at("owner").as_string().c_str());
deposit_preauth.at(JS(owner)).as_string().c_str());
auto const authorized = ripple::parseBase58<ripple::AccountID>(
deposit_preauth.at("authorized").as_string().c_str());
deposit_preauth.at(JS(authorized)).as_string().c_str());
if (!owner)
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
@@ -106,37 +110,37 @@ doLedgerEntry(Context const& context)
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
}
}
else if (request.contains("directory"))
else if (request.contains(JS(directory)))
{
if (!request.at("directory").is_object())
if (!request.at(JS(directory)).is_object())
{
if (!request.at("directory").is_string())
if (!request.at(JS(directory)).is_string())
return Status{Error::rpcINVALID_PARAMS, "directoryNotString"};
if (!key.parseHex(request.at("directory").as_string().c_str()))
if (!key.parseHex(request.at(JS(directory)).as_string().c_str()))
{
return Status{Error::rpcINVALID_PARAMS, "malformedDirectory"};
}
}
else if (
request.at("directory").as_object().contains("sub_index") &&
!request.at("directory").as_object().at("sub_index").is_int64())
request.at(JS(directory)).as_object().contains(JS(sub_index)) &&
!request.at(JS(directory)).as_object().at(JS(sub_index)).is_int64())
{
return Status{Error::rpcINVALID_PARAMS, "sub_indexNotInt"};
}
else
{
auto directory = request.at("directory").as_object();
std::uint64_t subIndex = directory.contains("sub_index")
auto directory = request.at(JS(directory)).as_object();
std::uint64_t subIndex = directory.contains(JS(sub_index))
? boost::json::value_to<std::uint64_t>(
directory.at("sub_index"))
directory.at(JS(sub_index)))
: 0;
if (directory.contains("dir_root"))
if (directory.contains(JS(dir_root)))
{
ripple::uint256 uDirRoot;
if (directory.contains("owner"))
if (directory.contains(JS(owner)))
{
// May not specify both dir_root and owner.
return Status{
@@ -144,7 +148,7 @@ doLedgerEntry(Context const& context)
"mayNotSpecifyBothDirRootAndOwner"};
}
else if (!uDirRoot.parseHex(
directory.at("dir_root").as_string().c_str()))
directory.at(JS(dir_root)).as_string().c_str()))
{
return Status{Error::rpcINVALID_PARAMS, "malformedDirRoot"};
}
@@ -153,10 +157,10 @@ doLedgerEntry(Context const& context)
key = ripple::keylet::page(uDirRoot, subIndex).key;
}
}
else if (directory.contains("owner"))
else if (directory.contains(JS(owner)))
{
auto const ownerID = ripple::parseBase58<ripple::AccountID>(
directory.at("owner").as_string().c_str());
directory.at(JS(owner)).as_string().c_str());
if (!ownerID)
{
@@ -176,31 +180,31 @@ doLedgerEntry(Context const& context)
}
}
}
else if (request.contains("escrow"))
else if (request.contains(JS(escrow)))
{
if (!request.at("escrow").is_object())
if (!request.at(JS(escrow)).is_object())
{
if (!key.parseHex(request.at("escrow").as_string().c_str()))
if (!key.parseHex(request.at(JS(escrow)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedEscrow"};
}
else if (
!request.at("escrow").as_object().contains("owner") ||
!request.at("escrow").as_object().at("owner").is_string())
!request.at(JS(escrow)).as_object().contains(JS(owner)) ||
!request.at(JS(escrow)).as_object().at(JS(owner)).is_string())
{
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
}
else if (
!request.at("escrow").as_object().contains("seq") ||
!request.at("escrow").as_object().at("seq").is_int64())
!request.at(JS(escrow)).as_object().contains(JS(seq)) ||
!request.at(JS(escrow)).as_object().at(JS(seq)).is_int64())
{
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
}
else
{
auto const id =
ripple::parseBase58<ripple::AccountID>(request.at("escrow")
ripple::parseBase58<ripple::AccountID>(request.at(JS(escrow))
.as_object()
.at("owner")
.at(JS(owner))
.as_string()
.c_str());
@@ -209,120 +213,122 @@ doLedgerEntry(Context const& context)
else
{
std::uint32_t seq =
request.at("escrow").as_object().at("seq").as_int64();
request.at(JS(escrow)).as_object().at(JS(seq)).as_int64();
key = ripple::keylet::escrow(*id, seq).key;
}
}
}
else if (request.contains("offer"))
else if (request.contains(JS(offer)))
{
if (!request.at("offer").is_object())
if (!request.at(JS(offer)).is_object())
{
if (!key.parseHex(request.at("offer").as_string().c_str()))
if (!key.parseHex(request.at(JS(offer)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedOffer"};
}
else if (
!request.at("offer").as_object().contains("account") ||
!request.at("offer").as_object().at("account").is_string())
!request.at(JS(offer)).as_object().contains(JS(account)) ||
!request.at(JS(offer)).as_object().at(JS(account)).is_string())
{
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
}
else if (
!request.at("offer").as_object().contains("seq") ||
!request.at("offer").as_object().at("seq").is_int64())
!request.at(JS(offer)).as_object().contains(JS(seq)) ||
!request.at(JS(offer)).as_object().at(JS(seq)).is_int64())
{
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
}
else
{
auto offer = request.at("offer").as_object();
auto offer = request.at(JS(offer)).as_object();
auto const id = ripple::parseBase58<ripple::AccountID>(
offer.at("account").as_string().c_str());
offer.at(JS(account)).as_string().c_str());
if (!id)
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
else
{
std::uint32_t seq =
boost::json::value_to<std::uint32_t>(offer.at("seq"));
boost::json::value_to<std::uint32_t>(offer.at(JS(seq)));
key = ripple::keylet::offer(*id, seq).key;
}
}
}
else if (request.contains("payment_channel"))
else if (request.contains(JS(payment_channel)))
{
if (!request.at("payment_channel").is_string())
if (!request.at(JS(payment_channel)).is_string())
return Status{Error::rpcINVALID_PARAMS, "paymentChannelNotString"};
if (!key.parseHex(request.at("payment_channel").as_string().c_str()))
if (!key.parseHex(request.at(JS(payment_channel)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedPaymentChannel"};
}
else if (request.contains("ripple_state"))
else if (request.contains(JS(ripple_state)))
{
if (!request.at("ripple_state").is_object())
if (!request.at(JS(ripple_state)).is_object())
return Status{Error::rpcINVALID_PARAMS, "rippleStateNotObject"};
ripple::Currency currency;
boost::json::object const& state =
request.at("ripple_state").as_object();
request.at(JS(ripple_state)).as_object();
if (!state.contains("currency") || !state.at("currency").is_string())
if (!state.contains(JS(currency)) ||
!state.at(JS(currency)).is_string())
{
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
}
if (!state.contains("accounts") || !state.at("accounts").is_array() ||
2 != state.at("accounts").as_array().size() ||
!state.at("accounts").as_array().at(0).is_string() ||
!state.at("accounts").as_array().at(1).is_string() ||
(state.at("accounts").as_array().at(0).as_string() ==
state.at("accounts").as_array().at(1).as_string()))
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{Error::rpcINVALID_PARAMS, "malformedAccounts"};
}
auto const id1 = ripple::parseBase58<ripple::AccountID>(
state.at("accounts").as_array().at(0).as_string().c_str());
state.at(JS(accounts)).as_array().at(0).as_string().c_str());
auto const id2 = ripple::parseBase58<ripple::AccountID>(
state.at("accounts").as_array().at(1).as_string().c_str());
state.at(JS(accounts)).as_array().at(1).as_string().c_str());
if (!id1 || !id2)
return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"};
else if (!ripple::to_currency(
currency, state.at("currency").as_string().c_str()))
currency, state.at(JS(currency)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
key = ripple::keylet::line(*id1, *id2, currency).key;
}
else if (request.contains("ticket"))
else if (request.contains(JS(ticket)))
{
if (!request.at("ticket").is_object())
if (!request.at(JS(ticket)).is_object())
{
if (!request.at("ticket").is_string())
if (!request.at(JS(ticket)).is_string())
return Status{Error::rpcINVALID_PARAMS, "ticketNotString"};
if (!key.parseHex(request.at("ticket").as_string().c_str()))
if (!key.parseHex(request.at(JS(ticket)).as_string().c_str()))
return Status{Error::rpcINVALID_PARAMS, "malformedTicket"};
}
else if (
!request.at("ticket").as_object().contains("account") ||
!request.at("ticket").as_object().at("account").is_string())
!request.at(JS(ticket)).as_object().contains(JS(account)) ||
!request.at(JS(ticket)).as_object().at(JS(account)).is_string())
{
return Status{Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
}
else if (
!request.at("ticket").as_object().contains("ticket_seq") ||
!request.at("ticket").as_object().at("ticket_seq").is_int64())
!request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) ||
!request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64())
{
return Status{Error::rpcINVALID_PARAMS, "malformedTicketSeq"};
}
else
{
auto const id =
ripple::parseBase58<ripple::AccountID>(request.at("ticket")
ripple::parseBase58<ripple::AccountID>(request.at(JS(ticket))
.as_object()
.at("account")
.at(JS(account))
.as_string()
.c_str());
@@ -331,8 +337,10 @@ doLedgerEntry(Context const& context)
Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
else
{
std::uint32_t seq =
request.at("offer").as_object().at("ticket_seq").as_int64();
std::uint32_t seq = request.at(JS(offer))
.as_object()
.at(JS(ticket_seq))
.as_int64();
key = ripple::getTicketIndex(*id, seq);
}
@@ -351,19 +359,19 @@ doLedgerEntry(Context const& context)
if (!dbResponse or dbResponse->size() == 0)
return Status{Error::rpcOBJECT_NOT_FOUND, "entryNotFound"};
response["index"] = ripple::strHex(key);
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
response["ledger_index"] = lgrInfo.seq;
response[JS(index)] = ripple::strHex(key);
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
response[JS(ledger_index)] = lgrInfo.seq;
if (binary)
{
response["node_binary"] = ripple::strHex(*dbResponse);
response[JS(node_binary)] = ripple::strHex(*dbResponse);
}
else
{
ripple::STLedgerEntry sle{
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key};
response["node"] = toJson(sle);
response[JS(node)] = toJson(sle);
}
return response;