Commit Graph

15225 Commits

Author SHA1 Message Date
Mayukha Vadari
97cbef72df Let a contract emit transactions from its own pseudo-account
checkSign rejects every pseudo-account when BatchV1_1 is enabled, but
emitted transactions need BatchV1_1 to skip the signature check. So no
contract could emit anything. Allow a contract pseudo-account on inner
transactions, and make emitBuiltTxn and emitTxn reject a transaction
whose Account is not the running contract's account. Without that
check, a contract could emit an unsigned transaction from any account.
2026-09-24 03:37:48 +05:30
Mayukha Vadari
670f1d5e6f Note the escrow and contract host functions are not isolated
The two groups share one flat ABI table in one `host_lib` namespace, and
the only thing separating them is that the escrow host leaves the contract
methods at their `Unimplemented` default. That separates them too late,
and in one direction only.

An escrow whose bytecode imports `emit_built_txn` passes screening, so
`EscrowCreate` stores it and the escrow fails at finish with
`tecINTERNAL`, because `Unimplemented` is a fatal fault rather than a code
a contract reads. A contract calling `set_data` does not fail at all: the
method is inherited, only `EscrowFinish::doApply` drains it, so the write
returns a byte count and is discarded.

Both need screening to know which subset of the ABI the host being built
for actually serves. `docs/wasm-amendment-gating.md` already designs that
transport for amendments; this wants the same one keyed on which host is
running.
2026-09-23 23:44:52 +05:30
Mayukha Vadari
cd5366dab7 Stop a contract's own mistake reading as a node fault when it emits
Both emit host functions rebuilt an `STTx`, and both were caught by the
outer guard when the rebuild threw, so a contract that got its own
transaction wrong was answered `InternalFatal`: the run stopped and the
transaction reported `tecINTERNAL`. Either was reachable from any guest.

`emit_built_txn` constructed the transaction before its try, so a field
the type requires but the contract never set threw there. The
construction moves inside the guard and answers `SubmitTxnFailure`, which
the contract can act on. It also copies rather than moves out of the
builder, so a contract told its transaction is malformed can supply what
was missing and emit the same index again.

`emit_txn` could not emit anything a contract had not already marked
`tfInnerBatchTxn`. Setting the flag meant rebuilding the transaction from
its fields, and that re-runs the format check, which rejects the defaulted
`sfPaths` an ordinary Payment carries harmlessly. The flag now goes on a
copy made through `STTx`'s own copy constructor, which does not re-check,
and through `setFieldU32` rather than `setFlag`: a transaction built
against a template holds `sfFlags` as a placeholder until something gives
it a value, and `setFlag` can only reach one that already has it.

The tests that pinned the old behavior now pin the new.
2026-09-15 19:10:16 -04:00
Mayukha Vadari
0d79820471 Document the contract layer in the wasm test README
The two byte-order conventions are the part worth writing down: a
parameter answers little-endian integers, a data read answers the
canonical big-endian serialization, and the TER an emit produces is four
little-endian bytes in an output region rather than the call's result.

Adds the four marshalling shapes the contract functions bring to the e2e
table, and the fixtures that stand a contract host up.
2026-09-15 18:54:13 -04:00
Mayukha Vadari
1109f5c344 Retire the Rust contract fixtures and the tests that could not run
`Contract_test.cpp` loaded its contract bytecode from a hardcoded path
into a developer's home directory, so on every other machine the loader
returned an empty string and the transaction was refused as malformed.
Those testcases have been failing wherever that directory does not exist,
which is everywhere. The four Rust guest crates they were meant to load
were never compiled by anything, and the script that once embedded such
fixtures is gone upstream.

What they covered now lives in the layered wasm tests: the host functions
themselves in `host_functions/`, the wire in `host_calls/`, and the
marshalling shapes end to end in `e2e/`, against WAT the suite assembles
itself. `ContractHostFuncImpl_test.cpp` goes with them; seven of its eight
testcases were already commented out.

