code.vegaprotocol.io/vega@v0.79.0/core/datasource/common/data.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  //lint:file-ignore ST1003 Ignore underscores in names, this is straigh copied from the proto package to ease introducing the domain types
    17  
    18  package common
    19  
    20  import (
    21  	"fmt"
    22  	"strconv"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  	"code.vegaprotocol.io/vega/logging"
    27  
    28  	"go.uber.org/zap"
    29  )
    30  
    31  // Data holds normalized data coming from an oracle.
    32  type Data struct {
    33  	// EthKey is currently just the spec id, which is suboptimal as multiple specs calling the same
    34  	// contracts at the same time will duplicate data. This is a temporary solution until we have
    35  	// a better method of making a key of (contract address, args, block height/time + previous height/time)
    36  	// 'previous' being required so that receivers can check if the their trigger would have fired.
    37  	EthKey   string
    38  	Signers  []*Signer
    39  	Data     map[string]string
    40  	MetaData map[string]string
    41  }
    42  
    43  func (d Data) GetUint(name string) (*num.Uint, error) {
    44  	value, ok := d.Data[name]
    45  	if !ok {
    46  		return nil, errPropertyNotFound(name)
    47  	}
    48  	val, fail := num.UintFromString(value, 10)
    49  	if fail {
    50  		return nil, errInvalidString(name, value)
    51  	}
    52  	return val, nil
    53  }
    54  
    55  // GetInteger converts the value associated to propertyName into an integer.
    56  func (d Data) GetInteger(propertyName string) (*num.Int, error) {
    57  	value, ok := d.Data[propertyName]
    58  	if !ok {
    59  		return num.IntZero(), errPropertyNotFound(propertyName)
    60  	}
    61  	return ToInteger(value)
    62  }
    63  
    64  // GetDecimal converts the value associated to propertyName into a decimal.
    65  func (d Data) GetDecimal(propertyName string) (num.Decimal, error) {
    66  	value, ok := d.Data[propertyName]
    67  	if !ok {
    68  		return num.DecimalZero(), errPropertyNotFound(propertyName)
    69  	}
    70  	return ToDecimal(value)
    71  }
    72  
    73  // GetBoolean converts the value associated to propertyName into a boolean.
    74  func (d Data) GetBoolean(propertyName string) (bool, error) {
    75  	value, ok := d.Data[propertyName]
    76  	if !ok {
    77  		return false, errPropertyNotFound(propertyName)
    78  	}
    79  	return ToBoolean(value)
    80  }
    81  
    82  // GetString returns the value associated to propertyName.
    83  func (d Data) GetString(propertyName string) (string, error) {
    84  	value, ok := d.Data[propertyName]
    85  	if !ok {
    86  		return "", errPropertyNotFound(propertyName)
    87  	}
    88  	return value, nil
    89  }
    90  
    91  // GetTimestamp converts the value associated to propertyName into a timestamp.
    92  func (d Data) GetTimestamp(propertyName string) (int64, error) {
    93  	value, ok := d.Data[propertyName]
    94  	if !ok {
    95  		return 0, errPropertyNotFound(propertyName)
    96  	}
    97  	return ToTimestamp(value)
    98  }
    99  
   100  // GetDataTimestampNano gets the eth block time (or vega time) associated with the oracle data.
   101  func (d Data) GetDataTimestampNano() (int64, error) {
   102  	if ebt, ok := d.MetaData["eth-block-time"]; ok {
   103  		// add price point with "eth-block-time" as time
   104  		pt, err := strconv.ParseInt(ebt, 10, 64)
   105  		if err != nil {
   106  			return 0, err
   107  		}
   108  		return time.Unix(pt, 0).UnixNano(), nil
   109  	}
   110  	// open oracle timestamp
   111  	if oot, ok := d.MetaData["open-oracle-timestamp"]; ok {
   112  		pt, err := strconv.ParseInt(oot, 10, 64)
   113  		if err != nil {
   114  			return 0, err
   115  		}
   116  		return time.Unix(pt, 0).UnixNano(), nil
   117  	}
   118  	// fall back to vega time
   119  	if vt, ok := d.MetaData["vega-time"]; ok {
   120  		t, err := strconv.ParseInt(vt, 10, 64)
   121  		if err != nil {
   122  			return 0, err
   123  		}
   124  		return time.Unix(t, 0).UnixNano(), nil
   125  	}
   126  	return 0, fmt.Errorf("data has no timestamp data")
   127  }
   128  
   129  // FromInternalOracle returns true if the oracle data has been emitted by an
   130  // internal oracle.
   131  func (d Data) FromInternalOracle() bool {
   132  	return len(d.Signers) == 0
   133  }
   134  
   135  func (d Data) Debug() []zap.Field {
   136  	keys := ""
   137  	for _, key := range d.Signers {
   138  		keys += key.String() + " "
   139  	}
   140  
   141  	fields := []zap.Field{
   142  		logging.String("Signers", keys),
   143  	}
   144  	for property, value := range d.Data {
   145  		fields = append(fields, logging.String(property, value))
   146  	}
   147  	return fields
   148  }
   149  
   150  // errPropertyNotFound is returned when the property is not present in the Data.
   151  func errPropertyNotFound(propertyName string) error {
   152  	return fmt.Errorf("property \"%s\" not found", propertyName)
   153  }
   154  
   155  func errInvalidString(name, val string) error {
   156  	return fmt.Errorf("could not parse value '%s' for property '%s'", val, name)
   157  }