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.
This commit is contained in:
Mayukha Vadari
2026-09-23 23:44:52 +05:30
parent cd5366dab7
commit 670f1d5e6f
2 changed files with 23 additions and 0 deletions

View File

@@ -22,6 +22,10 @@ public:
// getFieldBytesFromSTData(xrpl::STData const& funcParam, std::uint32_t
// stTypeId);
// TODO: `updateData` is inherited from the escrow host and is not refused here, so a
// contract calling `set_data` is told how many bytes it stored and then has them
// dropped: `ContractCall::doApply` never drains `getData()`. Refusing it needs an error
// a contract can read, which `Unimplemented` is not — see the note in HostFunc.h.
std::expected<Bytes, HostFunctionError>
instanceParam(std::uint32_t index, std::uint32_t stTypeId) override;

View File

@@ -479,6 +479,25 @@ public:
return std::unexpected(HostFunctionError::Unimplemented);
}
// Smart-contract host functions. `WasmHostFunctionsImpl`, the escrow host, leaves every
// one of them at the `Unimplemented` default below, and `ContractHostFunctionsImpl`
// overrides them.
//
// TODO: that default is the only thing separating the two groups, and it separates them
// too late and in one direction only. The ABI is one flat table in one `host_lib`
// namespace, so screening accepts an escrow whose bytecode imports `emit_built_txn`:
// `EscrowCreate` stores it, and the escrow fails at finish with `tecINTERNAL`, because
// `Unimplemented` is a fatal fault rather than a code a contract reads. The other
// direction does not fail at all — `ContractHostFunctionsImpl` inherits `updateData`,
// and only `EscrowFinish::doApply` ever drains it, so a contract's write returns a byte
// count and is discarded.
//
// Both want the same thing: screening has to know which subset of
// `HostFunctionSpec::ALL` the host being built for actually serves, so a module
// importing the wrong group is refused at create with `temINVALID_BYTECODE`.
// `docs/wasm-amendment-gating.md` designs that transport for amendments (a gate
// bitfield threaded into `check()` and `register_host_functions`); this needs the same
// transport keyed on which host is running.
virtual std::expected<Bytes, HostFunctionError>
instanceParam(std::uint32_t index, std::uint32_t stTypeId)
{