mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-23 07:10:53 +00:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
190 lines
5.8 KiB
C++
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
|