guardianset.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package common
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
  7. "github.com/ethereum/go-ethereum/common"
  8. "github.com/libp2p/go-libp2p/core/peer"
  9. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  10. "github.com/prometheus/client_golang/prometheus"
  11. "github.com/prometheus/client_golang/prometheus/promauto"
  12. )
  13. var (
  14. gsIndex = promauto.NewGauge(
  15. prometheus.GaugeOpts{
  16. Name: "wormhole_guardian_set_index",
  17. Help: "The guardians set index",
  18. })
  19. gsSigners = promauto.NewGauge(
  20. prometheus.GaugeOpts{
  21. Name: "wormhole_guardian_set_signers",
  22. Help: "Number of signers in the guardian set.",
  23. })
  24. )
  25. // MaxGuardianCount specifies the maximum number of guardians supported by on-chain contracts.
  26. //
  27. // Matching constants:
  28. // - MAX_LEN_GUARDIAN_KEYS in Solana contract (limited by transaction size - 19 is the maximum amount possible)
  29. //
  30. // The Eth and Terra contracts do not specify a maximum number and support more than that,
  31. // but presumably, chain-specific transaction size limits will apply at some point (untested).
  32. const MaxGuardianCount = 19
  33. // MaxNodesPerGuardian specifies the maximum amount of nodes per guardian key that we'll accept
  34. // whenever we maintain any per-guardian, per-node state.
  35. //
  36. // There currently isn't any state clean up, so the value is on the high side to prevent
  37. // accidentally reaching the limit due to operational mistakes.
  38. const MaxNodesPerGuardian = 15
  39. // MaxStateAge specified the maximum age of state entries in seconds. Expired entries are purged
  40. // from the state by Cleanup().
  41. const MaxStateAge = 1 * time.Minute
  42. type GuardianSet struct {
  43. // Guardian's public key hashes truncated by the ETH standard hashing mechanism (20 bytes).
  44. Keys []common.Address
  45. // On-chain set index
  46. Index uint32
  47. // quorum value for this set of keys
  48. quorum int
  49. // A map from address to index. Testing showed that, on average, a map is almost three times faster than a sequential search of the key slice.
  50. // Testing also showed that the map was twice as fast as using a sorted slice and `slices.BinarySearchFunc`. That being said, on a 4GHz CPU,
  51. // the sequential search takes an average of 800 nanos and the map look up takes about 260 nanos. Is this worth doing?
  52. keyMap map[common.Address]int
  53. }
  54. // Quorum returns the current quorum value.
  55. func (gs *GuardianSet) Quorum() int {
  56. return gs.quorum
  57. }
  58. func NewGuardianSet(keys []common.Address, index uint32) *GuardianSet {
  59. keyMap := map[common.Address]int{}
  60. for idx, key := range keys {
  61. keyMap[key] = idx
  62. }
  63. return &GuardianSet{
  64. Keys: keys,
  65. Index: index,
  66. quorum: vaa.CalculateQuorum(len(keys)),
  67. keyMap: keyMap,
  68. }
  69. }
  70. func (g *GuardianSet) KeysAsHexStrings() []string {
  71. r := make([]string, len(g.Keys))
  72. for n, k := range g.Keys {
  73. r[n] = k.Hex()
  74. }
  75. return r
  76. }
  77. // KeyIndex returns a given address index from the guardian set. Returns (-1, false)
  78. // if the address wasn't found and (addr, true) otherwise.
  79. func (g *GuardianSet) KeyIndex(addr common.Address) (int, bool) {
  80. if g.keyMap != nil {
  81. if idx, found := g.keyMap[addr]; found {
  82. return idx, true
  83. }
  84. } else {
  85. for n, k := range g.Keys {
  86. if k == addr {
  87. return n, true
  88. }
  89. }
  90. }
  91. return -1, false
  92. }
  93. type GuardianSetState struct {
  94. mu sync.Mutex
  95. current *GuardianSet
  96. // Last heartbeat message received per guardian per p2p node. Maintained
  97. // across guardian set updates - these values don't change.
  98. lastHeartbeats map[common.Address]map[peer.ID]*gossipv1.Heartbeat
  99. updateC chan *gossipv1.Heartbeat
  100. }
  101. // NewGuardianSetState returns a new GuardianSetState.
  102. //
  103. // The provided channel will be pushed heartbeat updates as they are set,
  104. // but be aware that the channel will block guardian set updates if full.
  105. func NewGuardianSetState(guardianSetStateUpdateC chan *gossipv1.Heartbeat) *GuardianSetState {
  106. return &GuardianSetState{
  107. lastHeartbeats: map[common.Address]map[peer.ID]*gossipv1.Heartbeat{},
  108. updateC: guardianSetStateUpdateC,
  109. }
  110. }
  111. func (st *GuardianSetState) Set(set *GuardianSet) {
  112. st.mu.Lock()
  113. gsIndex.Set(float64(set.Index))
  114. gsSigners.Set(float64(len(set.Keys)))
  115. defer st.mu.Unlock()
  116. st.current = set
  117. }
  118. func (st *GuardianSetState) Get() *GuardianSet {
  119. st.mu.Lock()
  120. defer st.mu.Unlock()
  121. return st.current
  122. }
  123. // LastHeartbeat returns the most recent heartbeat message received for
  124. // a given guardian node, or nil if none have been received.
  125. func (st *GuardianSetState) LastHeartbeat(addr common.Address) map[peer.ID]*gossipv1.Heartbeat {
  126. st.mu.Lock()
  127. defer st.mu.Unlock()
  128. ret := make(map[peer.ID]*gossipv1.Heartbeat)
  129. for k, v := range st.lastHeartbeats[addr] {
  130. ret[k] = v
  131. }
  132. return ret
  133. }
  134. // SetHeartbeat stores a verified heartbeat observed by a given guardian.
  135. func (st *GuardianSetState) SetHeartbeat(addr common.Address, peerId peer.ID, hb *gossipv1.Heartbeat) error {
  136. st.mu.Lock()
  137. defer st.mu.Unlock()
  138. v, ok := st.lastHeartbeats[addr]
  139. if !ok {
  140. v = make(map[peer.ID]*gossipv1.Heartbeat)
  141. st.lastHeartbeats[addr] = v
  142. } else {
  143. if len(v) >= MaxNodesPerGuardian {
  144. // TODO: age out old entries?
  145. return fmt.Errorf("too many nodes (%d) for guardian, cannot store entry", len(v))
  146. }
  147. }
  148. v[peerId] = hb
  149. if st.updateC != nil {
  150. WriteToChannelWithoutBlocking(st.updateC, hb, "heartbeat")
  151. }
  152. return nil
  153. }
  154. // GetAll returns all stored heartbeats.
  155. func (st *GuardianSetState) GetAll() map[common.Address]map[peer.ID]*gossipv1.Heartbeat {
  156. st.mu.Lock()
  157. defer st.mu.Unlock()
  158. ret := make(map[common.Address]map[peer.ID]*gossipv1.Heartbeat)
  159. // Deep copy
  160. for addr, v := range st.lastHeartbeats {
  161. ret[addr] = make(map[peer.ID]*gossipv1.Heartbeat)
  162. for peerId, hb := range v {
  163. ret[addr][peerId] = hb
  164. }
  165. }
  166. return ret
  167. }
  168. // Cleanup removes expired entries from the state.
  169. func (st *GuardianSetState) Cleanup() {
  170. st.mu.Lock()
  171. defer st.mu.Unlock()
  172. for addr, v := range st.lastHeartbeats {
  173. for peerId, hb := range v {
  174. ts := time.Unix(0, hb.Timestamp)
  175. if time.Since(ts) > MaxStateAge {
  176. delete(st.lastHeartbeats[addr], peerId)
  177. }
  178. }
  179. }
  180. }
  181. // IsSubscribedToHeartbeats returns true if the heartbeat update channel is set.
  182. func (st *GuardianSetState) IsSubscribedToHeartbeats() bool {
  183. return st.updateC != nil
  184. }