machine.gno
3.76 Kb · 100 lines
1// Package vmkit is the host ABI shared by every guest virtual machine that
2// runs inside a gno realm: a stepping contract with an explicit fuel budget,
3// a capability-scoped [Host], and a snapshot format that lets a guest program
4// outlive the transaction that started it.
5//
6// A guest VM implements [Machine]. Everything else in this package is the
7// machinery a VM should not have to write twice: the [Meter], the [Codec] the
8// snapshots are built from, an in-memory [TestHost], and the avl-backed
9// [Store] a realm keeps its instances in.
10//
11// The three things the ABI exists to fix:
12//
13// - Fuel is the interface, not the backstop. Running out of fuel yields
14// [OutOfFuel] plus a machine that can be snapshotted, never a panic. Gas
15// still bounds the transaction, but a guest that stops is a normal outcome.
16// - Continuations. Snapshot and Restore mean a program runs across blocks.
17// The realm stores the bytes, and the next caller pays for the next slice.
18// Gno realm code itself cannot pause; a guest can.
19// - Capabilities, not ambient authority. A guest gets exactly the [Host] it
20// was handed. No Send without a grant, no storage outside its own scope.
21//
22// Live demo: [r/moul/x/vm/bfdemo](/r/moul/x/vm/bfdemo/v0), running the
23// [p/moul/x/vm/bf](/p/moul/x/vm/bf/v0) guest.
24package vmkit
25
26// Status is the outcome of a call to [Machine.Step].
27type Status int
28
29const (
30 // Running means the machine stopped because it ran out of the fuel
31 // handed to this slice, but the program has not finished. It is
32 // resumable: snapshot it, and step it again later.
33 Running Status = iota
34
35 // Halted means the program reached its end. Terminal.
36 Halted
37
38 // Trapped means the guest did something the machine refuses to do:
39 // an invalid instruction, an out-of-range access, a capability it was
40 // not granted. Terminal.
41 Trapped
42
43 // OutOfFuel means the instance exhausted its total budget, not just the
44 // fuel for this slice. Terminal unless the owner raises the budget.
45 OutOfFuel
46)
47
48// String renders the status as the lowercase word used in realm output.
49func (s Status) String() string {
50 switch s {
51 case Running:
52 return "running"
53 case Halted:
54 return "halted"
55 case Trapped:
56 return "trapped"
57 case OutOfFuel:
58 return "out of fuel"
59 }
60 return "unknown"
61}
62
63// Done reports whether the status is terminal, i.e. stepping again is
64// pointless without operator intervention.
65func (s Status) Done() bool { return s != Running }
66
67// Machine is one guest virtual machine, mid-execution.
68//
69// Step runs until the program halts, traps, or burns `fuel` units, whichever
70// comes first, and reports how much fuel it actually used. A Machine must
71// charge at least one unit per guest instruction so that a fuel budget is a
72// real bound on work; beyond that the unit is the VM's own business, and
73// [r/moul/x/vm/bfdemo](/r/moul/x/vm/bfdemo/v0) compares them by measurement
74// rather than by trusting the number.
75//
76// Snapshot must round-trip through Restore: a machine stepped to exhaustion,
77// snapshotted, restored and stepped again must produce exactly what the same
78// machine stepped in one go would have. That property is what makes a guest
79// program a contract instead of a function call.
80type Machine interface {
81 Step(h Host, fuel int64) (used int64, status Status)
82 Snapshot() []byte
83 Restore(b []byte) error
84}
85
86// Trapper is an optional refinement of [Machine]: a machine that can explain
87// why it trapped. Kept out of [Machine] so the core ABI stays three methods.
88type Trapper interface {
89 // Trap returns the reason for a [Trapped] status, or "" when the
90 // machine has not trapped.
91 Trap() string
92}
93
94// TrapReason returns m's trap reason when m implements [Trapper], else "".
95func TrapReason(m Machine) string {
96 if t, ok := m.(Trapper); ok {
97 return t.Trap()
98 }
99 return ""
100}