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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}