What remains in `Contract_test.cpp` is the transactor surface the new
tests do not reach: preflight, preclaim and doApply for create, modify and
delete, driven from inline bytecode.

One of those was also failing, and for a reason of its own: the
"Functions array too large" case declared thirteen functions against a
limit of thirty-two, so the array was never too large and the transaction
fell through to the fee check. It now counts from the limit.
2026-09-15 18:50:56 -04:00
Mayukha Vadari
42904ac898 Cover the contract marshalling shapes end to end
Three shapes the contract host functions add to the e2e table, each one a
convention a guest and a host could agree about separately and still
disagree about in practice.

- A typed value written by the guest and read back without its type byte,
  answered out of the cache by a write in the same run, for an account
  whose data object is not on the ledger at all.
- A scalar index between two regions. Writing element 0 and element 1 and
  reading them apart is what shows an index taken from the wrong place;
  reading back the only element written would not.
- The builder index, which is the only contract host state that outlives
  one call, together with the TER crossing as bytes in a region rather
  than as the call's result.
- An `STJson` object the guest serialized, a different parser from the one
  a stored value goes through.

`ContractVmTest` takes the host rather than building one, because what
these tests assert is what the contract left behind, which only the
caller's `ContractHost` can be asked about afterwards.
2026-09-15 18:47:48 -04:00
Mayukha Vadari
fed023680a Pin the wire contract of every contract host function
Completes the `host_calls/` layer: a mock host behind the real VM, with
each module hand-writing its own imports, literal regions and literal
lengths, so what is tested is the wire rather than whatever an SDK sends.

The order of two same-shaped arguments is the thing worth asserting here,
because nothing downstream would notice them swapped: a nested read takes
the outer key first, and an array read takes the key before the index
while the host takes them the other way round. Each is asserted against a
value the other could not be.

What the shim refuses before the host is reached, now stated: an account
region that is not twenty bytes, a key that is not UTF-8, a negative
index, a transaction type that does not fit in sixteen bits, a field code
the protocol does not name, bytes that are not a transaction, bytes that
are not an object, and an empty value region.

`SliceIs` is added beside `BytesAre` for data that is not text. `BytesAre`
compares against a string and so stops at the first NUL, which most
serialized fields contain — an amount matched as "@" and passed.
2026-09-15 18:44:24 -04:00
Mayukha Vadari
77d8e5bf2e Test the data-shaped and transaction-shaped contract host functions
Completes the `host_functions/` layer: reads, nested objects, arrays,
nested arrays, and the two emit paths, each against a real ledger.

What the shapes turn out to mean, now asserted rather than assumed:

- An account has one data object, and the first write decides whether it
  is a map of keys or a list of elements. Mixing them is refused rather
  than silently replacing one with the other.
- The two string arguments of a nested write are the outer key then the
  inner one. Reading under the swapped pair finds nothing, which is what
  a contract that got the order wrong would see.
- Two arrays under two keys are two arrays.
- A stored integer reads back big-endian, the opposite of the
  little-endian a parameter answers with.

Two more defects are pinned rather than fixed, both the same shape as the
one in the previous commit: reconstructing an `STTx` re-runs its format
check, and the throw lands outside the guard that would have answered
`SubmitTxnFailure`. `emit_txn` cannot emit a transaction that does not
already carry `tfInnerBatchTxn`, because setting the flag means rebuilding
it, and an ordinary Payment's defaulted `sfPaths` is rejected on the way
back in. The contract is told `InternalFatal` and the run stops.

`ContractLedger` now gives each host a transaction of its own; two
identical ones share an id, which a ledger refuses to record twice.
2026-09-15 18:32:08 -04:00
Mayukha Vadari
692315043c Test the contract host functions against a real ledger
One `host_functions/` suite per function, in upstream's layering: a real
`ContractHostFunctionsImpl` over a real ledger, no VM.

What they pin beyond the happy path:

- A parameter comes back little-endian, unlike the big-endian canonical
  serialization a data read answers. Both conventions are in the ABI, so
  both are asserted.
