code.vegaprotocol.io/vega@v0.79.0/core/integration/stubs/state_var_stub.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 stubs
    17  
    18  import (
    19  	"context"
    20  	"math/rand"
    21  	"sort"
    22  	"strconv"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/types/statevar"
    26  )
    27  
    28  var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    29  
    30  type StateVarStub struct {
    31  	seq                 int
    32  	currentTime         time.Time
    33  	svs                 map[string]*sv
    34  	eventTypeToStateVar map[statevar.EventType][]*sv
    35  	rng                 *rand.Rand
    36  	readyForTimeTrigger map[string]struct{}
    37  	stateVarToNextCalc  map[string]time.Time
    38  	updateFrequency     time.Duration
    39  }
    40  
    41  func NewStateVar() *StateVarStub {
    42  	return &StateVarStub{
    43  		seq:                 0,
    44  		svs:                 map[string]*sv{},
    45  		eventTypeToStateVar: map[statevar.EventType][]*sv{},
    46  		rng:                 rand.New(rand.NewSource(0)),
    47  		readyForTimeTrigger: map[string]struct{}{},
    48  		stateVarToNextCalc:  map[string]time.Time{},
    49  	}
    50  }
    51  
    52  type sv struct {
    53  	ID               string
    54  	asset            string
    55  	market           string
    56  	converter        statevar.Converter
    57  	startCalculation func(string, statevar.FinaliseCalculation)
    58  	trigger          []statevar.EventType
    59  	result           func(context.Context, statevar.StateVariableResult) error
    60  	eventID          string
    61  }
    62  
    63  func (e *StateVarStub) OnFloatingPointUpdatesDurationUpdate(ctx context.Context, updateFrequency time.Duration) error {
    64  	e.updateFrequency = updateFrequency
    65  	return nil
    66  }
    67  
    68  func (e *StateVarStub) UnregisterStateVariable(asset, market string) {
    69  }
    70  
    71  func (e *StateVarStub) RegisterStateVariable(asset, market, name string, converter statevar.Converter, startCalculation func(string, statevar.FinaliseCalculation), trigger []statevar.EventType, result func(context.Context, statevar.StateVariableResult) error) error {
    72  	ID := asset + "_" + market + "_" + name + "_" + strconv.Itoa(e.seq)
    73  	e.seq++
    74  	e.svs[ID] = &sv{
    75  		ID:               ID,
    76  		asset:            asset,
    77  		market:           market,
    78  		converter:        converter,
    79  		startCalculation: startCalculation,
    80  		trigger:          trigger,
    81  		result:           result,
    82  	}
    83  	for _, t := range trigger {
    84  		if _, ok := e.eventTypeToStateVar[t]; !ok {
    85  			e.eventTypeToStateVar[t] = []*sv{}
    86  		}
    87  		e.eventTypeToStateVar[t] = append(e.eventTypeToStateVar[t], e.svs[ID])
    88  	}
    89  	e.ReadyForTimeTrigger(asset, market)
    90  	return nil
    91  }
    92  
    93  func (e *StateVarStub) NewEvent(asset, market string, eventType statevar.EventType) {
    94  	eventID := e.generateID(asset, market)
    95  	for _, s := range e.eventTypeToStateVar[eventType] {
    96  		if s.market != market || s.asset != asset {
    97  			continue
    98  		}
    99  		s.eventID = eventID
   100  		s.startCalculation(eventID, s)
   101  		if _, ok := e.stateVarToNextCalc[s.ID]; ok {
   102  			e.stateVarToNextCalc[s.ID] = e.currentTime.Add(e.updateFrequency)
   103  		}
   104  	}
   105  }
   106  
   107  func (s *sv) CalculationFinished(eventID string, result statevar.StateVariableResult, err error) {
   108  	if err == nil {
   109  		s.result(context.Background(), result)
   110  	}
   111  }
   112  
   113  func (e *StateVarStub) ReadyForTimeTrigger(asset, mktID string) {
   114  	if _, ok := e.readyForTimeTrigger[asset+mktID]; !ok {
   115  		e.readyForTimeTrigger[mktID] = struct{}{}
   116  		for _, s := range e.eventTypeToStateVar[statevar.EventTypeTimeTrigger] {
   117  			if s.asset == asset && s.market == mktID {
   118  				e.stateVarToNextCalc[s.ID] = e.currentTime.Add(e.updateFrequency)
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func (e *StateVarStub) OnTick(ctx context.Context, t time.Time) {
   125  	e.currentTime = t
   126  	stateVarIDs := []string{}
   127  	for ID, nextTime := range e.stateVarToNextCalc {
   128  		if nextTime.UnixNano() <= t.UnixNano() {
   129  			stateVarIDs = append(stateVarIDs, ID)
   130  		}
   131  	}
   132  
   133  	sort.Strings(stateVarIDs)
   134  	eventID := t.Format("20060102_150405.999999999")
   135  	for _, ID := range stateVarIDs {
   136  		s := e.svs[ID]
   137  		s.startCalculation(eventID, s)
   138  		e.stateVarToNextCalc[ID] = t.Add(e.updateFrequency)
   139  	}
   140  }
   141  
   142  // generate a random 32 chars identifier.
   143  func (e *StateVarStub) generateID(asset, market string) string {
   144  	b := make([]rune, 32)
   145  	for i := range b {
   146  		b[i] = chars[e.rng.Intn(len(chars))]
   147  	}
   148  	return asset + "_" + market + "_" + string(b)
   149  }