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  }