- The instance's parameters and the call's are separate tables.
- A data write is visible to a later read in the same run, through the
  cache, before anything reaches the ledger; and what is finalized is
  visible to a host that has no cache at all.
- The data object's owner pays its reserve, so an owner with nothing
  spare cannot be written to.
- An account field carries its length before its id. The bare twenty
  bytes are not a field.
- A payment the contract cannot afford is reported as its `tec` and still
  queued: a fee-claiming transaction belongs on the ledger. That code is
  negative, which is what the TER output region exists for.

`ATransactionMissingARequiredFieldStopsTheRun` records a defect rather
than a design: a built transaction that fails its format check throws
where `STTx` is constructed, outside the guard that would have answered
`SubmitTxnFailure`, so the contract's own mistake reads as `InternalFatal`
and the run reports `tecINTERNAL`. Left as it is here and worth a fix of
its own.
2026-09-15 18:25:52 -04:00
Mayukha Vadari
2641f0c819 Add the contract test fixtures and the first real-ledger host tests
`ContractLedger` is the contract counterpart of `WasmLedger`: a real
genesis ledger with a real `ContractHostFunctionsImpl` over it, built the
way `ContractCall::doApply` builds one. It carries no test framework, for
the reason `WasmLedger` carries none — the benchmarks need a ledger and a
host, not GTest's lifecycle — so it joins `xrpl.testkit.wasm`.

`ContractHost` owns the four things the host borrows, in the order it
needs them, and its `finalize()` does what the transactor does once a
contract returns: write the data cache and the events to the ledger, then
apply. Applying alone is not enough, because `ApplyContext` reaches a
sandbox rather than the ledger under it, so `finalize()` commits that too.

`ContractHostFixture` adds the framework and the value constructors a
data test compares against, including `valueWire`, which produces the
bytes a guest writes for a value: the inverse of what `HostContext`
decodes.

`WasmLedger::tracingJournal` is extracted so a derived fixture can build
a tracing host over the same sink.
2026-09-15 18:15:13 -04:00
Mayukha Vadari
3106b79e4b Port the contract host functions to the Rust-declared wasm ABI
The 15 smart-contract host functions were registered through
`HostFuncWrapper.cpp` and `createWasmImport`, both of which upstream
deleted. They are now declared where every other host function is: one
`host_functions!` entry each in `crates/xrpl-host-functions`, which
generates the trait, the spec table and the wasmi registration, with a
body in the engine, a crossing in the cxx bridge, and a `HostContext`
method lowering it onto the wire.

They share the `host_lib` namespace with the escrow functions, whose host
leaves them `Unimplemented`. Wire order and gas are unchanged, so a guest
built against the old ABI still links, with two exceptions.

`emit_built_txn` and `emit_txn` gain an output region and write the TER
there as four little-endian bytes, returning the length. Their old
signature returned the TER as the call's result, which the engine reads
as a `HostError` when negative: every `tem`, `tef`, `ter` and `tel` code
would have stopped the run instead of reaching the contract.

`HostContext` decodes what the old wrappers decoded: the account region,
the typed value a `set_data_*` call carries, the serialized transaction
and the serialized event. A guest that writes those bytes wrongly gets
`InvalidParams`, so the deserializers' exceptions are caught at the call
rather than by `guarded`, which would report a node fault.

Every Rust site that enumerates the ABI is extended: the two literal
tables in `generated_abi.rs`, the import list and arity check in
`preflight.rs`, the per-function wasm in `budgets.rs`, and the three fake
hosts. `MockHostFunctions` gains the 15 methods and the matchers a
contract expectation needs.

Two `host_calls` tests cover the shapes that are new here: a typed value
decoded on its way to the host, and a TER crossing as bytes, including
the negative code that motivated the change.
2026-09-15 18:08:35 -04:00
Mayukha Vadari
ca331d2dba Merge upstream/ripple/smart-escrow into xrplf/smart-contracts
Upstream rewrote the wasm VM and host-function system (Rust `crates/`
bridged via cxx), collapsed `transactions.macro` onto `TxSettings`, and
moved invariant running from `ApplyContext` to `Transactor`. This merge
resolves those conflicts and the breaks that carried no conflict marker.

