github.com/dominant-strategies/go-quai@v0.28.2/quaiclient/quaiclient.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package ethclient provides a client for the Quai RPC API. 18 package quaiclient 19 20 import ( 21 "context" 22 "encoding/json" 23 "fmt" 24 "math/big" 25 "time" 26 27 quai "github.com/dominant-strategies/go-quai" 28 "github.com/dominant-strategies/go-quai/common" 29 "github.com/dominant-strategies/go-quai/common/hexutil" 30 "github.com/dominant-strategies/go-quai/core/types" 31 "github.com/dominant-strategies/go-quai/log" 32 "github.com/dominant-strategies/go-quai/rpc" 33 ) 34 35 var exponentialBackoffCeilingSecs int64 = 60 // 1 minute 36 37 // Client defines typed wrappers for the Quai RPC API. 38 type Client struct { 39 c *rpc.Client 40 } 41 42 // Dial connects a client to the given URL. 43 func Dial(rawurl string) (*Client, error) { 44 return DialContext(context.Background(), rawurl) 45 } 46 47 func DialContext(ctx context.Context, rawurl string) (*Client, error) { 48 connectStatus := false 49 attempts := 0 50 51 var c *rpc.Client 52 var err error 53 for !connectStatus { 54 c, err = rpc.DialContext(ctx, rawurl) 55 if err == nil { 56 break 57 } 58 59 attempts += 1 60 // exponential back-off implemented 61 // delaySecs := int64(math.Floor((math.Pow(2, float64(attempts)) - 1) * 0.5)) 62 delaySecs := int64(1) 63 if delaySecs > exponentialBackoffCeilingSecs { 64 return nil, err 65 } 66 67 // should only get here if the ffmpeg record stream process dies 68 log.Warn("Attempting to connect to go-quai node. Waiting and retrying...", "attempts", attempts, "delay", delaySecs, "url", rawurl) 69 70 time.Sleep(time.Duration(delaySecs) * time.Second) 71 } 72 73 return NewClient(c), nil 74 } 75 76 // NewClient creates a client that uses the given RPC client. 77 func NewClient(c *rpc.Client) *Client { 78 return &Client{c} 79 } 80 81 func (ec *Client) Close() { 82 ec.c.Close() 83 } 84 85 type Termini struct { 86 Termini []common.Hash `json:"termini"` 87 } 88 89 type appendReturns struct { 90 Etxs types.Transactions `json:"pendingEtxs"` 91 SubReorg bool `json:"subReorg"` 92 SetHead bool `json:"setHead"` 93 } 94 95 // SubscribePendingHeader subscribes to notifications about the current pending block on the node. 96 func (ec *Client) SubscribePendingHeader(ctx context.Context, ch chan<- *types.Header) (quai.Subscription, error) { 97 return ec.c.QuaiSubscribe(ctx, ch, "pendingHeader") 98 } 99 100 func (ec *Client) Append(ctx context.Context, header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) { 101 fields := map[string]interface{}{ 102 "header": header.RPCMarshalHeader(), 103 "manifest": manifest, 104 "domPendingHeader": domPendingHeader.RPCMarshalHeader(), 105 "domTerminus": domTerminus, 106 "domOrigin": domOrigin, 107 "newInboundEtxs": newInboundEtxs, 108 } 109 110 var raw json.RawMessage 111 err := ec.c.CallContext(ctx, &raw, "quai_append", fields) 112 if err != nil { 113 return nil, false, false, err 114 } 115 116 // Decode header and transactions. 117 var aReturns appendReturns 118 if err := json.Unmarshal(raw, &aReturns); err != nil { 119 return nil, false, false, err 120 } 121 122 return aReturns.Etxs, aReturns.SubReorg, aReturns.SetHead, nil 123 } 124 125 func (ec *Client) DownloadBlocksInManifest(ctx context.Context, hash common.Hash, manifest types.BlockManifest, entropy *big.Int) { 126 fields := map[string]interface{}{ 127 "hash": hash, 128 "manifest": manifest, 129 "entropy": entropy, 130 } 131 ec.c.CallContext(ctx, nil, "quai_downloadBlocksInManifest", fields) 132 } 133 134 func (ec *Client) SubRelayPendingHeader(ctx context.Context, pendingHeader types.PendingHeader, newEntropy *big.Int, location common.Location, subReorg bool, order int) { 135 data := map[string]interface{}{"header": pendingHeader.Header().RPCMarshalHeader()} 136 data["NewEntropy"] = newEntropy 137 data["termini"] = pendingHeader.Termini().RPCMarshalTermini() 138 data["Location"] = location 139 data["SubReorg"] = subReorg 140 data["Order"] = order 141 142 ec.c.CallContext(ctx, nil, "quai_subRelayPendingHeader", data) 143 } 144 145 func (ec *Client) UpdateDom(ctx context.Context, oldTerminus common.Hash, pendingHeader types.PendingHeader, location common.Location) { 146 data := map[string]interface{}{"header": pendingHeader.Header().RPCMarshalHeader()} 147 data["OldTerminus"] = oldTerminus 148 data["Location"] = location 149 data["termini"] = pendingHeader.Termini().RPCMarshalTermini() 150 151 ec.c.CallContext(ctx, nil, "quai_updateDom", data) 152 } 153 154 func (ec *Client) RequestDomToAppendOrFetch(ctx context.Context, hash common.Hash, entropy *big.Int, order int) { 155 data := map[string]interface{}{"Hash": hash} 156 data["Entropy"] = entropy 157 data["Order"] = order 158 159 ec.c.CallContext(ctx, nil, "quai_requestDomToAppendOrFetch", data) 160 } 161 162 func (ec *Client) NewGenesisPendingHeader(ctx context.Context, header *types.Header) { 163 ec.c.CallContext(ctx, nil, "quai_newGenesisPendingHeader", header.RPCMarshalHeader()) 164 } 165 166 // GetManifest will get the block manifest ending with the parent hash 167 func (ec *Client) GetManifest(ctx context.Context, blockHash common.Hash) (types.BlockManifest, error) { 168 var raw json.RawMessage 169 err := ec.c.CallContext(ctx, &raw, "quai_getManifest", blockHash) 170 if err != nil { 171 return nil, err 172 } 173 var manifest types.BlockManifest 174 if err := json.Unmarshal(raw, &manifest); err != nil { 175 return nil, err 176 } 177 return manifest, nil 178 } 179 180 // GetPendingEtxsRollupFromSub gets the pendingEtxsRollup from the region 181 func (ec *Client) GetPendingEtxsRollupFromSub(ctx context.Context, hash common.Hash, location common.Location) (types.PendingEtxsRollup, error) { 182 fields := make(map[string]interface{}) 183 fields["Hash"] = hash 184 fields["Location"] = location 185 186 var raw json.RawMessage 187 err := ec.c.CallContext(ctx, &raw, "quai_getPendingEtxsRollupFromSub", fields) 188 if err != nil { 189 return types.PendingEtxsRollup{}, err 190 } 191 192 var pEtxsRollup types.PendingEtxsRollup 193 if err := json.Unmarshal(raw, &pEtxsRollup); err != nil { 194 return types.PendingEtxsRollup{}, err 195 } 196 return pEtxsRollup, nil 197 } 198 199 // GetPendingEtxsFromSub gets the pendingEtxsRollup from the region 200 func (ec *Client) GetPendingEtxsFromSub(ctx context.Context, hash common.Hash, location common.Location) (types.PendingEtxs, error) { 201 fields := make(map[string]interface{}) 202 fields["Hash"] = hash 203 fields["Location"] = location 204 205 var raw json.RawMessage 206 err := ec.c.CallContext(ctx, &raw, "quai_getPendingEtxsFromSub", fields) 207 if err != nil { 208 return types.PendingEtxs{}, err 209 } 210 211 var pEtxs types.PendingEtxs 212 if err := json.Unmarshal(raw, &pEtxs); err != nil { 213 return types.PendingEtxs{}, err 214 } 215 return pEtxs, nil 216 } 217 218 func (ec *Client) SendPendingEtxsToDom(ctx context.Context, pEtxs types.PendingEtxs) error { 219 fields := make(map[string]interface{}) 220 fields["header"] = pEtxs.Header.RPCMarshalHeader() 221 fields["etxs"] = pEtxs.Etxs 222 var raw json.RawMessage 223 err := ec.c.CallContext(ctx, &raw, "quai_sendPendingEtxsToDom", fields) 224 if err != nil { 225 return err 226 } 227 return nil 228 } 229 230 func (ec *Client) SendPendingEtxsRollupToDom(ctx context.Context, pEtxsRollup types.PendingEtxsRollup) error { 231 fields := make(map[string]interface{}) 232 fields["header"] = pEtxsRollup.Header.RPCMarshalHeader() 233 fields["manifest"] = pEtxsRollup.Manifest 234 var raw json.RawMessage 235 return ec.c.CallContext(ctx, &raw, "quai_sendPendingEtxsRollupToDom", fields) 236 } 237 238 func (ec *Client) GenerateRecoveryPendingHeader(ctx context.Context, pendingHeader *types.Header, checkpointHashes types.Termini) error { 239 fields := make(map[string]interface{}) 240 fields["pendingHeader"] = pendingHeader.RPCMarshalHeader() 241 fields["checkpointHashes"] = checkpointHashes.RPCMarshalTermini() 242 return ec.c.CallContext(ctx, nil, "quai_generateRecoveryPendingHeader", fields) 243 } 244 245 func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) *types.Header { 246 var raw json.RawMessage 247 ec.c.CallContext(ctx, &raw, "quai_getHeaderByHash", hash) 248 var header *types.Header 249 if err := json.Unmarshal(raw, &header); err != nil { 250 return nil 251 } 252 return header 253 } 254 255 func (ec *Client) HeaderByNumber(ctx context.Context, number string) *types.Header { 256 var raw json.RawMessage 257 ec.c.CallContext(ctx, &raw, "quai_getHeaderByNumber", number) 258 var header *types.Header 259 if err := json.Unmarshal(raw, &header); err != nil { 260 return nil 261 } 262 return header 263 } 264 265 func (ec *Client) SetSyncTarget(ctx context.Context, header *types.Header) { 266 fields := header.RPCMarshalHeader() 267 ec.c.CallContext(ctx, nil, "quai_setSyncTarget", fields) 268 } 269 270 //// Miner APIS 271 272 // GetPendingHeader gets the latest pending header from the chain. 273 func (ec *Client) GetPendingHeader(ctx context.Context) (*types.Header, error) { 274 var pendingHeader *types.Header 275 err := ec.c.CallContext(ctx, &pendingHeader, "quai_getPendingHeader") 276 if err != nil { 277 return nil, err 278 } 279 return pendingHeader, nil 280 } 281 282 // ReceiveMinedHeader sends a mined block back to the node 283 func (ec *Client) ReceiveMinedHeader(ctx context.Context, header *types.Header) error { 284 data := header.RPCMarshalHeader() 285 return ec.c.CallContext(ctx, nil, "quai_receiveMinedHeader", data) 286 } 287 288 // Filters 289 290 // SubscribeFilterLogs subscribes to the results of a streaming filter query. 291 func (ec *Client) SubscribeFilterLogs(ctx context.Context, q quai.FilterQuery, ch chan<- types.Log) (quai.Subscription, error) { 292 arg, err := toFilterArg(q) 293 if err != nil { 294 return nil, err 295 } 296 return ec.c.QuaiSubscribe(ctx, ch, "logs", arg) 297 } 298 299 func toFilterArg(q quai.FilterQuery) (interface{}, error) { 300 arg := map[string]interface{}{ 301 "address": q.Addresses, 302 "topics": q.Topics, 303 } 304 if q.BlockHash != nil { 305 arg["blockHash"] = *q.BlockHash 306 if q.FromBlock != nil || q.ToBlock != nil { 307 return nil, fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock") 308 } 309 } else { 310 if q.FromBlock == nil { 311 arg["fromBlock"] = "0x0" 312 } else { 313 arg["fromBlock"] = toBlockNumArg(q.FromBlock) 314 } 315 arg["toBlock"] = toBlockNumArg(q.ToBlock) 316 } 317 return arg, nil 318 } 319 320 func toBlockNumArg(number *big.Int) string { 321 if number == nil { 322 return "latest" 323 } 324 pending := big.NewInt(-1) 325 if number.Cmp(pending) == 0 { 326 return "pending" 327 } 328 return hexutil.EncodeBig(number) 329 } 330 331 func toCallArg(msg quai.CallMsg) interface{} { 332 arg := map[string]interface{}{ 333 "from": msg.From, 334 "to": msg.To, 335 } 336 if len(msg.Data) > 0 { 337 arg["data"] = hexutil.Bytes(msg.Data) 338 } 339 if msg.Value != nil { 340 arg["value"] = (*hexutil.Big)(msg.Value) 341 } 342 if msg.Gas != 0 { 343 arg["gas"] = hexutil.Uint64(msg.Gas) 344 } 345 if msg.GasPrice != nil { 346 arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 347 } 348 return arg 349 } 350 351 // FilterLogs executes a filter query. 352 func (ec *Client) FilterLogs(ctx context.Context, q quai.FilterQuery) ([]types.Log, error) { 353 var result []types.Log 354 arg, err := toFilterArg(q) 355 if err != nil { 356 return nil, err 357 } 358 err = ec.c.CallContext(ctx, &result, "quai_getLogs", arg) 359 return result, err 360 }