hlverifier.gno
3.95 Kb · 113 lines
1// Package hlverifier is the launch verifier of bridge/PLAN.md §5.1: the ed25519 check of the signers' signatures
2// over a message's Hyperlane checkpoint, under the rule r/hlmailbox keeps beside its signer set — a threshold over
3// the set, or §11.13's two groups (one key of group A and one of group B must sign). It registers itself with the
4// mailbox at deploy and keeps no state: the signers and the rule are the mailbox's, read at every check, so a
5// rotation there is a rotation here. Satisfiable answers whether a signer set could still meet the rule, which is
6// what makes a signer's removal at once or a 7-day wait (§5.3).
7package hlverifier
8
9import (
10 "crypto/ed25519"
11 "errors"
12 "strconv"
13
14 hl "gno.land/p/g1zdaz6sypk5tdsjc2fzsqrrn4ttn8qzmnl4waqp/hyperlane/v0"
15 "gno.land/r/g1zdaz6sypk5tdsjc2fzsqrrn4ttn8qzmnl4waqp/hlmailbox"
16)
17
18const pkgPath = "gno.land/r/g1zdaz6sypk5tdsjc2fzsqrrn4ttn8qzmnl4waqp/hlverifier" // must match gnomod.toml
19
20// verifier is the hlmailbox.Verifier this realm registers.
21type verifier struct{}
22
23func init(cur realm) {
24 hlmailbox.RegisterVerifier(cross(cur), verifier{})
25}
26
27// Verify checks the metadata — Hyperlane's checkpoint head (hook 32 ‖ root 32 ‖ index 4), then 65 bytes a
28// signature: the signer's index in the set, then its ed25519 signature over the checkpoint digest of this message
29// — and the rule: every signature present must verify, in increasing signer order, and the signers that signed
30// must meet the rule.
31func (verifier) Verify(origin uint32, metadata, message []byte) error {
32 keys, rule := hlmailbox.Signers()
33 if len(keys) == 0 {
34 return errors.New("hlverifier: no signers")
35 }
36 m, err := hl.ParseMessage(message)
37 if err != nil {
38 return err
39 }
40 if m.Origin != origin {
41 return errors.New("hlverifier: the message is from domain " + strconv.FormatUint(uint64(m.Origin), 10) + ", not " + strconv.FormatUint(uint64(origin), 10))
42 }
43 c, sigs, err := hl.ParseMultisigMetadata(metadata)
44 if err != nil {
45 return err
46 }
47 if len(sigs) == 0 {
48 return errors.New("hlverifier: no signatures")
49 }
50 digest := hl.CheckpointDigest(m.Origin, c.Hook, c.Root, c.Index, hl.ID(message))
51 var signed []hlmailbox.Key
52 last := -1
53 for _, s := range sigs {
54 i := int(s.Validator)
55 if i <= last {
56 return errors.New("hlverifier: signatures out of signer order")
57 }
58 if i >= len(keys) {
59 return errors.New("hlverifier: a signature names no signer")
60 }
61 k := keys[i]
62 if !ed25519.Verify(k[:], digest[:], s.Sig[:]) {
63 return errors.New("hlverifier: a signature does not verify")
64 }
65 signed = append(signed, k)
66 last = i
67 }
68 if !satisfies(rule, signed) {
69 return errors.New("hlverifier: the signatures do not meet the rule: " + rule.String())
70 }
71 return nil
72}
73
74// Satisfiable is whether a signer set could meet the rule the mailbox keeps now: the threshold many of them, or
75// one of each group among them.
76func (verifier) Satisfiable(signers []hlmailbox.Key) bool {
77 _, rule := hlmailbox.Signers()
78 return satisfies(rule, signers)
79}
80
81func satisfies(rule hlmailbox.Rule, signed []hlmailbox.Key) bool {
82 if rule.TwoGroups() {
83 return anyOf(rule.GroupA, signed) && anyOf(rule.GroupB, signed)
84 }
85 return rule.Threshold >= 1 && len(signed) >= rule.Threshold
86}
87
88func anyOf(group, signed []hlmailbox.Key) bool {
89 for _, g := range group {
90 for _, s := range signed {
91 if g == s {
92 return true
93 }
94 }
95 }
96 return false
97}
98
99// Registered is whether the mailbox holds this realm as a verifier.
100func Registered() bool { return hlmailbox.VerifierRegistered(pkgPath) }
101
102// Render: the rule enforced now.
103func Render(path string) string {
104 keys, rule := hlmailbox.Signers()
105 active := ""
106 if hlmailbox.ActiveVerifier(hlmailbox.LaunchOrigin()) == pkgPath {
107 active = "the active verifier"
108 } else {
109 active = "registered, not the active verifier"
110 }
111 return "# The ed25519 verifier\n\n" + active + " for origin " + strconv.FormatUint(uint64(hlmailbox.LaunchOrigin()), 10) +
112 ": " + strconv.Itoa(len(keys)) + " signers in r/hlmailbox under the rule " + rule.String() + "\n"
113}