| 1 | // Package moves holds the move protocol's data shapes and arithmetic |
| 2 | // (move-any-app design rev 7.18). It imports no activator, registry |
| 3 | // writer or console: everything here is pure so the box, the console and |
| 4 | // the CLI agree on one definition. |
| 5 | package moves |
| 6 | |
| 7 | import ( |
| 8 | "encoding/base32" |
| 9 | "fmt" |
| 10 | "io" |
| 11 | "regexp" |
| 12 | ) |
| 13 | |
| 14 | // opIDShape is §3: a role prefix and 26 lowercase base32 characters (16 |
| 15 | // random bytes, no padding). |
| 16 | // |
| 17 | // Twin of provision.moveIDShape and provision.rollbackIDShape: the copy is |
| 18 | // forced, because moves imports provision, so provision cannot import |
| 19 | // moves. TestOpIDShapeTwinsProvision holds the three together. |
| 20 | var opIDShape = regexp.MustCompile(`^(mv|rb)_[a-z2-7]{26}$`) |
| 21 | |
| 22 | var lowerBase32 = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding) |
| 23 | |
| 24 | func newID(prefix string, r io.Reader) (string, error) { |
| 25 | var b [16]byte |
| 26 | if _, err := io.ReadFull(r, b[:]); err != nil { |
| 27 | return "", fmt.Errorf("moves: mint %s id: %w", prefix, err) |
| 28 | } |
| 29 | return prefix + "_" + lowerBase32.EncodeToString(b[:]), nil |
| 30 | } |
| 31 | |
| 32 | // NewMoveID mints a move id. The console mints it at the request door and |
| 33 | // it names the operation in the claim, the record and every staging key. |
| 34 | func NewMoveID(r io.Reader) (string, error) { return newID("mv", r) } |
| 35 | |
| 36 | // NewRollbackID mints a rollback id, minted by whichever actor wins the |
| 37 | // rollback CAS (§7.8). |
| 38 | func NewRollbackID(r io.Reader) (string, error) { return newID("rb", r) } |
| 39 | |
| 40 | // ValidOpID reports whether id has §3's shape. Op ids are opaque beyond |
| 41 | // this: they are never compared for order (claim rev 6.1 Q1). |
| 42 | func ValidOpID(id string) bool { return opIDShape.MatchString(id) } |
| 43 | |
| 44 | // OpIDKind is "move", "rollback" or "" for an id of neither shape. |
| 45 | func OpIDKind(id string) string { |
| 46 | switch { |
| 47 | case !ValidOpID(id): |
| 48 | return "" |
| 49 | case id[:3] == "mv_": |
| 50 | return "move" |
| 51 | default: |
| 52 | return "rollback" |
| 53 | } |
| 54 | } |
| 55 | |