Conflict resolutions of note:
- transactions.macro: took upstream's 5-argument `TxSettings` form and
  re-expressed our `emitable` column as `TxSettings::emittance`, a new
  scoped `Emittance` enum in TxSettings.h. `Emitable.cpp` and the
  transaction code generator read the new member.
- sfields.macro: upstream claimed UINT32 75-80, so `sfParameterFlag`
  moves from 80 to 86. The amendment is not live, so no wire break.
- HostFunc.h: took upstream's version, which drops `floatRoot`, and
  re-added the 15 contract virtuals. `setDataNestedObjectField`'s
  parameters are renamed to `(account, key, nestedKey, value)` to match
  the implementation; the wire order is unchanged.
- WasmCommon.h: `SubmitTxnFailure` (-21) and `InvalidState` (-22) join
  upstream's enum and the Rust `host_errors!` table. `Success` is gone;
  the helpers that compared against it now return `expected<void, ...>`.
- Transactor.cpp: kept the emitted-transaction pass, now using
  `checkInvariants(result, fee, InvariantScope::ProtocolOnly)`.

Breaks with no conflict marker:
- `Emitable.cpp` used the 8-argument TRANSACTION macro.
- `STTx::getSeqValue` is gone; use `getSeqProxy().value()`.
- `NetworkOPsImp::subLock_` is now `streamLock_`, held with `scoped_lock`.
- The `LedgerEntryHelpers` namespace is now `ledger_entry_helpers`.
- `keylet::vault` takes a `SeqProxy`. The three contract `ledger_entry`
  parsers were copy-pasted from the vault one and returned vault keylets;
  they now build contract, contract source and contract data keylets from
  their own fields. `contract_hash` is added to jss.

