Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

rust_test.gno

3.39 Kb · 99 lines
 1package riscv
 2
 3import (
 4	"testing"
 5
 6	"gno.land/p/moul/x/vm/vmkit/v0"
 7	"gno.land/p/nt/uassert/v0"
 8)
 9
10// The demo the design issue actually asked for: Rust code, compiled by rustc,
11// running on gno.land, moving a balance from one account to another.
12//
13// The image is guests.gno's, built from tools/riscv-guests/ledger. It is worth
14// more than the C guest for one reason: it links Rust's real core and
15// compiler_builtins, so this exercises the standard library's own software
16// arithmetic rather than only what the compiler emitted for my source.
17
18func runLedger(t *testing.T, in string) (string, vmkit.Status, *Machine) {
19	t.Helper()
20	m, err := NewMachine(GuestLedger(), entry)
21	uassert.NoError(t, err)
22	if m == nil {
23		return "", vmkit.Trapped, nil
24	}
25	h := vmkit.NewTestHost().WithInput([]byte(in))
26	_, status := m.Step(h, 5000000)
27	return string(h.Out()), status, m
28}
29
30func TestRustLedgerTransfers(t *testing.T) {
31	out, status, _ := runLedger(t,
32		"mint alice 100\nmint bob 50\nsend alice bob 30\n")
33	uassert.Equal(t, "halted", status.String())
34	uassert.Equal(t, "alice 70\nbob 80\n", out)
35}
36
37func TestRustLedgerMintBurnAndBlankLines(t *testing.T) {
38	out, status, _ := runLedger(t, "mint a 5\n\n   \nburn a 2\n")
39	uassert.Equal(t, "halted", status.String())
40	uassert.Equal(t, "a 3\n", out)
41
42	out, status, _ = runLedger(t, "")
43	uassert.Equal(t, "halted", status.String())
44	uassert.Equal(t, "", out)
45}
46
47// A refusal is halted, not trapped, and it names the line. This is the
48// semantic the whole VM is built around: the program said no, the machine did
49// not break, and a chain has to be able to tell a user which one happened.
50func TestRustLedgerRefusalsAreHaltsNotTraps(t *testing.T) {
51	cases := []struct{ in, want string }{
52		{"mint a 1\nsend a b 9\n", "line 2: insufficient balance\n"},
53		{"send nobody b 1\n", "line 1: unknown sender\n"},
54		{"burn ghost 1\n", "line 1: unknown account\n"},
55		{"frobnicate\n", "line 1: unknown command\n"},
56		{"mint a x\n", "line 1: bad amount\n"},
57	}
58	for _, c := range cases {
59		out, status, m := runLedger(t, c.in)
60		uassert.Equal(t, "halted", status.String(), c.in+": "+m.Trap())
61		uassert.Equal(t, c.want, out)
62		uassert.Equal(t, uint64(1), uint64(m.Registers()[10]), "a refusal exits 1")
63	}
64}
65
66// 64-bit arithmetic on a 32-bit machine goes through compiler_builtins, and
67// printing a balance above 2^32 is the path that proves __udivdi3 ran.
68func TestRustLedgerDoes64BitArithmetic(t *testing.T) {
69	// Nine-digit mints, accumulated past what 32 bits holds.
70	in := ""
71	for i := 0; i < 6; i++ {
72		in += "mint whale 999999999\n"
73	}
74	out, status, _ := runLedger(t, in)
75	uassert.Equal(t, "halted", status.String())
76	uassert.Equal(t, "whale 5999999994\n", out)
77}
78
79// Compiled Rust pauses and resumes like anything else, including in the middle
80// of a library routine it never asked to call.
81func TestRustLedgerSurvivesSlicing(t *testing.T) {
82	image := GuestLedger()
83	h := vmkit.NewTestHost().WithInput([]byte("mint alice 100\nsend alice bob 30\n"))
84
85	sliced, err := NewMachine(image, entry)
86	uassert.NoError(t, err)
87	status := vmkit.Running
88	slices := 0
89	for i := 0; i < 4000 && status == vmkit.Running; i++ {
90		var fresh Machine
91		uassert.NoError(t, fresh.Restore(sliced.Snapshot()))
92		_, status = fresh.Step(h, 17)
93		sliced = &fresh
94		slices++
95	}
96	uassert.Equal(t, "halted", status.String())
97	uassert.True(t, slices > 20, "a sliced run must actually have been sliced")
98	uassert.Equal(t, "alice 70\nbob 30\n", string(h.Out()))
99}