github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/dlc/oracle.go (about)

     1  package dlc
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  )
    11  
    12  // DlcOracle contains the identifying data of an Oracle
    13  type DlcOracle struct {
    14  	Idx  uint64   // Index of the oracle for refencing in commands
    15  	A    [33]byte // public key of the oracle
    16  	Name string   // Name of the oracle for display purposes
    17  	Url  string   // Base URL of the oracle, if its REST based (optional)
    18  }
    19  
    20  // AddOracle manually imports an oracle using the pubkey (A) and a name for
    21  // reference purposes
    22  func (mgr *DlcManager) AddOracle(key [33]byte, name string) (*DlcOracle, error) {
    23  	var err error
    24  
    25  	o := new(DlcOracle)
    26  	o.A = key
    27  	o.Url = ""
    28  	o.Name = name
    29  	err = mgr.SaveOracle(o)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	return o, nil
    35  }
    36  
    37  // FindOracleByKey function looks up an oracle based on its key
    38  func (mgr *DlcManager) FindOracleByKey(key [33]byte) (*DlcOracle, error) {
    39  	oracles, err := mgr.ListOracles()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	for _, o := range oracles {
    45  		if bytes.Equal(o.A[:], key[:]) {
    46  			return o, nil
    47  		}
    48  	}
    49  
    50  	return nil, fmt.Errorf("Oracle not found")
    51  }
    52  
    53  // DlcOracleRestPubkeyResponse is the response format for the REST API that
    54  // returns the pubkey
    55  type DlcOracleRestPubkeyResponse struct {
    56  	AHex string `json:"A"`
    57  }
    58  
    59  // ImportOracle imports an oracle using a REST endpoint. It will save the oracle
    60  // in the database and give it the passed name.
    61  func (mgr *DlcManager) ImportOracle(url string, name string) (*DlcOracle, error) {
    62  	req, err := http.NewRequest("GET", url+"/api/pubkey", nil)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	client := &http.Client{}
    67  	resp, err := client.Do(req)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	defer resp.Body.Close()
    72  
    73  	var response DlcOracleRestPubkeyResponse
    74  
    75  	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	o := new(DlcOracle)
    80  	A, err := hex.DecodeString(response.AHex)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	copy(o.A[:], A[:])
    86  	o.Url = url
    87  	o.Name = name
    88  	err = mgr.SaveOracle(o)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	return o, nil
    94  }
    95  
    96  // DlcOracleRPointResponse is the response format for the REST API that returns
    97  // the R-point
    98  type DlcOracleRPointResponse struct {
    99  	RHex string `json:"R"`
   100  }
   101  
   102  // FetchRPoint retrieves the R-point based on datafeedID and timestamp
   103  // (unix epoch) from the REST API of the oracle.
   104  func (o *DlcOracle) FetchRPoint(datafeedId, timestamp uint64) ([33]byte, error) {
   105  	var rPoint [33]byte
   106  	if len(o.Url) == 0 {
   107  		return rPoint, fmt.Errorf("Oracle was not imported from the web -" +
   108  			" cannot fetch R point. Enter manually using the" +
   109  			" [dlc contract setrpoint] command")
   110  	}
   111  
   112  	url := fmt.Sprintf("%s/api/rpoint/%d/%d", o.Url, datafeedId, timestamp)
   113  
   114  	req, err := http.NewRequest("GET", url, nil)
   115  	if err != nil {
   116  		return rPoint, err
   117  	}
   118  	client := &http.Client{}
   119  	resp, err := client.Do(req)
   120  	if err != nil {
   121  		return rPoint, err
   122  	}
   123  	defer resp.Body.Close()
   124  
   125  	var response DlcOracleRPointResponse
   126  
   127  	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
   128  		return rPoint, err
   129  	}
   130  
   131  	R, err := hex.DecodeString(response.RHex)
   132  	if err != nil {
   133  		return rPoint, err
   134  	}
   135  
   136  	copy(rPoint[:], R[:])
   137  	return rPoint, nil
   138  
   139  }
   140  
   141  // DlcOracleFromBytes parses a byte array that was serialized using
   142  // DlcOracle.Bytes() back into a DlcOracle struct
   143  func DlcOracleFromBytes(b []byte) (*DlcOracle, error) {
   144  	buf := bytes.NewBuffer(b)
   145  	o := new(DlcOracle)
   146  
   147  	copy(o.A[:], buf.Next(33))
   148  
   149  	var nameLen uint32
   150  	err := binary.Read(buf, binary.BigEndian, &nameLen)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	o.Name = string(buf.Next(int(nameLen)))
   155  
   156  	var urlLen uint32
   157  	err = binary.Read(buf, binary.BigEndian, &urlLen)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	o.Url = string(buf.Next(int(urlLen)))
   162  
   163  	return o, nil
   164  }
   165  
   166  // Bytes serializes a DlcOracle struct into a byte array
   167  func (self *DlcOracle) Bytes() []byte {
   168  	var buf bytes.Buffer
   169  
   170  	buf.Write(self.A[:])
   171  
   172  	nameBytes := []byte(self.Name)
   173  	nameLen := uint32(len(nameBytes))
   174  	binary.Write(&buf, binary.BigEndian, nameLen)
   175  	buf.Write(nameBytes)
   176  
   177  	urlBytes := []byte(self.Url)
   178  	urlLen := uint32(len(urlBytes))
   179  	binary.Write(&buf, binary.BigEndian, urlLen)
   180  	buf.Write(urlBytes)
   181  
   182  	return buf.Bytes()
   183  }