The 15 contract host functions still register through the deleted C++
wrapper layer, so contract bytecode does not run yet. Porting them to the
Rust-declared ABI is the next commit.
2026-09-15 15:37:08 -04:00
Mayukha Vadari
824997df60 feat: Add sponsorship, loanBroker, and loan keylet host functions (#8195) 2026-09-15 12:40:32 -04:00
Sergey Kuznetsov
becfa96f64 Merge pull request #8212 from kuznetsss/Merge_develop
chore: Merge develop, fix pre-commit, update docs
2026-09-14 14:54:22 +01:00
Sergey Kuznetsov
2841b795f6 Merge ripple/smart-escrow 2026-09-14 13:58:32 +01:00
Timothy Banks
ba968988fe Merge pull request #8201 from XRPLF/timothybanks/port-smart-escrow-tests
fix: Port smart escrow tests to new design
2026-09-11 09:30:10 -04:00
TimothyBanks
d6757ce1a5 chore: Address code review comments 2026-09-10 13:07:34 -04:00
Sergey Kuznetsov
b68a2276f5 Comment test to make clang-tidy happy 2026-09-10 17:52:50 +01:00
Sergey Kuznetsov
4eac5dbd3b Fix coverage flags. Update AGENTS.md. Add crates/README.md 2026-09-10 16:28:50 +01:00
Sergey Kuznetsov
97b272be23 Merge develop 2026-09-10 16:10:13 +01:00
TimothyBanks
09c4e35c51 chore: Address code review comments 2026-09-10 09:40:55 -04:00
Sergey Kuznetsov
9403736199 ci: Exclude Rust unit tests from code coverage (#8203) 2026-09-10 11:38:25 +00:00
Sergey Kuznetsov
646c7ed494 refactor: Generate code to register host functions in wasm vm (#8206) 2026-09-10 11:27:40 +01:00
Mayukha Vadari
028783661d feat: Apply .macro changes from ripple/smart-escrow (#8157)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-09 20:05:58 +00:00
TimothyBanks
e1b1a010f5 fix: Address review comments 2026-09-09 11:53:38 -04:00
TimothyBanks
2d7e92ffc5 fix: Address review comments 2026-09-09 11:45:50 -04:00
TimothyBanks
c9b365315f fix: Port remaining escrow wasm tests 2026-09-09 10:41:34 -04:00
TimothyBanks
c1133a7443 fix: Port remaining escrow wasm tests 2026-09-09 10:04:37 -04:00
Vito Tumas
21890d9daf feat: Register featureLendingProtocolV1_2 amendment (#8185)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-09-09 10:01:32 +00:00
TimothyBanks
dd09ec2c2f fix: Port smart escrow tests to new design 2026-09-08 21:34:23 -04:00
Sergey Kuznetsov
060957ed39 ci: Add nightly Rust toolchain to the CI image (#8182)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-09-09 01:24:28 +00:00
TimothyBanks
a4d1bd1a55 fix: Port smart escrow tests to new design 2026-09-08 20:58:16 -04:00
Ayaz Salikhov
1381483c7a build: Fix test installation on debian:11 due to EOL (#8200) 2026-09-08 22:49:16 +00:00
TimothyBanks
4aa695f7d9 chore: Merge upstream branch 2026-09-08 15:54:17 -04:00
TimothyBanks
c9f7e39822 Merge branch 'ripple/se/wasmi-tests' into ripple/se/fees 2026-09-08 14:33:44 -04:00
TimothyBanks
9867cc295e chore: Merge upstream branch 2026-09-08 14:27:33 -04:00
Timothy Banks
0ba8b306e3 Merge pull request #8189 from XRPLF/timothybanks/upgrade-to-wasmi-2
chore: Upgrade to Wasmi 2.0
2026-09-08 10:34:47 -04:00
TimothyBanks
daecad43c9 chore: Upgrade to Wasmi 2.0 2026-09-08 09:58:32 -04:00
Timothy Banks
e9074726cd Merge pull request #8114 from XRPLF/timothybanks/port-wat-based-tests
feat: Port wat based wasm tests to new wasm design
2026-09-08 09:31:01 -04:00
dependabot[bot]
3e4bdf2782 ci: [DEPENDABOT] bump actions/deploy-pages from 5.0.0 to 5.0.1 in the github-actions group across 1 directory (#8180)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 16:07:56 +00:00
Mayukha Vadari
e3c8996e44 feat: Add fixCleanup3_5_0 amendment placeholder (#8174) 2026-09-05 00:06:14 +00:00
TimothyBanks
4208f14a91 fix: Add additional vm benchmarks 2026-09-03 20:47:09 -04:00
yinyiqian1
d5bfe94f15 feat: Support key rotation in MPTokenIssuanceSet (#7915) 2026-09-03 21:13:06 +00:00
Bart
2ad4def35f chore: Bump version to 3.4.0-rc1 (#8171)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
3.4.0-rc1
2026-09-03 20:07:24 +00:00
Mayukha Vadari
f7f50caa6e docs: Backfill API-CHANGELOG.md for 3.1.1 through 3.3.0 (#8159) 2026-09-03 19:48:15 +00:00
Ayaz Salikhov
3e54e7d00b fix: Don't use github.workspace as it slow downs gcovr (#8173) 2026-09-03 19:20:38 +00:00
Valentin Balaschenko
827b50f169 fix: Flaky online delete tests, and cover the health checks added in #5531 (#8137) 2026-09-03 18:56:30 +00:00
Mayukha Vadari
58a59c37ed fix: Add signature prefixes for sfCounterpartySignature and sfSponsorSignature (#8162) 2026-09-03 17:19:07 +00:00
Ayaz Salikhov
986065c16f build: Verify glibc version was determined in debian package (#8170) 2026-09-03 16:54:54 +00:00
Denis Angell
37cb4cdbe3 fix: Recycle the escrow reserve in EscrowCancel and EscrowFinish (#8142) 2026-09-03 16:27:45 +00:00