mv-cli 0.3.2

CLI frontend for the Move compiler and VM
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
# Move CLI

The Move command-line interface (Move CLI) is a tool that provides an easy way to interact with Move, to experiment
with writing and running Move code, and to experiment with developing new tools useful
for Move development. To reflect this, the Move CLI commands are grouped into
three main subcommands:
* **package commands**: are commands to create, compile, and test Move packages, as well as perform other operations related to packages. These do not rely on a Move Adapter implementation nor an implementation of storage.
* **sandbox commands**: are commands that allow you to write Move modules and scripts, write and run scripts and tests, and view the resulting state of execution in a local sandboxed environment.
* **experimental commands**: are experimental commands that are currently in development.

Every Move CLI command, with the exception of `package create`, is expected to be run within the context of a [Move package](https://move-language.github.io/move/packages.html).

## Installation
```shell
$ cargo install --path move/language/tools/move-cli
```
or
```shell
$ cargo install --git https://github.com/move-language/move move-cli --branch main
```

This will install the `move` binary in your Cargo binary directory. On
macOS and Linux this is usually `~/.cargo/bin`. You'll want to make sure
this location is in your `PATH` environment variable.

Now you should be able to run the Move CLI:

```shell
$ move
Move 0.1.0
CLI frontend for Move compiler and VM

USAGE:
    move [FLAGS] [OPTIONS] <SUBCOMMAND>
  ...
```

We'll go through the most common Move CLI commands and flags here, however
you can find the complete list of commands available by calling `move
--help`.  Additionally, the complete list of flags and options available
for each Move CLI command can be found by passing the `--help` flag to it,
i.e., `move <command> --help`.

## Package Commands

Package commands provide wrappers with sane defaults around other commands
that are provided either by various Move tools, compiler, or prover.

The `move new` command will create a new empty Move package:
```shell
$ move new <package_name> # Create a Move package <package_name> under the current dir
$ move new <package_name> -p <path> # Create a Move package <package_name> under path <path>
```

From within a package's root directory, you can build the modules and/or scripts that you have written in the package with:
```shell
$ move build # Builds the Move package you are currently in
$ move build -p <path> # Builds the Move package at <path>
```

The compiled artifacts will by default be stored in the `build` directory. You
can change where the build artifacts are saved by passing the optional `--build-dir` flag:

```shell
$ move build --build-dir <path_to_save_to> # Build current Move package and save artifacts under <path_to_save_to>
```

You can verify the specifications in a Move package using the Move Prover with the `prove` command:

```shell
$ move prove # Verify the specifications in the current package
$ move prove -p <path> # Verify the specifications in the package at <path>
```

In order to run the Move Prover [additional tools need to be
installed](https://github.com/move-language/move/blob/main/language/move-prover/doc/user/install.md).
Information on the Move Prover and its configuration options can be found
[here](https://github.com/move-language/move/blob/main/language/move-prover/doc/user/prover-guide.md)
and
[here](https://github.com/move-language/move/blob/main/language/move-prover/doc/user/spec-lang.md).

You can also run unit tests in a package using the `test` command

```shell
$ move test # Run Move unit tests in the current package
$ move test -p <path> # Run Move unit tests in the package at <path>
```
## Sandbox Commands

The sandbox allows you to experiment with writing and running Move code without
validators, a blockchain, or transactions. Persistent data is stored on-disk in
a directory structure that mimics the Move memory model

### Project structure

Each sandbox command is run in the context of a Move package. So let's create a
Move package that we'll use for the code in this README and `cd` into it:

```shell
$ move new readme
$ cd readme
```

### Compiling and running scripts

Let's first start out with a simple script that prints its [`signer`](https://move-language.github.io/move/signer.html).
Create a file named `sources/debug_script.move` and type the following into it:

```rust
// sources/debug_script.move
script {
use std::debug;
fun debug_script(account: signer) {
    debug::print(&account)
}
}
```

Before we can run this however, we need to import the Move standard library
nursery in order to have access to the `Debug` module and `Std` [named
address](https://move-language.github.io/move/address.html#named-addresses).
You can specify dependencies locally, or using a Git URL. Here, we will specify
it using Git, so add the following to the `Move.toml` file in the `readme`
directory:

```toml
[addresses]
std = "0x1" # Specify and assign 0x1 to the named address "std"

[dependencies]
MoveNursery = { git = "https://github.com/move-language/move.git", subdir = "language/move-stdlib/nursery", rev = "main" }
#                ^                    ^                              ^                                       ^
#            Git dependency       Git clone URL       Subdir under git repo (optional)           Git revision to use
```

Now let's try running the script -- the very first time may take some time since the package command will clone the repository at the given URL, but subsequent calls should be fast:

```shell
$ move sandbox run sources/debug_script.move --signers 0xf
[debug] (&) { 0000000000000000000000000000000F }
```

The `--signers 0xf` argument indicates which account address(es) have signed
off on the script. Omitting `--signers` or passing multiple signers to this
single-`signer` script will trigger a type error.

### Passing arguments

The CLI supports passing non-`signer` arguments to `move sandbox run` via `--args`. The following argument types are supported:
* `bool` literals (`true`, `false`)
* `u64` literals (e.g., `10`, `58`)
* `address` literals (e.g., `0x12`, `0x0000000000000000000000000000000f`)
* hexadecimal strings (e.g., `'x"0012"'` will parse as the `vector<u8>` value `[00, 12]`)
* ASCII strings (e.g., `'b"hi"'` will parse as the `vector<u8>` value `[68, 69]`)

### Publishing new modules

When executing a transaction script you'll often want to call into different
Move modules, like in the example above with the `Debug` module. New modules can
be added to the `sources` directory in the package where the CLI is being
invoked. You can also add dependencies on other packages to have access to the
modules that they define (just like we did with the `Debug` module above). The
`move sandbox run` command will compile and publish each module in the package, and
in each of the package's transitive dependencies, before running the given script.

Try saving this code in `sources/Test.move`:

```rust
// sources/Test.move
module 0x2::Test {
    use std::signer;

    struct Resource has key { i: u64 }

    public fun publish(account: &signer) {
        move_to(account, Resource { i: 10 })
    }

    public fun write(account: &signer, i: u64) acquires Resource {
        borrow_global_mut<Resource>(signer::address_of(account)).i = i;
    }

    public fun unpublish(account: &signer) acquires Resource {
        let Resource { i: _ } = move_from(signer::address_of(account));
  }
}
```

Now, try

```shell
$ move build
```

This will cause the CLI to compile and typecheck the modules under
`sources`, but it won't publish the module bytecode under `storage`. You can
compile and publish the module by running the `move sandbox publish` command
(here we pass the `-v` or verbose flag to get a better understanding of what's
happening):

```shell
$ move sandbox publish -v
Found 1 modules
Publishing a new module 00000000000000000000000000000002::Test (wrote 253 bytes)
Wrote 253 bytes of module ID's and code
```

Now, if we take a look under `storage`, we will see the published bytecode
for our `Test` module:

```shell
$ ls storage/0x00000000000000000000000000000002/modules
Test.mv
```

We can also inspect the compiled bytecode in storage using `move sandbox view`:

```shell
$ move sandbox view storage/0x00000000000000000000000000000002/modules/Test.mv
module 2.Test {
struct Resource has key {
  i: u64
}

public publish() {
  0: MoveLoc[0](Arg0: &signer)
  1: LdU64(10)
  2: Pack[0](Resource)
  3: MoveTo[0](Resource)
  4: Ret
}
public unpublish() {
  0: MoveLoc[0](Arg0: &signer)
  1: Call[3](address_of(&signer): address)
  2: MoveFrom[0](Resource)
  3: Unpack[0](Resource)
  4: Pop
  5: Ret
}
public write() {
  0: CopyLoc[1](Arg1: u64)
  1: MoveLoc[0](Arg0: &signer)
  2: Call[3](address_of(&signer): address)
  3: MutBorrowGlobal[0](Resource)
  4: MutBorrowField[0](Resource.i: u64)
  5: WriteRef
  6: Ret
}
}
```

You can also look at the compiled bytecode before publishing to `storage` by
running either `move disassemble --name <module_name>` or `move
disassemble --name <module_name> --interactive` to interactively inspect the
bytecode and how it relates to the Move source code:

```shell
$ move disassemble --name Test --interactive # You can quit by pressing "q"
$ move disassemble --name Test
// Move bytecode v4
module 2.Test {
struct Resource has key {
        i: u64
}

public publish() {
B0:
        0: MoveLoc[0](account: &signer)
        1: LdU64(10)
        2: Pack[0](Resource)
        3: MoveTo[0](Resource)
        4: Ret
}
public unpublish() {
B0:
        0: MoveLoc[0](account: &signer)
        1: Call[3](address_of(&signer): address)
        2: MoveFrom[0](Resource)
        3: Unpack[0](Resource)
        4: Pop
        5: Ret
}
public write() {
B0:
        0: CopyLoc[1](i: u64)
        1: MoveLoc[0](account: &signer)
        2: Call[3](address_of(&signer): address)
        3: MutBorrowGlobal[0](Resource)
        4: MutBorrowField[0](Resource.i: u64)
        5: WriteRef
        6: Ret
}
}
```

### Updating state

Let's exercise our new `Test` module by running the following script:

```rust
// sources/test_script.move
script {
use 0x2::Test;
fun test_script(account: signer) {
    Test::publish(&account)
}
}
```

This script invokes the `publish` function of our `Test` module, which will
publish a resource of type `Test::Resource` under the signer's account.
Let's first see what this script will change without committing those
changes first. We can do this by passing the `--dry-run` flag:

```shell
$ move sandbox run sources/test_script.move --signers 0xf -v --dry-run
Compiling transaction script...
Changed resource(s) under 1 address(es):
  Changed 1 resource(s) under address 0000000000000000000000000000000F:
    Added type 0x2::Test::Resource: [10, 0, 0, 0, 0, 0, 0, 0] (wrote 40 bytes)
Wrote 40 bytes of resource ID's and data
      key 0x2::Test::Resource {
           i: 10
      }
Discarding changes; re-run without --dry-run if you would like to keep them.
```

Everything looks good, so we can run this again, but this time commit the
changes by removing the `--dry-run` flag:

```shell
$ move sandbox run sources/test_script.move --signers 0xf -v
Compiling transaction script...
Changed resource(s) under 1 address(es):
  Changed 1 resource(s) under address 0000000000000000000000000000000F:
    Added type 0x2::Test::Resource: [10, 0, 0, 0, 0, 0, 0, 0] (wrote 40 bytes)
Wrote 40 bytes of resource ID's and data
      key 0x2::Test::Resource {
            i: 10
      }
```

While the verbose flag used above (`-v`) shows resource changes, it is also
possible to view them manually.
We can inspect the newly published resource using `move sandbox view` since
the change has been committed:

```shell
$ move sandbox view storage/0x0000000000000000000000000000000F/resources/0x00000000000000000000000000000002::Test::Resource.bcs
key 0x2::Test::Resource {
    i: 10
}
```

#### Cleaning state

Since state persists from one call to the Move CLI to another, there will
frequently be times where you want to start again at a clean state.  This
can be done using the `move sandbox clean` command which will remove the
`storage` and `build` directories:

```shell
$ move sandbox view storage/0x0000000000000000000000000000000F/resources/0x00000000000000000000000000000002::Test::Resource.bcs
resource 0x2::Test::Resource {
        i: 10
}
$ move sandbox clean
$ move sandbox view storage/0x0000000000000000000000000000000F/resources/0x00000000000000000000000000000002::Test::Resource.bcs
Error: `move sandbox view <file>` must point to a valid file under storage
```

### Expected Value Testing with the Move CLI

As mentioned previously, Move has a unit testing framework. However, unit tests
cannot test everything -- in particular testing for events cannot be easily
done. To help with writing tests that need to check for events, and expect
specific states, the Move CLI also has a built-in expected-value testing
framework. Each test is run independently in its own sandbox so state does not
persist from one test to another.

Each test is structured as a Move package along with an additional `args.txt` file that
specifies a sequence of Move CLI commands that should be run in that
directory.
Additionally, there must be an `args.exp` file that contain the expected
output from running the sequence of Move CLI commands specified in the
`args.txt` file for that test.

For example, if we wanted to create a Move CLI test that reran all of the
commands that we've seen so far, we could do so by adding an `args.txt`
to the `readme` directory that we created at the start and that we've been
adding scripts and modules to:

```
readme/
├── args.txt
├── Move.toml
└── sources
    ├── debug_script.move
    ├── Test.move
    └── test_script.move
```

And, where the `args.txt` file contains the following Move CLI commands:

```shell
$ cd ..
$ cat readme/args.txt
## Arg files can have comments!
sandbox run sources/debug_script.move --signers 0xf
sandbox run sources/debug_script.move --signers 0xf
build
sandbox publish
sandbox view storage/0x00000000000000000000000000000002/modules/Test.mv
sandbox run sources/test_script.move --signers 0xf -v
sandbox view storage/0x0000000000000000000000000000000F/resources/0x00000000000000000000000000000002::Test::Resource.bcs
```

We can then use the `move sandbox test` command and point it at the `readme` directory to run each of these
Move CLI commands for us in sequence:

```shell
$ move sandbox exp-test -p readme
...<snipped output>
0 / 1 test(s) passed.
Error: 1 / 1 test(s) failed.
```

However, as we see this test will fail since there is no `args.exp` file for the test
yet. We can generate this expectation file by setting the `UPDATE_BASELINE`
environment variable when running the test:

```shell
$ UPDATE_BASELINE=1 move sandbox exp-test -p readme
1 / 1 test(s) passed.
```

There should now be an `args.exp` file under the `readme` directory that
contains the expected output of running the sequence of Move CLI commands
in the `args.txt` file:

```shell
$ cat readme/args.exp
Command `sandbox run sources/debug_script.move --signers 0xf`:
[debug] (&) { 0000000000000000000000000000000F }
Command `sandbox run sources/debug_script.move --signers 0xf --mode bare`:
...
```

#### Testing with code coverage tracking

Code coverage has been an important metric in software testing. In Move CLI expected value tests, we
address the need for code coverage information with an additional flag,
`--track-cov`, that can be passed to the `move sandbox exp-test` command.

Note: To view coverage information, the Move CLI must be installed with the `--debug` flag;
i.e., `cargo install --debug --path move/language/tools/move-cli`.

Using our running example to illustrate:
```shell
$ move sandbox exp-test -p readme --track-cov
1 / 1 test(s) passed.
Module 00000000000000000000000000000002::Test
        fun publish
                total: 5
                covered: 5
                % coverage: 100.00
        fun unpublish
                total: 6
                covered: 0
                % coverage: 0.00
        fun write
                total: 7
                covered: 0
                % coverage: 0.00
>>> % Module coverage: 27.78
```

The output indicates that not only the test is passed, but also that 100%
instruction coverage is observed in the `publish` funciton. This is expected
as the whole purpose of our `test_script.move` is to run the `publish` function.
At the same time, the other two functions, `unpublish` and `write`, are never
executed, making the average coverage 27.78% for the whole `Test` module.

Internally, Move CLI uses the tracing feature provided by the Move VM to record
which instructions in the compiled bytecode are executed and uses this
information to calculate code coverage. Instruction coverage in Move can
usually serve the purpose of line coverage in common C/C++/Rust coverage
tracking tools.

Note that the coverage information is aggregated across multiple `run` commands
in `args.txt`. To illustrate this, suppose that we have another test script,
`test_unpublish_script.move`, under `readme/sources` with the following
content:

```rust
script {
use 0x2::Test;
fun test_unpublish_script(account: signer) {
    Test::unpublish(&account)
}
}
```

We further add a new command to the end of `args.txt`
(`args.exp` needs to be updated too).
```shell
sandbox run sources/test_unpublish_script.move --signers 0xf -v
```

Now we can re-test the `readme` again
```shell
$ move sandbox exp-test -p readme --track-cov
1 / 1 test(s) passed.
Module 00000000000000000000000000000002::Test
        fun publish
                total: 5
                covered: 5
                % coverage: 100.00
        fun unpublish
                total: 6
                covered: 6
                % coverage: 100.00
        fun write
                total: 7
                covered: 0
                % coverage: 0.00
>>> % Module coverage: 61.11
```

This time, note that the `unpublish` function is 100% covered too and the
overall module coverage is boosted to 61.11%.

### Detecting breaking changes

The `move sandbox publish` command automatically detects when upgrading a module may lead to a breaking change.
There are two kinds of breaking changes:

* Linking compatibility (e.g., removing or changing the signature of a public function that is invoked by other modules, removing a
struct or resource type used by other modules)
* Layout compatibility (e.g., adding/removing a resource or struct field)

The breaking changes analysis performed by `move sandbox publish` is necessarily conservative. For example, say we `move sandbox publish` the following
module:

```
address 0x2 {
module M {
    struct S has key { f: u64, g: u64 }
}
}
```

and then wish to upgrade it to the following:

```
address 0x2 {
module M {
    struct S has key { f: u64 }
}
}
```

Running `move sandbox publish` on this new version will fail:

```
Breaking change detected--publishing aborted. Re-run with --ignore-breaking-changes to publish anyway.
Error: Layout API for structs of module 00000000000000000000000000000002::M has changed. Need to do a data migration of published structs
```

In this case, we know we have not published any instances of `S` in global storage, so it is safe to re-run `move sandbox publish --ignore-breaking-changes` (as recommended).
We can double-check that this was not a breaking change by running `move sandbox doctor`.
This handy command runs exhaustive sanity checks on global storage to detect any breaking changes that occurred in the past:
* All modules pass the bytecode verifier
* All modules link against their dependencies
* All resources deserialize according to their declared types
* All events deserialize according to their declared types