add sfData preflight checks + tests (#5839)

This commit is contained in:
Mayukha Vadari
2025-10-02 17:50:43 -04:00
committed by GitHub
parent 965a9e89ac
commit 55772a0d07
6 changed files with 167 additions and 3 deletions

View File

@@ -1730,6 +1730,14 @@ struct Escrow_test : public beast::unit_test::suite
fee(txnFees),
ter(temDISABLED));
env.close();
env(escrowCreate,
escrow::finish_function(wasmHex),
escrow::cancel_time(env.now() + 100s),
escrow::data("00112233"),
fee(txnFees),
ter(temDISABLED));
env.close();
}
{
@@ -1757,6 +1765,44 @@ struct Escrow_test : public beast::unit_test::suite
env.close();
}
{
// Data without FinishFunction
Env env(*this, features);
XRPAmount const txnFees = env.current()->fees().base + 100000;
// create escrow
env.fund(XRP(5000), alice, carol);
auto escrowCreate = escrow::create(alice, carol, XRP(500));
std::string longData(4, 'A');
env(escrowCreate,
escrow::data(longData),
escrow::finish_time(env.now() + 100s),
fee(txnFees),
ter(temMALFORMED));
env.close();
}
{
// Data > max length
Env env(*this, features);
XRPAmount const txnFees = env.current()->fees().base + 100000;
// create escrow
env.fund(XRP(5000), alice, carol);
auto escrowCreate = escrow::create(alice, carol, XRP(500));
// string of length maxWasmDataLength * 2 + 2
std::string longData(maxWasmDataLength * 2 + 2, 'B');
env(escrowCreate,
escrow::data(longData),
escrow::finish_function(wasmHex),
escrow::cancel_time(env.now() + 100s),
fee(txnFees),
ter(temMALFORMED));
env.close();
}
Env env(
*this,
envconfig([](std::unique_ptr<Config> cfg) {
@@ -2286,6 +2332,72 @@ struct Escrow_test : public beast::unit_test::suite
}
}
void
testUpdateDataOnFailure(FeatureBitset features)
{
testcase("Update escrow data on failure");
using namespace jtx;
using namespace std::chrono;
// wasm that always fails
static auto const wasmHex = updateDataWasmHex;
Account const alice{"alice"};
Account const carol{"carol"};
Env env(*this, features);
// create escrow
env.fund(XRP(5000), alice);
auto const seq = env.seq(alice);
BEAST_EXPECT(env.ownerCount(alice) == 0);
auto escrowCreate = escrow::create(alice, alice, XRP(1000));
XRPAmount txnFees =
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
env(escrowCreate,
escrow::finish_function(wasmHex),
escrow::finish_time(env.now() + 2s),
escrow::cancel_time(env.now() + 100s),
fee(txnFees));
env.close();
env.close();
env.close();
if (BEAST_EXPECT(
env.ownerCount(alice) == (1 + wasmHex.size() / 2 / 500)))
{
env.require(balance(alice, XRP(4000) - txnFees));
auto const allowance = 1'015;
XRPAmount const finishFee = env.current()->fees().base +
(allowance * env.current()->fees().gasPrice) /
MICRO_DROPS_PER_DROP +
1;
// FinishAfter time hasn't passed
env(escrow::finish(alice, alice, seq),
escrow::comp_allowance(allowance),
fee(finishFee),
ter(tecWASM_REJECTED));
auto const txMeta = env.meta();
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
BEAST_EXPECTS(
txMeta->getFieldU32(sfGasUsed) == allowance,
std::to_string(txMeta->getFieldU32(sfGasUsed)));
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
BEAST_EXPECTS(
txMeta->getFieldI32(sfWasmReturnCode) == -256,
std::to_string(txMeta->getFieldI32(sfWasmReturnCode)));
auto const sle = env.le(keylet::escrow(alice, seq));
if (BEAST_EXPECT(sle && sle->isFieldPresent(sfData)))
BEAST_EXPECTS(
checkVL(sle, sfData, "Data"),
strHex(sle->getFieldVL(sfData)));
}
}
void
testAllHostFunctions(FeatureBitset features)
{
@@ -2478,6 +2590,7 @@ struct Escrow_test : public beast::unit_test::suite
testCreateFinishFunctionPreflight(features);
testFinishWasmFailures(features);
testFinishFunction(features);
testUpdateDataOnFailure(features);
// TODO: Update module with new host functions
testAllHostFunctions(features);

View File

@@ -4,8 +4,8 @@
extern crate std;
use crate::host::{Error, Result, Result::Err, Result::Ok};
use xrpl_std::core::ledger_objects::current_escrow::CurrentEscrow;
use xrpl_std::core::ledger_objects::current_escrow::get_current_escrow;
use xrpl_std::core::ledger_objects::current_escrow::CurrentEscrow;
use xrpl_std::core::ledger_objects::ledger_object;
use xrpl_std::core::ledger_objects::traits::CurrentEscrowFields;
use xrpl_std::core::types::amount::asset::{Asset, IouAsset, XrpAsset};
@@ -13,7 +13,7 @@ use xrpl_std::core::types::amount::currency_code::CurrencyCode;
use xrpl_std::core::types::amount::mpt_id::MptId;
use xrpl_std::core::types::keylets;
use xrpl_std::host;
use xrpl_std::host::trace::{DataRepr, trace, trace_account, trace_data, trace_num};
use xrpl_std::host::trace::{trace, trace_account, trace_data, trace_num, DataRepr};
use xrpl_std::sfield;
#[unsafe(no_mangle)]

View File

@@ -10330,3 +10330,21 @@ extern std::string const disabledFloatHex =
"6503050b5f5f686561705f6261736503060a5f5f686561705f656e640307"
"0d5f5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503"
"090a150202000b100043000000c54300200045921a41010b";
extern std::string const updateDataWasmHex =
"0061736d01000000010e0360027f7f017f6000006000017f02130103656e760b7570646174"
"655f64617461000003030201020503010002063f0a7f01419088040b7f004180080b7f0041"
"85080b7f004190080b7f00419088040b7f004180080b7f00419088040b7f00418080080b7f"
"0041000b7f0041010b07aa010c066d656d6f72790200115f5f7761736d5f63616c6c5f6374"
"6f727300010666696e69736800020c5f5f64736f5f68616e646c6503010a5f5f646174615f"
"656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f"
"676c6f62616c5f6261736503050b5f5f686561705f6261736503060a5f5f686561705f656e"
"6403070d5f5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503090a3f02"
"02000b3a01017f230041106b220024002000410c6a4184082d00003a000020004180082800"
"00360208200041086a410410001a200041106a240041807e0b0b0b01004180080b04446174"
"61007f0970726f647563657273010c70726f6365737365642d62790105636c616e675f3139"
"2e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c76"
"6d2f6c6c766d2d70726f6a6563742061623462356132646235383239353861663165653330"
"3861373930636664623432626432343732302900490f7461726765745f6665617475726573"
"042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e"
"63652d74797065732b0a6d756c746976616c7565";

View File

@@ -50,3 +50,5 @@ extern std::string const floatTestsWasmHex;
extern std::string const float0Hex;
extern std::string const disabledFloatHex;
extern std::string const updateDataWasmHex;

View File

@@ -0,0 +1,13 @@
#include <stdint.h>
int32_t
update_data(uint8_t const*, int32_t);
int
finish()
{
uint8_t buf[] = "Data";
update_data(buf, sizeof(buf) - 1);
return -256;
}

View File

@@ -137,7 +137,8 @@ EscrowCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
bool
EscrowCreate::checkExtraFeatures(PreflightContext const& ctx)
{
if (ctx.tx.isFieldPresent(sfFinishFunction) &&
if ((ctx.tx.isFieldPresent(sfFinishFunction) ||
ctx.tx.isFieldPresent(sfData)) &&
!ctx.rules.enabled(featureSmartEscrow))
return false;
@@ -225,6 +226,22 @@ EscrowCreate::preflight(PreflightContext const& ctx)
return temDISABLED;
}
if (ctx.tx.isFieldPresent(sfData))
{
if (!ctx.tx.isFieldPresent(sfFinishFunction))
{
JLOG(ctx.j.debug())
<< "EscrowCreate with Data requires FinishFunction";
return temMALFORMED;
}
auto const data = ctx.tx.getFieldVL(sfData);
if (data.size() > maxWasmDataLength)
{
JLOG(ctx.j.debug()) << "EscrowCreate.Data bad size " << data.size();
return temMALFORMED;
}
}
if (ctx.tx.isFieldPresent(sfFinishFunction))
{
auto const code = ctx.tx.getFieldVL(sfFinishFunction);
@@ -1317,6 +1334,7 @@ EscrowFinish::doApply()
if (auto const& data = ledgerDataProvider.getData(); data.has_value())
{
slep->setFieldVL(sfData, makeSlice(*data));
ctx_.view().update(slep);
}
if (re.has_value())