Files
rippled/src/xrpld/rpc/handlers/transaction/Submit.cpp
2026-08-04 13:46:55 +00:00

190 lines
5.8 KiB
C++

#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/Transaction.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/StringUtilities.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/resource/Fees.h>
#include <xrpl/tx/apply.h>
#include <exception>
#include <expected>
#include <functional>
#include <memory>
namespace xrpl {
static std::expected<NetworkOPs::FailHard, json::Value>
getFailHard(rpc::JsonContext const& context)
{
if (context.params.isMember(jss::fail_hard) && !context.params[jss::fail_hard].isBool())
{
return std::unexpected(rpc::expectedFieldError(jss::fail_hard, "boolean"));
}
return NetworkOPs::doFailHard(
context.params.isMember(jss::fail_hard) && context.params[jss::fail_hard].asBool());
}
// {
// tx_blob: <string> XOR tx_json: <object>,
// secret: <secret>
// }
json::Value
doSubmit(rpc::JsonContext& context)
{
context.loadType = resource::kFeeMediumBurdenRpc;
if (!context.params.isMember(jss::tx_blob))
{
auto const failType = getFailHard(context);
if (!failType)
return failType.error();
if (context.role != Role::ADMIN && !context.app.config().canSign())
return rpc::makeError(RpcNotSupported, "Signing is not supported by this server.");
auto ret = rpc::transactionSubmit(
context.params,
context.apiVersion,
*failType,
context.role,
context.ledgerMaster.getValidatedLedgerAge(),
context.app,
rpc::getProcessTxnFn(context.netOps));
ret[jss::deprecated] =
"Signing support in the 'submit' command has been "
"deprecated and will be removed in a future version "
"of the server. Please migrate to a standalone "
"signing tool.";
return ret;
}
json::Value jvResult;
auto ret = strUnHex(context.params[jss::tx_blob].asString());
if (!ret || ret->empty())
return rpcError(RpcInvalidParams);
SerialIter sitTrans(makeSlice(*ret));
std::shared_ptr<STTx const> stTx;
try
{
stTx = std::make_shared<STTx const>(std::ref(sitTrans));
}
catch (std::exception& e)
{
jvResult[jss::error] = "invalidTransaction";
jvResult[jss::error_exception] = e.what();
return jvResult;
}
{
if (!context.app.checkSigs())
{
forceValidity(
context.app.getHashRouter(), stTx->getTransactionID(), Validity::SigGoodOnly);
}
auto [validity, reason] = checkValidity(
context.app.getHashRouter(), *stTx, context.ledgerMaster.getCurrentLedger()->rules());
if (validity != Validity::Valid)
{
jvResult[jss::error] = "invalidTransaction";
jvResult[jss::error_exception] = "fails local checks: " + reason;
return jvResult;
}
}
std::string reason;
auto transaction = std::make_shared<Transaction>(stTx, reason, context.app);
if (transaction->getStatus() != TransStatus::NEW)
{
jvResult[jss::error] = "invalidTransaction";
jvResult[jss::error_exception] = "fails local checks: " + reason;
return jvResult;
}
try
{
auto const failType = getFailHard(context);
if (!failType)
return failType.error();
context.netOps.processTransaction(transaction, isUnlimited(context.role), true, *failType);
}
catch (std::exception& e)
{
jvResult[jss::error] = "internalSubmit";
jvResult[jss::error_exception] = e.what();
return jvResult;
}
try
{
jvResult[jss::tx_json] = transaction->getJson(JsonOptions::Values::None);
jvResult[jss::tx_blob] = strHex(transaction->getSTransaction()->getSerializer().peekData());
if (temUNCERTAIN != transaction->getResult())
{
std::string sToken;
std::string sHuman;
transResultInfo(transaction->getResult(), sToken, sHuman);
jvResult[jss::engine_result] = sToken;
jvResult[jss::engine_result_code] = transaction->getResult();
jvResult[jss::engine_result_message] = sHuman;
auto const submitResult = transaction->getSubmitResult();
jvResult[jss::accepted] = submitResult.any();
jvResult[jss::applied] = submitResult.applied;
jvResult[jss::broadcast] = submitResult.broadcast;
jvResult[jss::queued] = submitResult.queued;
jvResult[jss::kept] = submitResult.kept;
if (auto currentLedgerState = transaction->getCurrentLedgerState())
{
jvResult[jss::account_sequence_next] =
safeCast<json::Value::UInt>(currentLedgerState->accountSeqNext);
jvResult[jss::account_sequence_available] =
safeCast<json::Value::UInt>(currentLedgerState->accountSeqAvail);
jvResult[jss::open_ledger_cost] = to_string(currentLedgerState->minFeeRequired);
jvResult[jss::validated_ledger_index] =
safeCast<json::Value::UInt>(currentLedgerState->validatedLedger);
}
}
return jvResult;
}
catch (std::exception& e)
{
jvResult[jss::error] = "internalJson";
jvResult[jss::error_exception] = e.what();
return jvResult;
}
}
} // namespace xrpl