code.vegaprotocol.io/vega@v0.79.0/core/staking/stake_verifier.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package staking 17 18 import ( 19 "context" 20 "errors" 21 "sync" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/core/validators" 27 "code.vegaprotocol.io/vega/logging" 28 ) 29 30 const ( 31 // 3 weeks, duration of the whole network at first? 32 timeTilCancel = 24 * 21 * time.Hour 33 ) 34 35 var ( 36 ErrNoStakeDepositedEventFound = errors.New("no stake deposited event found") 37 ErrNoStakeRemovedEventFound = errors.New("no stake removed event found") 38 ErrMissingConfirmations = errors.New("not enough confirmations") 39 ErrInvalidStakeRemovedEventID = errors.New("invalid stake removed event ID") 40 ErrInvalidStakeDepositedEventID = errors.New("invalid stake deposited event ID") 41 ErrDuplicatedStakeDepositedEvent = errors.New("duplicated stake deposited event") 42 ErrDuplicatedStakeRemovedEvent = errors.New("duplicated stake deposited event") 43 ) 44 45 // Witness provide foreign chain resources validations 46 47 type TimeService interface { 48 GetTimeNow() time.Time 49 } 50 51 type EthConfirmations interface { 52 Check(uint64) error 53 } 54 55 type EthOnChainVerifier interface { 56 CheckStakeDeposited(*types.StakeDeposited) error 57 CheckStakeRemoved(*types.StakeRemoved) error 58 GetStakingBridgeAddresses() []string 59 } 60 61 // Witness provide foreign chain resources validations. 62 type Witness interface { 63 StartCheck(validators.Resource, func(interface{}, bool), time.Time) error 64 RestoreResource(validators.Resource, func(interface{}, bool)) error 65 } 66 67 type StakeVerifier struct { 68 log *logging.Logger 69 cfg Config 70 71 accs *Accounting 72 witness Witness 73 timeService TimeService 74 broker Broker 75 76 ocv EthOnChainVerifier 77 ethEventSource EthereumEventSource 78 79 pendingSDs []*pendingSD 80 pendingSRs []*pendingSR 81 finalizedEvents []*types.StakeLinking 82 83 mu sync.Mutex 84 ids map[string]struct{} 85 hashes map[string]struct{} 86 87 // snapshot data 88 svss *stakeVerifierSnapshotState 89 } 90 91 type pendingSD struct { 92 *types.StakeDeposited 93 chainID string 94 check func() error 95 } 96 97 func (p pendingSD) GetID() string { return p.ID } 98 func (p pendingSD) GetChainID() string { return p.chainID } 99 func (p pendingSD) GetType() types.NodeVoteType { return types.NodeVoteTypeStakeDeposited } 100 func (p *pendingSD) Check(ctx context.Context) error { return p.check() } 101 102 type pendingSR struct { 103 *types.StakeRemoved 104 chainID string 105 check func() error 106 } 107 108 func (p pendingSR) GetID() string { return p.ID } 109 func (p pendingSR) GetChainID() string { return p.chainID } 110 func (p pendingSR) GetType() types.NodeVoteType { return types.NodeVoteTypeStakeRemoved } 111 func (p *pendingSR) Check(ctx context.Context) error { return p.check() } 112 113 func NewStakeVerifier( 114 log *logging.Logger, 115 cfg Config, 116 accs *Accounting, 117 witness Witness, 118 ts TimeService, 119 120 broker Broker, 121 onChainVerifier EthOnChainVerifier, 122 ethEventSource EthereumEventSource, 123 ) (sv *StakeVerifier) { 124 log = log.Named("stake-verifier") 125 s := &StakeVerifier{ 126 log: log, 127 cfg: cfg, 128 accs: accs, 129 witness: witness, 130 ocv: onChainVerifier, 131 timeService: ts, 132 broker: broker, 133 ethEventSource: ethEventSource, 134 ids: map[string]struct{}{}, 135 hashes: map[string]struct{}{}, 136 svss: &stakeVerifierSnapshotState{}, 137 } 138 return s 139 } 140 141 func (s *StakeVerifier) ensureNotDuplicate(id, h string) bool { 142 s.mu.Lock() 143 defer s.mu.Unlock() 144 145 if _, ok := s.ids[id]; ok { 146 return false 147 } 148 if _, ok := s.hashes[h]; ok { 149 return false 150 } 151 152 s.ids[id] = struct{}{} 153 s.hashes[h] = struct{}{} 154 155 return true 156 } 157 158 // TODO: address this as the ID/hash map will grow forever now 159 // func (s *StakeVerifier) removeEvent(id string) { 160 // delete(s.ids, id) 161 // } 162 163 func (s *StakeVerifier) ProcessStakeRemoved( 164 ctx context.Context, event *types.StakeRemoved, 165 ) error { 166 if ok := s.ensureNotDuplicate(event.ID, event.IntoStakeLinking().Hash()); !ok { 167 s.log.Error("stake removed event already exists", 168 logging.String("event", event.String())) 169 return ErrDuplicatedStakeRemovedEvent 170 } 171 172 pending := &pendingSR{ 173 StakeRemoved: event, 174 chainID: s.accs.chainID, 175 check: func() error { return s.ocv.CheckStakeRemoved(event) }, 176 } 177 s.pendingSRs = append(s.pendingSRs, pending) 178 evt := pending.IntoStakeLinking() 179 evt.Status = types.StakeLinkingStatusPending 180 s.broker.Send(events.NewStakeLinking(ctx, *evt)) 181 182 s.log.Info("stake removed event received, starting validation", 183 logging.String("event", event.String())) 184 185 return s.witness.StartCheck( 186 pending, s.onEventVerified, s.timeService.GetTimeNow().Add(timeTilCancel)) 187 } 188 189 func (s *StakeVerifier) ProcessStakeDeposited( 190 ctx context.Context, event *types.StakeDeposited, 191 ) error { 192 if ok := s.ensureNotDuplicate(event.ID, event.IntoStakeLinking().Hash()); !ok { 193 s.log.Error("stake deposited event already exists", 194 logging.String("event", event.String())) 195 return ErrDuplicatedStakeDepositedEvent 196 } 197 198 pending := &pendingSD{ 199 StakeDeposited: event, 200 chainID: s.accs.chainID, 201 check: func() error { return s.ocv.CheckStakeDeposited(event) }, 202 } 203 204 s.pendingSDs = append(s.pendingSDs, pending) 205 206 evt := pending.IntoStakeLinking() 207 evt.Status = types.StakeLinkingStatusPending 208 s.broker.Send(events.NewStakeLinking(ctx, *evt)) 209 210 s.log.Info("stake deposited event received, starting validation", 211 logging.String("event", event.String())) 212 213 return s.witness.StartCheck( 214 pending, s.onEventVerified, s.timeService.GetTimeNow().Add(timeTilCancel)) 215 } 216 217 func (s *StakeVerifier) removePendingStakeDeposited(id string) error { 218 for i, v := range s.pendingSDs { 219 if v.ID == id { 220 s.pendingSDs = s.pendingSDs[:i+copy(s.pendingSDs[i:], s.pendingSDs[i+1:])] 221 return nil 222 } 223 } 224 return ErrInvalidStakeDepositedEventID 225 } 226 227 func (s *StakeVerifier) removePendingStakeRemoved(id string) error { 228 for i, v := range s.pendingSRs { 229 if v.ID == id { 230 s.pendingSRs = s.pendingSRs[:i+copy(s.pendingSRs[i:], s.pendingSRs[i+1:])] 231 return nil 232 } 233 } 234 return ErrInvalidStakeRemovedEventID 235 } 236 237 func (s *StakeVerifier) getLastBlockSeen() uint64 { 238 var block uint64 239 for _, p := range s.pendingSDs { 240 if block == 0 { 241 block = p.BlockNumber 242 continue 243 } 244 245 if p.BlockNumber < block { 246 block = p.BlockNumber 247 } 248 } 249 250 for _, p := range s.pendingSRs { 251 if block == 0 { 252 block = p.BlockNumber 253 continue 254 } 255 256 if p.BlockNumber < block { 257 block = p.BlockNumber 258 } 259 } 260 261 return block 262 } 263 264 func (s *StakeVerifier) onEventVerified(event interface{}, ok bool) { 265 var evt *types.StakeLinking 266 switch pending := event.(type) { 267 case *pendingSD: 268 evt = pending.IntoStakeLinking() 269 if err := s.removePendingStakeDeposited(evt.ID); err != nil { 270 s.log.Error("could not remove pending stake deposited event", logging.Error(err)) 271 } 272 case *pendingSR: 273 evt = pending.IntoStakeLinking() 274 if err := s.removePendingStakeRemoved(evt.ID); err != nil { 275 s.log.Error("could not remove pending stake removed event", logging.Error(err)) 276 } 277 default: 278 s.log.Error("stake verifier received invalid event") 279 return 280 } 281 282 evt.Status = types.StakeLinkingStatusRejected 283 if ok { 284 evt.Status = types.StakeLinkingStatusAccepted 285 } 286 evt.FinalizedAt = s.timeService.GetTimeNow().UnixNano() 287 s.finalizedEvents = append(s.finalizedEvents, evt) 288 } 289 290 func (s *StakeVerifier) OnTick(ctx context.Context, t time.Time) { 291 for _, evt := range s.finalizedEvents { 292 if evt.Status == types.StakeLinkingStatusAccepted { 293 s.accs.AddEvent(ctx, evt) 294 for _, addresss := range s.ocv.GetStakingBridgeAddresses() { 295 s.ethEventSource.UpdateContractBlock(addresss, s.accs.chainID, evt.BlockHeight) 296 } 297 } 298 299 s.log.Info("stake linking finalized", 300 logging.String("status", evt.Status.String()), 301 logging.String("event", evt.String())) 302 s.broker.Send(events.NewStakeLinking(ctx, *evt)) 303 } 304 s.finalizedEvents = nil 305 }