diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 13e7471d65..3d64e0783a 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -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 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); diff --git a/src/test/app/wasm_fixtures/all_keylets/src/lib.rs b/src/test/app/wasm_fixtures/all_keylets/src/lib.rs index 970db7bd50..6171045522 100644 --- a/src/test/app/wasm_fixtures/all_keylets/src/lib.rs +++ b/src/test/app/wasm_fixtures/all_keylets/src/lib.rs @@ -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)] diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index 6ad3e33639..727ddeb4fe 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -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"; diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index 0e7e3785ea..f10b493900 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -50,3 +50,5 @@ extern std::string const floatTestsWasmHex; extern std::string const float0Hex; extern std::string const disabledFloatHex; + +extern std::string const updateDataWasmHex; diff --git a/src/test/app/wasm_fixtures/updateData.c b/src/test/app/wasm_fixtures/updateData.c new file mode 100644 index 0000000000..8436f1c390 --- /dev/null +++ b/src/test/app/wasm_fixtures/updateData.c @@ -0,0 +1,13 @@ +#include + +int32_t +update_data(uint8_t const*, int32_t); + +int +finish() +{ + uint8_t buf[] = "Data"; + update_data(buf, sizeof(buf) - 1); + + return -256; +} diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 25dcc0415a..6f73faa323 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -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())