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.
This commit is contained in:
Mayukha Vadari
2026-09-15 15:37:08 -04:00
932 changed files with 84511 additions and 47992 deletions

View File

@@ -8,6 +8,7 @@ Uses pcpp to preprocess the macro file and pyparsing to parse the DSL.
import io
import argparse
import re
from pathlib import Path
import pyparsing as pp
@@ -53,28 +54,90 @@ def create_transaction_parser():
return macro_parser
# Defaults for xrpl::TxSettings members, mirroring
# include/xrpl/protocol/TxSettings.h. A transaction's settings blob only names
# the members that differ from these.
SETTING_DEFAULTS = {
"delegable": "Delegation::NotDelegable",
"amendment": "uint256{}",
"privileges": "Privilege::NoPriv",
"emittance": "Emittance::Emitable",
}
def parse_settings(settings_str):
"""Parse a TxSettings blob into a dict, filling in defaults.
Args:
settings_str: A string like '({.delegable = Delegation::NotDelegable,
.privileges = Privilege::CreateAcct})', or '({})'.
Returns:
A dict with a value for every key in SETTING_DEFAULTS.
"""
body = settings_str.strip()
if not (body.startswith("(") and body.endswith(")")):
raise ValueError(
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
)
body = body[1:-1].strip()
if not (body.startswith("{") and body.endswith("}")):
raise ValueError(
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
)
body = body[1:-1]
# Strip comments, which may be interleaved with the designated initializers.
body = re.sub(r"//[^\n]*", "", body)
settings = dict(SETTING_DEFAULTS)
seen = set()
# Each entry runs from '.key =' up to the next '.key =' or the end.
for key, value in re.findall(
r"\.(\w+)\s*=\s*(.*?)(?=,\s*\.\w+\s*=|,?\s*$)", body, re.S
):
if key not in SETTING_DEFAULTS:
raise ValueError(f"Unknown TxSettings member '.{key}' in {settings_str!r}")
settings[key] = " ".join(value.split()).rstrip(",")
seen.add(key)
# Catch a typo'd or unparsed initializer rather than silently defaulting it.
# Every '.member' in the blob must have been consumed above.
if len(re.findall(r"\.\w+", body)) != len(seen):
raise ValueError(f"Could not parse every setting in {settings_str!r}")
# A blob with content but no designated initializer is positional, which
# would otherwise be read as "all defaults" and silently generate the
# wrong output.
if body.strip() and not seen:
raise ValueError(
"TxSettings requires designated initializers (.member = value), "
f"got {settings_str!r}"
)
return settings
def parse_transaction_args(args_list):
"""Parse the arguments of a TRANSACTION macro call.
Args:
args_list: A list of parsed arguments from pyparsing, e.g.,
['ttPAYMENT', '0', 'Payment', 'Delegation::delegable',
'uint256{}', 'createAcct', '({...})']
['ttPAYMENT', '0', 'Payment',
'({.privileges = Privilege::CreateAcct})', '({...})']
Returns:
A dict with parsed transaction information.
"""
if len(args_list) < 7:
if len(args_list) < 5:
raise ValueError(
f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}"
f"Expected at least 5 parts in TRANSACTION, got {len(args_list)}: {args_list}"
)
tag = args_list[0]
value = args_list[1]
name = args_list[2]
delegable = args_list[3]
amendments = args_list[4]
privileges = args_list[5]
settings = parse_settings(args_list[3])
fields_str = args_list[-1]
# Parse fields: ({field1, field2, ...})
@@ -84,9 +147,9 @@ def parse_transaction_args(args_list):
"tag": tag,
"value": value,
"name": name,
"delegable": delegable,
"amendments": amendments,
"privileges": privileges,
"delegable": settings["delegable"],
"amendments": settings["amendment"],
"privileges": settings["privileges"],
"fields": fields,
}