This commit is contained in:
tequ
2025-09-05 21:12:17 +09:00
parent c732609f46
commit 4a36ca527e
4 changed files with 76 additions and 73 deletions

View File

@@ -1,9 +1,4 @@
// A decoupled Hook host API helper for programmatic use and testing.
// Provides selected Hook APIs (emit-related and dependencies) without any
// dependency on WASM memory. It operates directly on ripple types.
#pragma once
#include <ripple/app/hook/Enum.h>
#include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/basics/Blob.h>
#include <ripple/basics/Expected.h>
@@ -14,6 +9,8 @@
#include <cstdint>
namespace hook {
using namespace ripple;
using HookReturnCode = hook_api::hook_return_code;
struct HookContext; // defined in applyHook.h
@@ -26,21 +23,21 @@ public:
// Emit a transaction from the running hook. On success, returns 32-byte
// transaction ID bytes (same content written by the wasm host function).
ripple::Expected<std::shared_ptr<ripple::Transaction>, std::int64_t>
emit(ripple::Slice txBlob);
Expected<std::shared_ptr<Transaction>, HookReturnCode>
emit(Slice txBlob);
// Dependencies (public so callers can compose):
// etxn_generation == otxn_generation() + 1
std::int64_t
uint32_t
etxn_generation() const;
std::int64_t
Expected<uint64_t, HookReturnCode>
etxn_burden() const;
std::int64_t
etxn_fee_base(ripple::Slice txBlob) const;
Expected<uint64_t, HookReturnCode>
etxn_fee_base(Slice txBlob) const;
std::int64_t
uint32_t
otxn_generation() const;
std::int64_t
uint64_t
otxn_burden() const;
private:

View File

@@ -16,7 +16,7 @@ namespace hook {
using namespace ripple;
std::int64_t
uint64_t
HookAPI::otxn_burden() const
{
auto& applyCtx = hookCtx.applyCtx;
@@ -41,13 +41,13 @@ HookAPI::otxn_burden() const
return 1;
}
std::uint64_t burden = pd.getFieldU64(sfEmitBurden);
uint64_t burden = pd.getFieldU64(sfEmitBurden);
burden &= ((1ULL << 63) - 1);
hookCtx.burden = burden;
return static_cast<std::int64_t>(burden);
return static_cast<int64_t>(burden);
}
std::int64_t
uint32_t
HookAPI::otxn_generation() const
{
auto& applyCtx = hookCtx.applyCtx;
@@ -76,34 +76,34 @@ HookAPI::otxn_generation() const
return hookCtx.generation;
}
std::int64_t
uint32_t
HookAPI::etxn_generation() const
{
return otxn_generation() + 1;
}
std::int64_t
Expected<uint64_t, HookReturnCode>
HookAPI::etxn_burden() const
{
if (hookCtx.expected_etxn_count <= -1)
return hook_api::PREREQUISITE_NOT_MET;
return Unexpected(hook_api::PREREQUISITE_NOT_MET);
std::uint64_t last_burden = static_cast<std::uint64_t>(otxn_burden());
std::uint64_t burden =
last_burden * static_cast<std::uint64_t>(hookCtx.expected_etxn_count);
uint64_t last_burden = static_cast<uint64_t>(otxn_burden());
uint64_t burden =
last_burden * static_cast<uint64_t>(hookCtx.expected_etxn_count);
if (burden < last_burden)
return hook_api::FEE_TOO_LARGE;
return static_cast<std::int64_t>(burden);
return Unexpected(hook_api::FEE_TOO_LARGE);
return burden;
}
std::int64_t
Expected<uint64_t, HookReturnCode>
HookAPI::etxn_fee_base(ripple::Slice txBlob) const
{
auto& applyCtx = hookCtx.applyCtx;
auto j = applyCtx.app.journal("View");
if (hookCtx.expected_etxn_count <= -1)
return hook_api::PREREQUISITE_NOT_MET;
return Unexpected(hook_api::PREREQUISITE_NOT_MET);
try
{
@@ -118,11 +118,11 @@ HookAPI::etxn_fee_base(ripple::Slice txBlob) const
{
JLOG(j.trace()) << "HookInfo[" << HC_ACC()
<< "]: etxn_fee_base exception: " << e.what();
return hook_api::INVALID_TXN;
return Unexpected(hook_api::INVALID_TXN);
}
}
ripple::Expected<std::shared_ptr<Transaction>, std::int64_t>
Expected<std::shared_ptr<Transaction>, HookReturnCode>
HookAPI::emit(ripple::Slice txBlob)
{
auto& applyCtx = hookCtx.applyCtx;
@@ -130,10 +130,10 @@ HookAPI::emit(ripple::Slice txBlob)
auto& view = applyCtx.view();
if (hookCtx.expected_etxn_count < 0)
return ripple::Unexpected<std::int64_t>(hook_api::PREREQUISITE_NOT_MET);
return Unexpected(hook_api::PREREQUISITE_NOT_MET);
if (hookCtx.result.emittedTxn.size() >= hookCtx.expected_etxn_count)
return ripple::Unexpected<std::int64_t>(hook_api::TOO_MANY_EMITTED_TXN);
return Unexpected(hook_api::TOO_MANY_EMITTED_TXN);
std::shared_ptr<STTx const> stpTrans;
try
@@ -144,14 +144,14 @@ HookAPI::emit(ripple::Slice txBlob)
catch (std::exception const& e)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC() << "]: Failed " << e.what();
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (isPseudoTx(*stpTrans))
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Attempted to emit pseudo txn.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
ripple::TxType txType = stpTrans->getTxnType();
@@ -161,7 +161,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Hook cannot emit this txn.";
return ripple::Unexpected<std::uint64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// check the emitted txn is valid
@@ -184,7 +184,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfAccount does not match hook account";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 1: sfSequence must be present and 0
@@ -193,7 +193,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfSequence missing or non-zero";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 2: sfSigningPubKey must be present and 00...00
@@ -201,7 +201,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfSigningPubKey missing";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
auto const pk = stpTrans->getSigningPubKey();
@@ -209,7 +209,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfSigningPubKey present but wrong size";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
for (int i = 0; i < pk.size(); ++i)
@@ -217,7 +217,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfSigningPubKey present but non-zero.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 2.a: no signers
@@ -225,7 +225,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfSigners not allowed in emitted txns.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 2.b: ticketseq cannot be used
@@ -233,7 +233,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfTicketSequence not allowed in emitted txns.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 2.c sfAccountTxnID not allowed
@@ -241,7 +241,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfAccountTxnID not allowed in emitted txns.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 3: sfEmitDetails must be present and valid
@@ -249,7 +249,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitDetails missing.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
auto const& emitDetails = const_cast<ripple::STTx&>(*stpTrans)
@@ -264,7 +264,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitDetails malformed.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 8: emit generation cannot exceed 10
@@ -272,7 +272,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitGeneration was 10 or more.";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
auto const gen = emitDetails.getFieldU32(sfEmitGeneration);
@@ -286,7 +286,7 @@ HookAPI::emit(ripple::Slice txBlob)
auto const& hash = emitDetails.getFieldH256(sfEmitHookHash);
std::uint32_t gen_proper = static_cast<std::uint32_t>(etxn_generation());
uint32_t gen_proper = static_cast<uint32_t>(etxn_generation());
if (gen != gen_proper)
{
@@ -294,17 +294,17 @@ HookAPI::emit(ripple::Slice txBlob)
<< "]: sfEmitGeneration provided in EmitDetails "
<< "not correct (" << gen << ") "
<< "should be " << gen_proper;
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
std::uint64_t bur_proper = static_cast<std::uint64_t>(etxn_burden());
uint64_t bur_proper = static_cast<uint64_t>(etxn_burden().value());
if (bur != bur_proper)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitBurden provided in EmitDetails "
<< "was not correct (" << bur << ") "
<< "should be " << bur_proper;
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (pTxnID != applyCtx.tx.getTransactionID())
@@ -312,7 +312,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitParentTxnID provided in EmitDetails"
<< "was not correct";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (hookCtx.nonce_used.find(nonce) == hookCtx.nonce_used.end())
@@ -320,7 +320,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitNonce provided in EmitDetails was not "
"generated by nonce api";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (callback && *callback != hookCtx.result.account)
@@ -328,7 +328,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfEmitCallback account must be the account of "
"the emitting hook";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (hash != hookCtx.result.hookHash)
@@ -336,7 +336,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace())
<< "HookEmit[" << HC_ACC()
<< "]: sfEmitHookHash must be the hash of the emitting hook";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 4: sfTxnSignature must be absent
@@ -344,7 +344,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfTxnSignature is present but should not be";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 5: LastLedgerSeq must be present and after current ledger
@@ -352,17 +352,17 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfLastLedgerSequence missing";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
std::uint32_t tx_lls = stpTrans->getFieldU32(sfLastLedgerSequence);
std::uint32_t ledgerSeq = view.info().seq;
uint32_t tx_lls = stpTrans->getFieldU32(sfLastLedgerSequence);
uint32_t ledgerSeq = view.info().seq;
if (tx_lls < ledgerSeq + 1)
{
JLOG(j.trace())
<< "HookEmit[" << HC_ACC()
<< "]: sfLastLedgerSequence invalid (less than next ledger)";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (tx_lls > ledgerSeq + 5)
@@ -370,7 +370,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace())
<< "HookEmit[" << HC_ACC()
<< "]: sfLastLedgerSequence cannot be greater than current seq + 5";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 6
@@ -380,32 +380,32 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: sfFirstLedgerSequence must be present and <= "
"LastLedgerSequence";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// rule 7 check the emitted txn pays the appropriate fee
std::int64_t minfee = etxn_fee_base(txBlob);
int64_t minfee = etxn_fee_base(txBlob).value();
if (minfee < 0)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Fee could not be calculated";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
if (!stpTrans->isFieldPresent(sfFee))
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Fee missing from emitted tx";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
std::int64_t fee = stpTrans->getFieldAmount(sfFee).xrp().drops();
int64_t fee = stpTrans->getFieldAmount(sfFee).xrp().drops();
if (fee < minfee)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Fee less than minimum required";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
std::string reason;
@@ -415,7 +415,7 @@ HookAPI::emit(ripple::Slice txBlob)
{
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: tpTrans->getStatus() != NEW";
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
// preflight the transaction
@@ -431,7 +431,7 @@ HookAPI::emit(ripple::Slice txBlob)
JLOG(j.trace()) << "HookEmit[" << HC_ACC()
<< "]: Transaction preflight failure: "
<< preflightResult.ter;
return ripple::Unexpected<std::int64_t>(hook_api::EMISSION_FAILURE);
return Unexpected(hook_api::EMISSION_FAILURE);
}
return tpTrans;

View File

@@ -3490,7 +3490,10 @@ DEFINE_HOOK_FUNCNARG(int64_t, etxn_burden)
{
HOOK_SETUP();
hook::HookAPI api(hookCtx);
return api.etxn_burden();
auto const burden = api.etxn_burden();
if (!burden)
return burden.error();
return burden.value();
HOOK_TEARDOWN();
}
@@ -4280,7 +4283,10 @@ DEFINE_HOOK_FUNCTION(
hook::HookAPI api(hookCtx);
ripple::Slice tx{
reinterpret_cast<const void*>(read_ptr + memory), read_len};
return api.etxn_fee_base(tx);
auto const fee_base = api.etxn_fee_base(tx);
if (!fee_base)
return fee_base.error();
return fee_base.value();
HOOK_TEARDOWN();
}

View File

@@ -137,14 +137,14 @@ class [[nodiscard]] Expected
public:
template <typename U>
requires std::convertible_to<U, T> constexpr Expected(U && r)
: Base(T(std::forward<U>(r)))
: Base(boost::outcome_v2::success(T(std::forward<U>(r))))
{
}
template <typename U>
requires std::convertible_to<U, E> &&
(!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
: Base(E(std::move(e.value())))
: Base(boost::outcome_v2::failure(E(std::move(e.value()))))
{
}
@@ -220,7 +220,7 @@ public:
template <typename U>
requires std::convertible_to<U, E> &&
(!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
: Base(E(std::move(e.value())))
: Base(boost::outcome_v2::failure(E(std::move(e.value()))))
{
}