PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/vm/vmkit/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / vm / vmkit
Files
9 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/vm/vmkit/v0
gno
0.9

Files (9)

  • README.mdmarkdown
  • gnomod.tomltoml
  • codec.gnogno
  • fuel.gnogno
  • host.gnogno
  • instance.gnogno
  • machine.gnogno
  • testhost.gnogno
  • vmkit_test.gnogno
  • vmkit_test.gnogno
    1package vmkit23import (4	"testing"56	"gno.land/p/nt/uassert/v0"7)8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    9// counter is a toy [Machine] used to test the kit itself without dragging in
    10// a real guest VM: it writes one byte per step and halts after `total` of
    11// them. Anything that works here is a property of the ABI, not of brainfuck.
    12type counter struct {
    13 total int64
    14 done int64
    15 trap string
    16}
    17
    18func (c *counter) Step(h Host, fuel int64) (int64, Status) {
    19 m := NewMeter(fuel)
    20 var used int64
    21 for c.done < c.total {
    22 if !m.Charge(1) {
    23 return used, Running
    24 }
    25 used++
    26 c.done++
    27 h.Output([]byte{byte('a' + (c.done-1)%26)})
    28 }
    29 return used, Halted
    30}
    31
    32func (c *counter) Snapshot() []byte {
    33 w := NewWriter(24)
    34 w.Int(c.total)
    35 w.Int(c.done)
    36 w.String(c.trap)
    37 return w.Out()
    38}
    39
    40func (c *counter) Restore(b []byte) error {
    41 r := NewReader(b)
    42 total := r.Int()
    43 done := r.Int()
    44 trap := r.String()
    45 if err := r.Err(); err != nil {
    46 return err
    47 }
    48 c.total, c.done, c.trap = total, done, trap
    49 return nil
    50}
    51
    52func (c *counter) Trap() string { return c.trap }
    53
    54func TestStatusString(t *testing.T) {
    55 cases := []struct {
    56 s Status
    57 want string
    58 done bool
    59 }{
    60 {Running, "running", false},
    61 {Halted, "halted", true},
    62 {Trapped, "trapped", true},
    63 {OutOfFuel, "out of fuel", true},
    64 {Status(99), "unknown", true},
    65 }
    66 for _, tc := range cases {
    67 uassert.Equal(t, tc.want, tc.s.String())
    68 uassert.Equal(t, tc.done, tc.s.Done())
    69 }
    70}
    71
    72func TestMeterCharge(t *testing.T) {
    73 m := NewMeter(10)
    74 uassert.True(t, m.Charge(4))
    75 uassert.Equal(t, int64(4), m.Used())
    76 uassert.Equal(t, int64(6), m.Remaining())
    77
    78 // A charge that does not fit spends nothing, so the caller can stop
    79 // before the instruction it cannot pay for.
    80 uassert.False(t, m.Charge(7))
    81 uassert.Equal(t, int64(4), m.Used())
    82
    83 uassert.True(t, m.Charge(6))
    84 uassert.True(t, m.Exhausted())
    85 uassert.False(t, m.Charge(1))
    86}
    87
    88func TestMeterUnmetered(t *testing.T) {
    89 m := NewMeter(Unmetered)
    90 uassert.True(t, m.Charge(1 << 40))
    91 uassert.False(t, m.Exhausted())
    92 uassert.Equal(t, Unmetered, m.Remaining())
    93}
    94
    95func TestMeterZeroBudget(t *testing.T) {
    96 m := NewMeter(0)
    97 uassert.True(t, m.Exhausted())
    98 uassert.False(t, m.Charge(1))
    99 uassert.Equal(t, int64(0), m.Used())
    100}
    101
    102func TestCodecRoundTrip(t *testing.T) {
    103 w := NewWriter(0)
    104 w.Byte(0xab)
    105 w.Uint32(0xdeadbeef)
    106 w.Uint64(0x0102030405060708)
    107 w.Int(-1234567)
    108 w.Int(0)
    109 w.Int(1234567)
    110 w.Bytes([]byte{0, 1, 2, 255})
    111 w.String("gno.land")
    112
    113 r := NewReader(w.Out())
    114 uassert.Equal(t, uint64(0xab), uint64(r.Byte()))
    115 uassert.Equal(t, uint64(0xdeadbeef), uint64(r.Uint32()))
    116 uassert.Equal(t, uint64(0x0102030405060708), r.Uint64())
    117 uassert.Equal(t, int64(-1234567), r.Int())
    118 uassert.Equal(t, int64(0), r.Int())
    119 uassert.Equal(t, int64(1234567), r.Int())
    120 uassert.Equal(t, 4, len(r.Bytes()))
    121 uassert.Equal(t, "gno.land", r.String())
    122 uassert.NoError(t, r.Err())
    123 uassert.Equal(t, 0, r.Remaining())
    124}
    125
    126func TestCodecIsCanonical(t *testing.T) {
    127 // The same state must always produce the same bytes: a snapshot is
    128 // consensus state, so two nodes encoding it differently is a fork.
    129 build := func() []byte {
    130 w := NewWriter(0)
    131 w.Int(-7)
    132 w.String("x")
    133 w.Uint32(9)
    134 return w.Out()
    135 }
    136 uassert.Equal(t, string(build()), string(build()))
    137}
    138
    139func TestCodecTruncated(t *testing.T) {
    140 r := NewReader([]byte{1, 2})
    141 r.Uint64()
    142 uassert.ErrorIs(t, r.Err(), ErrTruncated)
    143 // Once latched, every later read is a zero value and the error stands.
    144 uassert.Equal(t, "", r.String())
    145 uassert.ErrorIs(t, r.Err(), ErrTruncated)
    146}
    147
    148func TestCodecBytesAreCopied(t *testing.T) {
    149 w := NewWriter(0)
    150 w.Bytes([]byte{1, 2, 3})
    151 buf := w.Out()
    152 r := NewReader(buf)
    153 got := r.Bytes()
    154 buf[len(buf)-1] = 99 // mutate the snapshot under the reader
    155 uassert.Equal(t, 3, len(got))
    156 uassert.Equal(t, uint64(3), uint64(got[2]))
    157}
    158
    159func TestTestHostStorage(t *testing.T) {
    160 h := NewTestHost()
    161 uassert.Equal(t, 0, len(h.Get([]byte("missing"))))
    162
    163 h.Set([]byte{0, 1}, []byte("zero-prefixed"))
    164 h.Set([]byte("k"), []byte("v"))
    165 uassert.Equal(t, "zero-prefixed", string(h.Get([]byte{0, 1})))
    166 uassert.Equal(t, "v", string(h.Get([]byte("k"))))
    167
    168 // Keys are hex so a zero byte survives, and sorted so a test can pin
    169 // them.
    170 keys := h.Keys()
    171 uassert.Equal(t, 2, len(keys))
    172 uassert.Equal(t, "0001", keys[0])
    173 uassert.Equal(t, "6b", keys[1])
    174}
    175
    176func TestTestHostStorageIsCopied(t *testing.T) {
    177 h := NewTestHost()
    178 val := []byte("abc")
    179 h.Set([]byte("k"), val)
    180 val[0] = 'z'
    181 uassert.Equal(t, "abc", string(h.Get([]byte("k"))))
    182
    183 got := h.Get([]byte("k"))
    184 got[0] = 'z'
    185 uassert.Equal(t, "abc", string(h.Get([]byte("k"))))
    186}
    187
    188func TestTestHostSendNeedsAGrant(t *testing.T) {
    189 to := address("g1manfred47kzduec920z88wfr64ylksmdcedlf5")
    190
    191 h := NewTestHost()
    192 uassert.ErrorIs(t, h.Send(to, 100), ErrNotGranted)
    193 uassert.Equal(t, 0, len(h.Sends()))
    194
    195 h.Grant(150)
    196 uassert.NoError(t, h.Send(to, 100))
    197 uassert.Equal(t, 1, len(h.Sends()))
    198 uassert.Equal(t, int64(100), h.Sends()[0].Amount)
    199
    200 // The grant is a budget, not a switch.
    201 uassert.ErrorIs(t, h.Send(to, 100), ErrNotGranted)
    202 uassert.NoError(t, h.Send(to, 50))
    203}
    204
    205func TestTestHostEventsAndInput(t *testing.T) {
    206 h := NewTestHost().WithInput([]byte("hi")).WithHeight(42).WithTime(1700000000)
    207 uassert.Equal(t, "hi", string(h.Input()))
    208 uassert.Equal(t, int64(42), h.Height())
    209 uassert.Equal(t, int64(1700000000), h.Now())
    210
    211 h.Emit("run", "id", "1", "status", "halted")
    212 h.Emit("odd", "dangling")
    213 uassert.Equal(t, 2, len(h.Events()))
    214 uassert.Equal(t, "run id=1 status=halted", h.Events()[0])
    215 uassert.Equal(t, "odd", h.Events()[1]) // odd trailing element dropped
    216}
    217
    218func TestInstanceRunsToCompletion(t *testing.T) {
    219 inst := NewInstance("1", address("g1x"), "counter", []byte("5"), Unmetered)
    220 h := NewTestHost()
    221 uassert.NoError(t, inst.Run(&counter{total: 5}, h, Unmetered))
    222 uassert.Equal(t, "halted", inst.Status.String())
    223 uassert.Equal(t, int64(5), inst.FuelUsed)
    224 uassert.Equal(t, int64(1), inst.Slices)
    225 uassert.Equal(t, "abcde", h.OutString())
    226}
    227
    228func TestInstanceResumesAcrossSlices(t *testing.T) {
    229 // The property that makes a guest program a contract: five slices of
    230 // one unit must equal one slice of five.
    231 inst := NewInstance("1", address("g1x"), "counter", []byte("5"), Unmetered)
    232 h := NewTestHost()
    233 for i := 0; i < 5; i++ {
    234 // A realm holds bytes, not a machine: every slice loads a fresh
    235 // one from the program and lets Restore carry the state over.
    236 uassert.NoError(t, inst.Run(&counter{total: 5}, h, 1))
    237 }
    238 uassert.Equal(t, "halted", inst.Status.String())
    239 uassert.Equal(t, int64(5), inst.FuelUsed)
    240 uassert.Equal(t, int64(5), inst.Slices)
    241 uassert.Equal(t, "abcde", h.OutString())
    242}
    243
    244func TestInstanceStopsAtItsBudget(t *testing.T) {
    245 inst := NewInstance("1", address("g1x"), "counter", []byte("10"), 3)
    246 h := NewTestHost()
    247 uassert.NoError(t, inst.Run(&counter{total: 10}, h, Unmetered))
    248 uassert.Equal(t, "out of fuel", inst.Status.String())
    249 uassert.Equal(t, int64(3), inst.FuelUsed)
    250 uassert.Equal(t, int64(0), inst.Remaining())
    251 uassert.Equal(t, "abc", h.OutString())
    252
    253 // A second call has nothing to spend and says so instead of looping.
    254 uassert.ErrorIs(t, inst.Run(&counter{}, h, Unmetered), ErrBudgetExhausted)
    255}
    256
    257func TestInstanceSliceClamps(t *testing.T) {
    258 inst := NewInstance("1", address("g1x"), "counter", nil, 10)
    259 inst.FuelUsed = 7
    260 uassert.Equal(t, int64(3), inst.Slice(100))
    261 uassert.Equal(t, int64(2), inst.Slice(2))
    262 uassert.Equal(t, int64(3), inst.Slice(Unmetered))
    263
    264 open := NewInstance("2", address("g1x"), "counter", nil, Unmetered)
    265 uassert.Equal(t, int64(5), open.Slice(5))
    266 uassert.Equal(t, Unmetered, open.Slice(Unmetered))
    267}
    268
    269func TestInstanceRejectsABadSnapshot(t *testing.T) {
    270 inst := NewInstance("1", address("g1x"), "counter", []byte("5"), Unmetered)
    271 inst.Snapshot = []byte{1, 2, 3} // too short for the counter's three fields
    272 err := inst.Run(&counter{}, NewTestHost(), Unmetered)
    273 uassert.ErrorIs(t, err, ErrTruncated)
    274 // The instance is untouched: a snapshot that cannot be decoded costs
    275 // gas but never corrupts the stored program.
    276 uassert.Equal(t, int64(0), inst.Slices)
    277 uassert.Equal(t, "5", string(inst.Program))
    278}
    279
    280func TestStore(t *testing.T) {
    281 s := NewStore()
    282 uassert.Equal(t, 0, s.Size())
    283 uassert.Equal(t, true, s.Get("nope") == nil)
    284
    285 s.Set(NewInstance("001", address("g1a"), "counter", nil, Unmetered))
    286 s.Set(NewInstance("002", address("g1b"), "counter", nil, Unmetered))
    287 s.Set(NewInstance("003", address("g1c"), "counter", nil, Unmetered))
    288 uassert.Equal(t, 3, s.Size())
    289 uassert.Equal(t, "002", s.Get("002").ID)
    290
    291 ids := ""
    292 s.Iterate(func(i *Instance) bool { ids += i.ID + " "; return false })
    293 uassert.Equal(t, "001 002 003 ", ids)
    294
    295 rev := ""
    296 s.ReverseIterate(func(i *Instance) bool { rev += i.ID + " "; return false })
    297 uassert.Equal(t, "003 002 001 ", rev)
    298
    299 // Early stop.
    300 first := ""
    301 s.Iterate(func(i *Instance) bool { first = i.ID; return true })
    302 uassert.Equal(t, "001", first)
    303
    304 uassert.True(t, s.Remove("002"))
    305 uassert.False(t, s.Remove("002"))
    306 uassert.Equal(t, 2, s.Size())
    307}
    308
    309func TestTrapReason(t *testing.T) {
    310 uassert.Equal(t, "boom", TrapReason(&counter{trap: "boom"}))
    311 uassert.Equal(t, "", TrapReason(&counter{}))
    312}
    313