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

height.gno

4.82 Kb · 160 lines
  1package types
  2
  3import (
  4	"encoding/binary"
  5	"errors"
  6	"strconv"
  7	"strings"
  8)
  9
 10// Height is a blockchain height, optionally prefixed with a revision number as
 11// per the IBC specification. A revision of zero is treated as "no revision":
 12// it is omitted from the string form and ignored during comparison, mirroring
 13// the Rust `Option<NonZeroU64>` revision semantics.
 14type Height struct {
 15	// the revision that the client is currently on
 16	RevisionNumber uint64
 17	// the height within the given revision
 18	RevisionHeight uint64
 19}
 20
 21// NewHeight returns a Height with no revision.
 22func NewHeight(height uint64) Height {
 23	return Height{RevisionNumber: 0, RevisionHeight: height}
 24}
 25
 26// NewHeightWithRevision returns a Height carrying both a revision and a height.
 27func NewHeightWithRevision(revision, height uint64) Height {
 28	return Height{RevisionNumber: revision, RevisionHeight: height}
 29}
 30
 31// IsZero reports whether the height is the zero value (no revision, zero
 32// height). Union packets require a zero timeout height.
 33func (h Height) IsZero() bool {
 34	return h.RevisionNumber == 0 && h.RevisionHeight == 0
 35}
 36
 37// HasRevision reports whether the height carries a non-zero revision number.
 38func (h Height) HasRevision() bool {
 39	return h.RevisionNumber != 0
 40}
 41
 42// GetRevision returns the revision number, or zero when unset.
 43func (h Height) GetRevision() uint64 {
 44	return h.RevisionNumber
 45}
 46
 47// GetHeight returns the height number.
 48func (h Height) GetHeight() uint64 {
 49	return h.RevisionHeight
 50}
 51
 52// RevisionMatches reports whether two heights share the same revision. When
 53// either side has no revision the revisions are considered compatible.
 54func (h Height) RevisionMatches(other Height) bool {
 55	if h.RevisionNumber == 0 || other.RevisionNumber == 0 {
 56		return true
 57	}
 58
 59	return h.RevisionNumber == other.RevisionNumber
 60}
 61
 62// Increment returns a copy of the height advanced by one, preserving revision.
 63func (h Height) Increment() Height {
 64	return Height{RevisionNumber: h.RevisionNumber, RevisionHeight: h.RevisionHeight + 1}
 65}
 66
 67// IncrementBy returns a copy of the height advanced by n, preserving revision.
 68func (h Height) IncrementBy(n uint64) Height {
 69	return Height{RevisionNumber: h.RevisionNumber, RevisionHeight: h.RevisionHeight + n}
 70}
 71
 72// Cmp compares two heights, returning -1, 0, or 1. When both heights carry a
 73// revision, revisions are compared first and heights break the tie; otherwise
 74// only the height numbers are compared.
 75func (h Height) Cmp(other Height) int {
 76	if h.RevisionNumber != 0 && other.RevisionNumber != 0 {
 77		switch {
 78		case h.RevisionNumber < other.RevisionNumber:
 79			return -1
 80		case h.RevisionNumber > other.RevisionNumber:
 81			return 1
 82		}
 83	}
 84
 85	switch {
 86	case h.RevisionHeight < other.RevisionHeight:
 87		return -1
 88	case h.RevisionHeight > other.RevisionHeight:
 89		return 1
 90	default:
 91		return 0
 92	}
 93}
 94
 95// LT reports whether h is strictly less than other.
 96func (h Height) LT(other Height) bool { return h.Cmp(other) < 0 }
 97
 98// LTE reports whether h is less than or equal to other.
 99func (h Height) LTE(other Height) bool { return h.Cmp(other) <= 0 }
100
101// GT reports whether h is strictly greater than other.
102func (h Height) GT(other Height) bool { return h.Cmp(other) > 0 }
103
104// GTE reports whether h is greater than or equal to other.
105func (h Height) GTE(other Height) bool { return h.Cmp(other) >= 0 }
106
107// EQ reports whether h equals other under height comparison semantics.
108func (h Height) EQ(other Height) bool { return h.Cmp(other) == 0 }
109
110// String renders the height as "revision-height" when a revision is set, or as
111// the bare height otherwise.
112func (h Height) String() string {
113	if h.RevisionNumber != 0 {
114		return strconv.FormatUint(h.RevisionNumber, 10) + "-" + strconv.FormatUint(h.RevisionHeight, 10)
115	}
116
117	return strconv.FormatUint(h.RevisionHeight, 10)
118}
119
120// Bytes returns a 16-byte big-endian encoding of the height (revision then
121// height), suitable as a BPTree key with correct lexicographic ordering when
122// cast to string.
123func (h Height) Bytes() []byte {
124	buf := make([]byte, 16)
125	binary.BigEndian.PutUint64(buf[:8], h.RevisionNumber)
126	binary.BigEndian.PutUint64(buf[8:], h.RevisionHeight)
127
128	return buf
129}
130
131// ParseHeight parses a height string of the form "revision-height" or a bare
132// "height". A "revision-height" form always carries its revision, even when it
133// is zero.
134func ParseHeight(s string) (Height, error) {
135	if s == "" {
136		return Height{}, errors.New("types: invalid height string")
137	}
138
139	revStr, heightStr, found := strings.Cut(s, "-")
140	if !found {
141		height, err := strconv.ParseUint(s, 10, 64)
142		if err != nil {
143			return Height{}, err
144		}
145
146		return Height{RevisionNumber: 0, RevisionHeight: height}, nil
147	}
148
149	revision, err := strconv.ParseUint(revStr, 10, 64)
150	if err != nil {
151		return Height{}, err
152	}
153
154	height, err := strconv.ParseUint(heightStr, 10, 64)
155	if err != nil {
156		return Height{}, err
157	}
158
159	return Height{RevisionNumber: revision, RevisionHeight: height}, nil
160}