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 }