Skip to content

Run contract executions one at a time - #36

Merged
TracSystems merged 2 commits into
mainfrom
fix/serialize-contract-execution
Sep 10, 2026
Merged

TracSystems merged 2 commits into
mainfrom
fix/serialize-contract-execution

Conversation

@TracSystems

Copy link
Copy Markdown
Contributor

A contract instance keeps the state of the running call on the instance itself:
address, validator_address, is_feature, is_message, tx, op, value
and storage are set on entry to Contract.execute(), and contract handlers
re-read those fields after every await. Two executions in flight on one instance
therefore interleave their working state.

Autobase apply is sequential, but Protocol.simulateTransaction() executes on
the same live contract instance from outside the apply loop, so a simulation and
an apply do overlap on a busy peer. Observed on a live subnet writer: a simulated
command returned a record with two of its fields missing, and the operation that
resumed a moment later found the fields already reset and threw
get(key): storage undefined, which ended the process.

The execution body moves unchanged to executeQueued(), and execute() now
queues calls so a call starts only after the previous one has finished and
releases the next one in its finally block, so a throwing handler cannot wedge
the queue. Apply order and results are unchanged because apply was already
sequential; simulations wait behind whatever is applying instead of interleaving
with it.

New unit tests in tests/unit/contractExecuteQueue.test.js: two overlapping
executions each keep their own operation, sender, transaction and storage and
match a sequential run byte for byte; a throwing handler leaves the queue usable;
a sub-class override that calls super.execute() still runs.

The second commit pins trac-msb to its GitHub repository at the commit that
carries version 0.2.21, so every consumer builds against the same settlement bus
code instead of whatever the registry range picks up.

Local runs on this branch: npm run test:unit:node 43/43 tests, 125/125 asserts
(40/40 and 89/89 on main); npm run test:acceptance:node 4/4 tests, 21/21
asserts.

A contract instance keeps the state of the running call on the instance itself:
address, validator_address, is_feature, is_message, tx, op, value and storage
are set on entry to Contract.execute(), and contract handlers re-read those
fields after every await. Two executions in flight on one instance therefore
interleave their working state.

Autobase apply is sequential, but Protocol.simulateTransaction() executes on the
same live contract instance from outside the apply loop, so a simulation and an
apply do overlap on a busy peer. Observed on a live subnet writer: a simulated
command returned a record with two of its fields missing, and the operation that
resumed a moment later found the fields already reset and threw
"get(key): storage undefined", which ended the process.

The execution body moves unchanged to executeQueued(), and execute() now queues
calls so a call starts only after the previous one has finished and releases the
next one in its finally block, so a throwing handler cannot wedge the queue.
Apply order and results are unchanged because apply was already sequential;
simulations wait behind whatever is applying instead of interleaving with it.

New unit tests in tests/unit/contractExecuteQueue.test.js: two overlapping
executions each keep their own operation, sender, transaction and storage and
match a sequential run byte for byte; a throwing handler leaves the queue
usable; a sub-class override that calls super.execute() still runs.
The trac-msb dependency now resolves from its GitHub repository at the exact
commit that carries version 0.2.21, so every consumer builds against the same
settlement bus code instead of whatever the registry range picks up.
@TracSystems
TracSystems merged commit abefdbf into main Sep 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant