A plugin is its own binary; the daemon that loads it has no business being
linked into it. Twelve VMs reached into github.com/luxfi/node for four things,
and each one now programs against a home that is actually its own:
- fee admission (Policy, FlatPolicy, NoUserTxPolicy, Validate) joins the
settlement half already in chains/fee. One fee package, and the nodefee alias
that marked the split is gone.
- the cross-chain artifact types move to chains/artifacts.
- the factory the daemon registers is luxfi/vm/manager.Factory, and the version
types are luxfi/version. Both are ordinary modules the daemon reads too, so a
VM imports them directly rather than through anything of ours.
The GPU switch is deleted, not moved. It was a global set during daemon startup
that a separate process could never observe, so every plugin read its default;
FHEOptions.Backend is the switch that was really there.
go list -deps names no luxfi/node for aivm, bridgevm, dexvm, evm, fhevm,
graphvm, identityvm, keyvm, mpcvm, quantumvm, schain or zkvm, and the built
plugin binaries carry no such string. oraclevm and relayvm still do: they are
re-export shims over luxfi/oracle and luxfi/relay, which build their fee
policies upstream. Their tests keep naming the daemon's fee package because
that is the type their value has.
Every VM in this repo burns through this package, and its fail-secure guards
were the part with no tests: the overflow arms, the corrupt-state refusal, and
every storage-failure path. 82.7% -> 100.0%.
An unreadable balance is not a balance of zero. Reporting zero would make an
unfunded account and an unreachable store indistinguishable, and the caller
would charge the one it could not read. Same for a stored value of the wrong
width: that is corruption, not a number, and decoding it invents a balance from
whatever bytes are there.
Burn's two writes are ordered on purpose. Either can fail, and whichever half
stages alone is wrong -- the caller must abort, because the Balances contract
puts both writes in the block's commit. But they are not equally wrong if one
ever did commit: debit-then-record destroys value, record-then-debit invents it,
and an overstated burn counter is unrecoverable because nobody can say which
account should have paid. The survivable failure is the chosen one.
Conservation is now asserted rather than assumed: across an interleaving of
credits and burns, including refused ones, everything credited is still either
spendable or burned -- never both, never neither. That is what makes this a
ledger instead of two counters that happen to move together.
8/8 mutation checks caught, including the write order.