github.com/jimmyx0x/go-ethereum@v1.10.28/graphql/graphql.go (about) 1 // Copyright 2019 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 graphql provides a GraphQL interface to Ethereum node data. 18 package graphql 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "math/big" 25 "sort" 26 "strconv" 27 28 "github.com/ethereum/go-ethereum" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/common/hexutil" 31 "github.com/ethereum/go-ethereum/common/math" 32 "github.com/ethereum/go-ethereum/consensus/misc" 33 "github.com/ethereum/go-ethereum/core/state" 34 "github.com/ethereum/go-ethereum/core/types" 35 "github.com/ethereum/go-ethereum/eth/filters" 36 "github.com/ethereum/go-ethereum/internal/ethapi" 37 "github.com/ethereum/go-ethereum/rlp" 38 "github.com/ethereum/go-ethereum/rpc" 39 ) 40 41 var ( 42 errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash") 43 ) 44 45 type Long int64 46 47 // ImplementsGraphQLType returns true if Long implements the provided GraphQL type. 48 func (b Long) ImplementsGraphQLType(name string) bool { return name == "Long" } 49 50 // UnmarshalGraphQL unmarshals the provided GraphQL query data. 51 func (b *Long) UnmarshalGraphQL(input interface{}) error { 52 var err error 53 switch input := input.(type) { 54 case string: 55 // uncomment to support hex values 56 //if strings.HasPrefix(input, "0x") { 57 // // apply leniency and support hex representations of longs. 58 // value, err := hexutil.DecodeUint64(input) 59 // *b = Long(value) 60 // return err 61 //} else { 62 value, err := strconv.ParseInt(input, 10, 64) 63 *b = Long(value) 64 return err 65 //} 66 case int32: 67 *b = Long(input) 68 case int64: 69 *b = Long(input) 70 case float64: 71 *b = Long(input) 72 default: 73 err = fmt.Errorf("unexpected type %T for Long", input) 74 } 75 return err 76 } 77 78 // Account represents an Ethereum account at a particular block. 79 type Account struct { 80 r *Resolver 81 address common.Address 82 blockNrOrHash rpc.BlockNumberOrHash 83 } 84 85 // getState fetches the StateDB object for an account. 86 func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { 87 state, _, err := a.r.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash) 88 return state, err 89 } 90 91 func (a *Account) Address(ctx context.Context) (common.Address, error) { 92 return a.address, nil 93 } 94 95 func (a *Account) Balance(ctx context.Context) (hexutil.Big, error) { 96 state, err := a.getState(ctx) 97 if err != nil { 98 return hexutil.Big{}, err 99 } 100 balance := state.GetBalance(a.address) 101 if balance == nil { 102 return hexutil.Big{}, fmt.Errorf("failed to load balance %x", a.address) 103 } 104 return hexutil.Big(*balance), nil 105 } 106 107 func (a *Account) TransactionCount(ctx context.Context) (hexutil.Uint64, error) { 108 // Ask transaction pool for the nonce which includes pending transactions 109 if blockNr, ok := a.blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber { 110 nonce, err := a.r.backend.GetPoolNonce(ctx, a.address) 111 if err != nil { 112 return 0, err 113 } 114 return hexutil.Uint64(nonce), nil 115 } 116 state, err := a.getState(ctx) 117 if err != nil { 118 return 0, err 119 } 120 return hexutil.Uint64(state.GetNonce(a.address)), nil 121 } 122 123 func (a *Account) Code(ctx context.Context) (hexutil.Bytes, error) { 124 state, err := a.getState(ctx) 125 if err != nil { 126 return hexutil.Bytes{}, err 127 } 128 return state.GetCode(a.address), nil 129 } 130 131 func (a *Account) Storage(ctx context.Context, args struct{ Slot common.Hash }) (common.Hash, error) { 132 state, err := a.getState(ctx) 133 if err != nil { 134 return common.Hash{}, err 135 } 136 return state.GetState(a.address, args.Slot), nil 137 } 138 139 // Log represents an individual log message. All arguments are mandatory. 140 type Log struct { 141 r *Resolver 142 transaction *Transaction 143 log *types.Log 144 } 145 146 func (l *Log) Transaction(ctx context.Context) *Transaction { 147 return l.transaction 148 } 149 150 func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account { 151 return &Account{ 152 r: l.r, 153 address: l.log.Address, 154 blockNrOrHash: args.NumberOrLatest(), 155 } 156 } 157 158 func (l *Log) Index(ctx context.Context) int32 { 159 return int32(l.log.Index) 160 } 161 162 func (l *Log) Topics(ctx context.Context) []common.Hash { 163 return l.log.Topics 164 } 165 166 func (l *Log) Data(ctx context.Context) hexutil.Bytes { 167 return l.log.Data 168 } 169 170 // AccessTuple represents EIP-2930 171 type AccessTuple struct { 172 address common.Address 173 storageKeys []common.Hash 174 } 175 176 func (at *AccessTuple) Address(ctx context.Context) common.Address { 177 return at.address 178 } 179 180 func (at *AccessTuple) StorageKeys(ctx context.Context) []common.Hash { 181 return at.storageKeys 182 } 183 184 // Transaction represents an Ethereum transaction. 185 // backend and hash are mandatory; all others will be fetched when required. 186 type Transaction struct { 187 r *Resolver 188 hash common.Hash 189 tx *types.Transaction 190 block *Block 191 index uint64 192 } 193 194 // resolve returns the internal transaction object, fetching it if needed. 195 func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) { 196 if t.tx == nil { 197 // Try to return an already finalized transaction 198 tx, blockHash, _, index, err := t.r.backend.GetTransaction(ctx, t.hash) 199 if err == nil && tx != nil { 200 t.tx = tx 201 blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false) 202 t.block = &Block{ 203 r: t.r, 204 numberOrHash: &blockNrOrHash, 205 } 206 t.index = index 207 return t.tx, nil 208 } 209 // No finalized transaction, try to retrieve it from the pool 210 t.tx = t.r.backend.GetPoolTransaction(t.hash) 211 } 212 return t.tx, nil 213 } 214 215 func (t *Transaction) Hash(ctx context.Context) common.Hash { 216 return t.hash 217 } 218 219 func (t *Transaction) InputData(ctx context.Context) (hexutil.Bytes, error) { 220 tx, err := t.resolve(ctx) 221 if err != nil || tx == nil { 222 return hexutil.Bytes{}, err 223 } 224 return tx.Data(), nil 225 } 226 227 func (t *Transaction) Gas(ctx context.Context) (hexutil.Uint64, error) { 228 tx, err := t.resolve(ctx) 229 if err != nil || tx == nil { 230 return 0, err 231 } 232 return hexutil.Uint64(tx.Gas()), nil 233 } 234 235 func (t *Transaction) GasPrice(ctx context.Context) (hexutil.Big, error) { 236 tx, err := t.resolve(ctx) 237 if err != nil || tx == nil { 238 return hexutil.Big{}, err 239 } 240 switch tx.Type() { 241 case types.AccessListTxType: 242 return hexutil.Big(*tx.GasPrice()), nil 243 case types.DynamicFeeTxType: 244 if t.block != nil { 245 if baseFee, _ := t.block.BaseFeePerGas(ctx); baseFee != nil { 246 // price = min(tip, gasFeeCap - baseFee) + baseFee 247 return (hexutil.Big)(*math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee.ToInt()), tx.GasFeeCap())), nil 248 } 249 } 250 return hexutil.Big(*tx.GasPrice()), nil 251 default: 252 return hexutil.Big(*tx.GasPrice()), nil 253 } 254 } 255 256 func (t *Transaction) EffectiveGasPrice(ctx context.Context) (*hexutil.Big, error) { 257 tx, err := t.resolve(ctx) 258 if err != nil || tx == nil { 259 return nil, err 260 } 261 // Pending tx 262 if t.block == nil { 263 return nil, nil 264 } 265 header, err := t.block.resolveHeader(ctx) 266 if err != nil || header == nil { 267 return nil, err 268 } 269 if header.BaseFee == nil { 270 return (*hexutil.Big)(tx.GasPrice()), nil 271 } 272 return (*hexutil.Big)(math.BigMin(new(big.Int).Add(tx.GasTipCap(), header.BaseFee), tx.GasFeeCap())), nil 273 } 274 275 func (t *Transaction) MaxFeePerGas(ctx context.Context) (*hexutil.Big, error) { 276 tx, err := t.resolve(ctx) 277 if err != nil || tx == nil { 278 return nil, err 279 } 280 switch tx.Type() { 281 case types.AccessListTxType: 282 return nil, nil 283 case types.DynamicFeeTxType: 284 return (*hexutil.Big)(tx.GasFeeCap()), nil 285 default: 286 return nil, nil 287 } 288 } 289 290 func (t *Transaction) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) { 291 tx, err := t.resolve(ctx) 292 if err != nil || tx == nil { 293 return nil, err 294 } 295 switch tx.Type() { 296 case types.AccessListTxType: 297 return nil, nil 298 case types.DynamicFeeTxType: 299 return (*hexutil.Big)(tx.GasTipCap()), nil 300 default: 301 return nil, nil 302 } 303 } 304 305 func (t *Transaction) EffectiveTip(ctx context.Context) (*hexutil.Big, error) { 306 tx, err := t.resolve(ctx) 307 if err != nil || tx == nil { 308 return nil, err 309 } 310 // Pending tx 311 if t.block == nil { 312 return nil, nil 313 } 314 header, err := t.block.resolveHeader(ctx) 315 if err != nil || header == nil { 316 return nil, err 317 } 318 if header.BaseFee == nil { 319 return (*hexutil.Big)(tx.GasPrice()), nil 320 } 321 322 tip, err := tx.EffectiveGasTip(header.BaseFee) 323 if err != nil { 324 return nil, err 325 } 326 return (*hexutil.Big)(tip), nil 327 } 328 329 func (t *Transaction) Value(ctx context.Context) (hexutil.Big, error) { 330 tx, err := t.resolve(ctx) 331 if err != nil || tx == nil { 332 return hexutil.Big{}, err 333 } 334 if tx.Value() == nil { 335 return hexutil.Big{}, fmt.Errorf("invalid transaction value %x", t.hash) 336 } 337 return hexutil.Big(*tx.Value()), nil 338 } 339 340 func (t *Transaction) Nonce(ctx context.Context) (hexutil.Uint64, error) { 341 tx, err := t.resolve(ctx) 342 if err != nil || tx == nil { 343 return 0, err 344 } 345 return hexutil.Uint64(tx.Nonce()), nil 346 } 347 348 func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, error) { 349 tx, err := t.resolve(ctx) 350 if err != nil || tx == nil { 351 return nil, err 352 } 353 to := tx.To() 354 if to == nil { 355 return nil, nil 356 } 357 return &Account{ 358 r: t.r, 359 address: *to, 360 blockNrOrHash: args.NumberOrLatest(), 361 }, nil 362 } 363 364 func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account, error) { 365 tx, err := t.resolve(ctx) 366 if err != nil || tx == nil { 367 return nil, err 368 } 369 signer := types.LatestSigner(t.r.backend.ChainConfig()) 370 from, _ := types.Sender(signer, tx) 371 return &Account{ 372 r: t.r, 373 address: from, 374 blockNrOrHash: args.NumberOrLatest(), 375 }, nil 376 } 377 378 func (t *Transaction) Block(ctx context.Context) (*Block, error) { 379 if _, err := t.resolve(ctx); err != nil { 380 return nil, err 381 } 382 return t.block, nil 383 } 384 385 func (t *Transaction) Index(ctx context.Context) (*int32, error) { 386 if _, err := t.resolve(ctx); err != nil { 387 return nil, err 388 } 389 if t.block == nil { 390 return nil, nil 391 } 392 index := int32(t.index) 393 return &index, nil 394 } 395 396 // getReceipt returns the receipt associated with this transaction, if any. 397 func (t *Transaction) getReceipt(ctx context.Context) (*types.Receipt, error) { 398 if _, err := t.resolve(ctx); err != nil { 399 return nil, err 400 } 401 if t.block == nil { 402 return nil, nil 403 } 404 receipts, err := t.block.resolveReceipts(ctx) 405 if err != nil { 406 return nil, err 407 } 408 return receipts[t.index], nil 409 } 410 411 func (t *Transaction) Status(ctx context.Context) (*Long, error) { 412 receipt, err := t.getReceipt(ctx) 413 if err != nil || receipt == nil { 414 return nil, err 415 } 416 if len(receipt.PostState) != 0 { 417 return nil, nil 418 } 419 ret := Long(receipt.Status) 420 return &ret, nil 421 } 422 423 func (t *Transaction) GasUsed(ctx context.Context) (*Long, error) { 424 receipt, err := t.getReceipt(ctx) 425 if err != nil || receipt == nil { 426 return nil, err 427 } 428 ret := Long(receipt.GasUsed) 429 return &ret, nil 430 } 431 432 func (t *Transaction) CumulativeGasUsed(ctx context.Context) (*Long, error) { 433 receipt, err := t.getReceipt(ctx) 434 if err != nil || receipt == nil { 435 return nil, err 436 } 437 ret := Long(receipt.CumulativeGasUsed) 438 return &ret, nil 439 } 440 441 func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs) (*Account, error) { 442 receipt, err := t.getReceipt(ctx) 443 if err != nil || receipt == nil || receipt.ContractAddress == (common.Address{}) { 444 return nil, err 445 } 446 return &Account{ 447 r: t.r, 448 address: receipt.ContractAddress, 449 blockNrOrHash: args.NumberOrLatest(), 450 }, nil 451 } 452 453 func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) { 454 if _, err := t.resolve(ctx); err != nil { 455 return nil, err 456 } 457 if t.block == nil { 458 return nil, nil 459 } 460 if _, ok := t.block.numberOrHash.Hash(); !ok { 461 header, err := t.r.backend.HeaderByNumberOrHash(ctx, *t.block.numberOrHash) 462 if err != nil { 463 return nil, err 464 } 465 hash := header.Hash() 466 t.block.numberOrHash.BlockHash = &hash 467 } 468 return t.getLogs(ctx) 469 } 470 471 // getLogs returns log objects for the given tx. 472 // Assumes block hash is resolved. 473 func (t *Transaction) getLogs(ctx context.Context) (*[]*Log, error) { 474 var ( 475 hash, _ = t.block.numberOrHash.Hash() 476 filter = t.r.filterSystem.NewBlockFilter(hash, nil, nil) 477 logs, err = filter.Logs(ctx) 478 ) 479 if err != nil { 480 return nil, err 481 } 482 var ret []*Log 483 // Select tx logs from all block logs 484 ix := sort.Search(len(logs), func(i int) bool { return uint64(logs[i].TxIndex) >= t.index }) 485 for ix < len(logs) && uint64(logs[ix].TxIndex) == t.index { 486 ret = append(ret, &Log{ 487 r: t.r, 488 transaction: t, 489 log: logs[ix], 490 }) 491 ix++ 492 } 493 return &ret, nil 494 } 495 496 func (t *Transaction) Type(ctx context.Context) (*int32, error) { 497 tx, err := t.resolve(ctx) 498 if err != nil { 499 return nil, err 500 } 501 txType := int32(tx.Type()) 502 return &txType, nil 503 } 504 505 func (t *Transaction) AccessList(ctx context.Context) (*[]*AccessTuple, error) { 506 tx, err := t.resolve(ctx) 507 if err != nil || tx == nil { 508 return nil, err 509 } 510 accessList := tx.AccessList() 511 ret := make([]*AccessTuple, 0, len(accessList)) 512 for _, al := range accessList { 513 ret = append(ret, &AccessTuple{ 514 address: al.Address, 515 storageKeys: al.StorageKeys, 516 }) 517 } 518 return &ret, nil 519 } 520 521 func (t *Transaction) R(ctx context.Context) (hexutil.Big, error) { 522 tx, err := t.resolve(ctx) 523 if err != nil || tx == nil { 524 return hexutil.Big{}, err 525 } 526 _, r, _ := tx.RawSignatureValues() 527 return hexutil.Big(*r), nil 528 } 529 530 func (t *Transaction) S(ctx context.Context) (hexutil.Big, error) { 531 tx, err := t.resolve(ctx) 532 if err != nil || tx == nil { 533 return hexutil.Big{}, err 534 } 535 _, _, s := tx.RawSignatureValues() 536 return hexutil.Big(*s), nil 537 } 538 539 func (t *Transaction) V(ctx context.Context) (hexutil.Big, error) { 540 tx, err := t.resolve(ctx) 541 if err != nil || tx == nil { 542 return hexutil.Big{}, err 543 } 544 v, _, _ := tx.RawSignatureValues() 545 return hexutil.Big(*v), nil 546 } 547 548 func (t *Transaction) Raw(ctx context.Context) (hexutil.Bytes, error) { 549 tx, err := t.resolve(ctx) 550 if err != nil || tx == nil { 551 return hexutil.Bytes{}, err 552 } 553 return tx.MarshalBinary() 554 } 555 556 func (t *Transaction) RawReceipt(ctx context.Context) (hexutil.Bytes, error) { 557 receipt, err := t.getReceipt(ctx) 558 if err != nil || receipt == nil { 559 return hexutil.Bytes{}, err 560 } 561 return receipt.MarshalBinary() 562 } 563 564 type BlockType int 565 566 // Block represents an Ethereum block. 567 // backend, and numberOrHash are mandatory. All other fields are lazily fetched 568 // when required. 569 type Block struct { 570 r *Resolver 571 numberOrHash *rpc.BlockNumberOrHash 572 hash common.Hash 573 header *types.Header 574 block *types.Block 575 receipts []*types.Receipt 576 } 577 578 // resolve returns the internal Block object representing this block, fetching 579 // it if necessary. 580 func (b *Block) resolve(ctx context.Context) (*types.Block, error) { 581 if b.block != nil { 582 return b.block, nil 583 } 584 if b.numberOrHash == nil { 585 latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) 586 b.numberOrHash = &latest 587 } 588 var err error 589 b.block, err = b.r.backend.BlockByNumberOrHash(ctx, *b.numberOrHash) 590 if b.block != nil && b.header == nil { 591 b.header = b.block.Header() 592 if hash, ok := b.numberOrHash.Hash(); ok { 593 b.hash = hash 594 } 595 } 596 return b.block, err 597 } 598 599 // resolveHeader returns the internal Header object for this block, fetching it 600 // if necessary. Call this function instead of `resolve` unless you need the 601 // additional data (transactions and uncles). 602 func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { 603 if b.numberOrHash == nil && b.hash == (common.Hash{}) { 604 return nil, errBlockInvariant 605 } 606 var err error 607 if b.header == nil { 608 if b.hash != (common.Hash{}) { 609 b.header, err = b.r.backend.HeaderByHash(ctx, b.hash) 610 } else { 611 b.header, err = b.r.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash) 612 } 613 } 614 return b.header, err 615 } 616 617 // resolveReceipts returns the list of receipts for this block, fetching them 618 // if necessary. 619 func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) { 620 if b.receipts == nil { 621 hash := b.hash 622 if hash == (common.Hash{}) { 623 header, err := b.resolveHeader(ctx) 624 if err != nil { 625 return nil, err 626 } 627 hash = header.Hash() 628 } 629 receipts, err := b.r.backend.GetReceipts(ctx, hash) 630 if err != nil { 631 return nil, err 632 } 633 b.receipts = receipts 634 } 635 return b.receipts, nil 636 } 637 638 func (b *Block) Number(ctx context.Context) (Long, error) { 639 header, err := b.resolveHeader(ctx) 640 if err != nil { 641 return 0, err 642 } 643 644 return Long(header.Number.Uint64()), nil 645 } 646 647 func (b *Block) Hash(ctx context.Context) (common.Hash, error) { 648 if b.hash == (common.Hash{}) { 649 header, err := b.resolveHeader(ctx) 650 if err != nil { 651 return common.Hash{}, err 652 } 653 b.hash = header.Hash() 654 } 655 return b.hash, nil 656 } 657 658 func (b *Block) GasLimit(ctx context.Context) (Long, error) { 659 header, err := b.resolveHeader(ctx) 660 if err != nil { 661 return 0, err 662 } 663 return Long(header.GasLimit), nil 664 } 665 666 func (b *Block) GasUsed(ctx context.Context) (Long, error) { 667 header, err := b.resolveHeader(ctx) 668 if err != nil { 669 return 0, err 670 } 671 return Long(header.GasUsed), nil 672 } 673 674 func (b *Block) BaseFeePerGas(ctx context.Context) (*hexutil.Big, error) { 675 header, err := b.resolveHeader(ctx) 676 if err != nil { 677 return nil, err 678 } 679 if header.BaseFee == nil { 680 return nil, nil 681 } 682 return (*hexutil.Big)(header.BaseFee), nil 683 } 684 685 func (b *Block) NextBaseFeePerGas(ctx context.Context) (*hexutil.Big, error) { 686 header, err := b.resolveHeader(ctx) 687 if err != nil { 688 return nil, err 689 } 690 chaincfg := b.r.backend.ChainConfig() 691 if header.BaseFee == nil { 692 // Make sure next block doesn't enable EIP-1559 693 if !chaincfg.IsLondon(new(big.Int).Add(header.Number, common.Big1)) { 694 return nil, nil 695 } 696 } 697 nextBaseFee := misc.CalcBaseFee(chaincfg, header) 698 return (*hexutil.Big)(nextBaseFee), nil 699 } 700 701 func (b *Block) Parent(ctx context.Context) (*Block, error) { 702 if _, err := b.resolveHeader(ctx); err != nil { 703 return nil, err 704 } 705 if b.header == nil || b.header.Number.Uint64() < 1 { 706 return nil, nil 707 } 708 num := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(b.header.Number.Uint64() - 1)) 709 return &Block{ 710 r: b.r, 711 numberOrHash: &num, 712 hash: b.header.ParentHash, 713 }, nil 714 } 715 716 func (b *Block) Difficulty(ctx context.Context) (hexutil.Big, error) { 717 header, err := b.resolveHeader(ctx) 718 if err != nil { 719 return hexutil.Big{}, err 720 } 721 return hexutil.Big(*header.Difficulty), nil 722 } 723 724 func (b *Block) Timestamp(ctx context.Context) (hexutil.Uint64, error) { 725 header, err := b.resolveHeader(ctx) 726 if err != nil { 727 return 0, err 728 } 729 return hexutil.Uint64(header.Time), nil 730 } 731 732 func (b *Block) Nonce(ctx context.Context) (hexutil.Bytes, error) { 733 header, err := b.resolveHeader(ctx) 734 if err != nil { 735 return hexutil.Bytes{}, err 736 } 737 return header.Nonce[:], nil 738 } 739 740 func (b *Block) MixHash(ctx context.Context) (common.Hash, error) { 741 header, err := b.resolveHeader(ctx) 742 if err != nil { 743 return common.Hash{}, err 744 } 745 return header.MixDigest, nil 746 } 747 748 func (b *Block) TransactionsRoot(ctx context.Context) (common.Hash, error) { 749 header, err := b.resolveHeader(ctx) 750 if err != nil { 751 return common.Hash{}, err 752 } 753 return header.TxHash, nil 754 } 755 756 func (b *Block) StateRoot(ctx context.Context) (common.Hash, error) { 757 header, err := b.resolveHeader(ctx) 758 if err != nil { 759 return common.Hash{}, err 760 } 761 return header.Root, nil 762 } 763 764 func (b *Block) ReceiptsRoot(ctx context.Context) (common.Hash, error) { 765 header, err := b.resolveHeader(ctx) 766 if err != nil { 767 return common.Hash{}, err 768 } 769 return header.ReceiptHash, nil 770 } 771 772 func (b *Block) OmmerHash(ctx context.Context) (common.Hash, error) { 773 header, err := b.resolveHeader(ctx) 774 if err != nil { 775 return common.Hash{}, err 776 } 777 return header.UncleHash, nil 778 } 779 780 func (b *Block) OmmerCount(ctx context.Context) (*int32, error) { 781 block, err := b.resolve(ctx) 782 if err != nil || block == nil { 783 return nil, err 784 } 785 count := int32(len(block.Uncles())) 786 return &count, err 787 } 788 789 func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) { 790 block, err := b.resolve(ctx) 791 if err != nil || block == nil { 792 return nil, err 793 } 794 ret := make([]*Block, 0, len(block.Uncles())) 795 for _, uncle := range block.Uncles() { 796 blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) 797 ret = append(ret, &Block{ 798 r: b.r, 799 numberOrHash: &blockNumberOrHash, 800 header: uncle, 801 }) 802 } 803 return &ret, nil 804 } 805 806 func (b *Block) ExtraData(ctx context.Context) (hexutil.Bytes, error) { 807 header, err := b.resolveHeader(ctx) 808 if err != nil { 809 return hexutil.Bytes{}, err 810 } 811 return header.Extra, nil 812 } 813 814 func (b *Block) LogsBloom(ctx context.Context) (hexutil.Bytes, error) { 815 header, err := b.resolveHeader(ctx) 816 if err != nil { 817 return hexutil.Bytes{}, err 818 } 819 return header.Bloom.Bytes(), nil 820 } 821 822 func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) { 823 h := b.hash 824 if h == (common.Hash{}) { 825 header, err := b.resolveHeader(ctx) 826 if err != nil { 827 return hexutil.Big{}, err 828 } 829 h = header.Hash() 830 } 831 td := b.r.backend.GetTd(ctx, h) 832 if td == nil { 833 return hexutil.Big{}, fmt.Errorf("total difficulty not found %x", b.hash) 834 } 835 return hexutil.Big(*td), nil 836 } 837 838 func (b *Block) RawHeader(ctx context.Context) (hexutil.Bytes, error) { 839 header, err := b.resolveHeader(ctx) 840 if err != nil { 841 return hexutil.Bytes{}, err 842 } 843 return rlp.EncodeToBytes(header) 844 } 845 846 func (b *Block) Raw(ctx context.Context) (hexutil.Bytes, error) { 847 block, err := b.resolve(ctx) 848 if err != nil { 849 return hexutil.Bytes{}, err 850 } 851 return rlp.EncodeToBytes(block) 852 } 853 854 // BlockNumberArgs encapsulates arguments to accessors that specify a block number. 855 type BlockNumberArgs struct { 856 // TODO: Ideally we could use input unions to allow the query to specify the 857 // block parameter by hash, block number, or tag but input unions aren't part of the 858 // standard GraphQL schema SDL yet, see: https://github.com/graphql/graphql-spec/issues/488 859 Block *hexutil.Uint64 860 } 861 862 // NumberOr returns the provided block number argument, or the "current" block number or hash if none 863 // was provided. 864 func (a BlockNumberArgs) NumberOr(current rpc.BlockNumberOrHash) rpc.BlockNumberOrHash { 865 if a.Block != nil { 866 blockNr := rpc.BlockNumber(*a.Block) 867 return rpc.BlockNumberOrHashWithNumber(blockNr) 868 } 869 return current 870 } 871 872 // NumberOrLatest returns the provided block number argument, or the "latest" block number if none 873 // was provided. 874 func (a BlockNumberArgs) NumberOrLatest() rpc.BlockNumberOrHash { 875 return a.NumberOr(rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)) 876 } 877 878 func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, error) { 879 header, err := b.resolveHeader(ctx) 880 if err != nil { 881 return nil, err 882 } 883 return &Account{ 884 r: b.r, 885 address: header.Coinbase, 886 blockNrOrHash: args.NumberOrLatest(), 887 }, nil 888 } 889 890 func (b *Block) TransactionCount(ctx context.Context) (*int32, error) { 891 block, err := b.resolve(ctx) 892 if err != nil || block == nil { 893 return nil, err 894 } 895 count := int32(len(block.Transactions())) 896 return &count, err 897 } 898 899 func (b *Block) Transactions(ctx context.Context) (*[]*Transaction, error) { 900 block, err := b.resolve(ctx) 901 if err != nil || block == nil { 902 return nil, err 903 } 904 ret := make([]*Transaction, 0, len(block.Transactions())) 905 for i, tx := range block.Transactions() { 906 ret = append(ret, &Transaction{ 907 r: b.r, 908 hash: tx.Hash(), 909 tx: tx, 910 block: b, 911 index: uint64(i), 912 }) 913 } 914 return &ret, nil 915 } 916 917 func (b *Block) TransactionAt(ctx context.Context, args struct{ Index int32 }) (*Transaction, error) { 918 block, err := b.resolve(ctx) 919 if err != nil || block == nil { 920 return nil, err 921 } 922 txs := block.Transactions() 923 if args.Index < 0 || int(args.Index) >= len(txs) { 924 return nil, nil 925 } 926 tx := txs[args.Index] 927 return &Transaction{ 928 r: b.r, 929 hash: tx.Hash(), 930 tx: tx, 931 block: b, 932 index: uint64(args.Index), 933 }, nil 934 } 935 936 func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block, error) { 937 block, err := b.resolve(ctx) 938 if err != nil || block == nil { 939 return nil, err 940 } 941 uncles := block.Uncles() 942 if args.Index < 0 || int(args.Index) >= len(uncles) { 943 return nil, nil 944 } 945 uncle := uncles[args.Index] 946 blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) 947 return &Block{ 948 r: b.r, 949 numberOrHash: &blockNumberOrHash, 950 header: uncle, 951 }, nil 952 } 953 954 // BlockFilterCriteria encapsulates criteria passed to a `logs` accessor inside 955 // a block. 956 type BlockFilterCriteria struct { 957 Addresses *[]common.Address // restricts matches to events created by specific contracts 958 959 // The Topic list restricts matches to particular event topics. Each event has a list 960 // of topics. Topics matches a prefix of that list. An empty element slice matches any 961 // topic. Non-empty elements represent an alternative that matches any of the 962 // contained topics. 963 // 964 // Examples: 965 // {} or nil matches any topic list 966 // {{A}} matches topic A in first position 967 // {{}, {B}} matches any topic in first position, B in second position 968 // {{A}, {B}} matches topic A in first position, B in second position 969 // {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position 970 Topics *[][]common.Hash 971 } 972 973 // runFilter accepts a filter and executes it, returning all its results as 974 // `Log` objects. 975 func runFilter(ctx context.Context, r *Resolver, filter *filters.Filter) ([]*Log, error) { 976 logs, err := filter.Logs(ctx) 977 if err != nil || logs == nil { 978 return nil, err 979 } 980 ret := make([]*Log, 0, len(logs)) 981 for _, log := range logs { 982 ret = append(ret, &Log{ 983 r: r, 984 transaction: &Transaction{r: r, hash: log.TxHash}, 985 log: log, 986 }) 987 } 988 return ret, nil 989 } 990 991 func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteria }) ([]*Log, error) { 992 var addresses []common.Address 993 if args.Filter.Addresses != nil { 994 addresses = *args.Filter.Addresses 995 } 996 var topics [][]common.Hash 997 if args.Filter.Topics != nil { 998 topics = *args.Filter.Topics 999 } 1000 hash := b.hash 1001 if hash == (common.Hash{}) { 1002 header, err := b.resolveHeader(ctx) 1003 if err != nil { 1004 return nil, err 1005 } 1006 hash = header.Hash() 1007 } 1008 // Construct the range filter 1009 filter := b.r.filterSystem.NewBlockFilter(hash, addresses, topics) 1010 1011 // Run the filter and return all the logs 1012 return runFilter(ctx, b.r, filter) 1013 } 1014 1015 func (b *Block) Account(ctx context.Context, args struct { 1016 Address common.Address 1017 }) (*Account, error) { 1018 if b.numberOrHash == nil { 1019 _, err := b.resolveHeader(ctx) 1020 if err != nil { 1021 return nil, err 1022 } 1023 } 1024 return &Account{ 1025 r: b.r, 1026 address: args.Address, 1027 blockNrOrHash: *b.numberOrHash, 1028 }, nil 1029 } 1030 1031 // CallData encapsulates arguments to `call` or `estimateGas`. 1032 // All arguments are optional. 1033 type CallData struct { 1034 From *common.Address // The Ethereum address the call is from. 1035 To *common.Address // The Ethereum address the call is to. 1036 Gas *hexutil.Uint64 // The amount of gas provided for the call. 1037 GasPrice *hexutil.Big // The price of each unit of gas, in wei. 1038 MaxFeePerGas *hexutil.Big // The max price of each unit of gas, in wei (1559). 1039 MaxPriorityFeePerGas *hexutil.Big // The max tip of each unit of gas, in wei (1559). 1040 Value *hexutil.Big // The value sent along with the call. 1041 Data *hexutil.Bytes // Any data sent with the call. 1042 } 1043 1044 // CallResult encapsulates the result of an invocation of the `call` accessor. 1045 type CallResult struct { 1046 data hexutil.Bytes // The return data from the call 1047 gasUsed Long // The amount of gas used 1048 status Long // The return status of the call - 0 for failure or 1 for success. 1049 } 1050 1051 func (c *CallResult) Data() hexutil.Bytes { 1052 return c.data 1053 } 1054 1055 func (c *CallResult) GasUsed() Long { 1056 return c.gasUsed 1057 } 1058 1059 func (c *CallResult) Status() Long { 1060 return c.status 1061 } 1062 1063 func (b *Block) Call(ctx context.Context, args struct { 1064 Data ethapi.TransactionArgs 1065 }) (*CallResult, error) { 1066 if b.numberOrHash == nil { 1067 _, err := b.resolve(ctx) 1068 if err != nil { 1069 return nil, err 1070 } 1071 } 1072 result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap()) 1073 if err != nil { 1074 return nil, err 1075 } 1076 status := Long(1) 1077 if result.Failed() { 1078 status = 0 1079 } 1080 1081 return &CallResult{ 1082 data: result.ReturnData, 1083 gasUsed: Long(result.UsedGas), 1084 status: status, 1085 }, nil 1086 } 1087 1088 func (b *Block) EstimateGas(ctx context.Context, args struct { 1089 Data ethapi.TransactionArgs 1090 }) (Long, error) { 1091 if b.numberOrHash == nil { 1092 _, err := b.resolveHeader(ctx) 1093 if err != nil { 1094 return 0, err 1095 } 1096 } 1097 gas, err := ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap()) 1098 return Long(gas), err 1099 } 1100 1101 type Pending struct { 1102 r *Resolver 1103 } 1104 1105 func (p *Pending) TransactionCount(ctx context.Context) (int32, error) { 1106 txs, err := p.r.backend.GetPoolTransactions() 1107 return int32(len(txs)), err 1108 } 1109 1110 func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) { 1111 txs, err := p.r.backend.GetPoolTransactions() 1112 if err != nil { 1113 return nil, err 1114 } 1115 ret := make([]*Transaction, 0, len(txs)) 1116 for i, tx := range txs { 1117 ret = append(ret, &Transaction{ 1118 r: p.r, 1119 hash: tx.Hash(), 1120 tx: tx, 1121 index: uint64(i), 1122 }) 1123 } 1124 return &ret, nil 1125 } 1126 1127 func (p *Pending) Account(ctx context.Context, args struct { 1128 Address common.Address 1129 }) *Account { 1130 pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) 1131 return &Account{ 1132 r: p.r, 1133 address: args.Address, 1134 blockNrOrHash: pendingBlockNr, 1135 } 1136 } 1137 1138 func (p *Pending) Call(ctx context.Context, args struct { 1139 Data ethapi.TransactionArgs 1140 }) (*CallResult, error) { 1141 pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) 1142 result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap()) 1143 if err != nil { 1144 return nil, err 1145 } 1146 status := Long(1) 1147 if result.Failed() { 1148 status = 0 1149 } 1150 1151 return &CallResult{ 1152 data: result.ReturnData, 1153 gasUsed: Long(result.UsedGas), 1154 status: status, 1155 }, nil 1156 } 1157 1158 func (p *Pending) EstimateGas(ctx context.Context, args struct { 1159 Data ethapi.TransactionArgs 1160 }) (Long, error) { 1161 pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) 1162 gas, err := ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap()) 1163 return Long(gas), err 1164 } 1165 1166 // Resolver is the top-level object in the GraphQL hierarchy. 1167 type Resolver struct { 1168 backend ethapi.Backend 1169 filterSystem *filters.FilterSystem 1170 } 1171 1172 func (r *Resolver) Block(ctx context.Context, args struct { 1173 Number *Long 1174 Hash *common.Hash 1175 }) (*Block, error) { 1176 var block *Block 1177 if args.Number != nil { 1178 if *args.Number < 0 { 1179 return nil, nil 1180 } 1181 number := rpc.BlockNumber(*args.Number) 1182 numberOrHash := rpc.BlockNumberOrHashWithNumber(number) 1183 block = &Block{ 1184 r: r, 1185 numberOrHash: &numberOrHash, 1186 } 1187 } else if args.Hash != nil { 1188 numberOrHash := rpc.BlockNumberOrHashWithHash(*args.Hash, false) 1189 block = &Block{ 1190 r: r, 1191 numberOrHash: &numberOrHash, 1192 } 1193 } else { 1194 numberOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) 1195 block = &Block{ 1196 r: r, 1197 numberOrHash: &numberOrHash, 1198 } 1199 } 1200 // Resolve the header, return nil if it doesn't exist. 1201 // Note we don't resolve block directly here since it will require an 1202 // additional network request for light client. 1203 h, err := block.resolveHeader(ctx) 1204 if err != nil { 1205 return nil, err 1206 } else if h == nil { 1207 return nil, nil 1208 } 1209 return block, nil 1210 } 1211 1212 func (r *Resolver) Blocks(ctx context.Context, args struct { 1213 From *Long 1214 To *Long 1215 }) ([]*Block, error) { 1216 from := rpc.BlockNumber(*args.From) 1217 1218 var to rpc.BlockNumber 1219 if args.To != nil { 1220 to = rpc.BlockNumber(*args.To) 1221 } else { 1222 to = rpc.BlockNumber(r.backend.CurrentBlock().Number().Int64()) 1223 } 1224 if to < from { 1225 return []*Block{}, nil 1226 } 1227 ret := make([]*Block, 0, to-from+1) 1228 for i := from; i <= to; i++ { 1229 numberOrHash := rpc.BlockNumberOrHashWithNumber(i) 1230 block := &Block{ 1231 r: r, 1232 numberOrHash: &numberOrHash, 1233 } 1234 // Resolve the header to check for existence. 1235 // Note we don't resolve block directly here since it will require an 1236 // additional network request for light client. 1237 h, err := block.resolveHeader(ctx) 1238 if err != nil { 1239 return nil, err 1240 } else if h == nil { 1241 // Blocks after must be non-existent too, break. 1242 break 1243 } 1244 ret = append(ret, block) 1245 } 1246 return ret, nil 1247 } 1248 1249 func (r *Resolver) Pending(ctx context.Context) *Pending { 1250 return &Pending{r} 1251 } 1252 1253 func (r *Resolver) Transaction(ctx context.Context, args struct{ Hash common.Hash }) (*Transaction, error) { 1254 tx := &Transaction{ 1255 r: r, 1256 hash: args.Hash, 1257 } 1258 // Resolve the transaction; if it doesn't exist, return nil. 1259 t, err := tx.resolve(ctx) 1260 if err != nil { 1261 return nil, err 1262 } else if t == nil { 1263 return nil, nil 1264 } 1265 return tx, nil 1266 } 1267 1268 func (r *Resolver) SendRawTransaction(ctx context.Context, args struct{ Data hexutil.Bytes }) (common.Hash, error) { 1269 tx := new(types.Transaction) 1270 if err := tx.UnmarshalBinary(args.Data); err != nil { 1271 return common.Hash{}, err 1272 } 1273 hash, err := ethapi.SubmitTransaction(ctx, r.backend, tx) 1274 return hash, err 1275 } 1276 1277 // FilterCriteria encapsulates the arguments to `logs` on the root resolver object. 1278 type FilterCriteria struct { 1279 FromBlock *hexutil.Uint64 // beginning of the queried range, nil means genesis block 1280 ToBlock *hexutil.Uint64 // end of the range, nil means latest block 1281 Addresses *[]common.Address // restricts matches to events created by specific contracts 1282 1283 // The Topic list restricts matches to particular event topics. Each event has a list 1284 // of topics. Topics matches a prefix of that list. An empty element slice matches any 1285 // topic. Non-empty elements represent an alternative that matches any of the 1286 // contained topics. 1287 // 1288 // Examples: 1289 // {} or nil matches any topic list 1290 // {{A}} matches topic A in first position 1291 // {{}, {B}} matches any topic in first position, B in second position 1292 // {{A}, {B}} matches topic A in first position, B in second position 1293 // {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position 1294 Topics *[][]common.Hash 1295 } 1296 1297 func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria }) ([]*Log, error) { 1298 // Convert the RPC block numbers into internal representations 1299 begin := rpc.LatestBlockNumber.Int64() 1300 if args.Filter.FromBlock != nil { 1301 begin = int64(*args.Filter.FromBlock) 1302 } 1303 end := rpc.LatestBlockNumber.Int64() 1304 if args.Filter.ToBlock != nil { 1305 end = int64(*args.Filter.ToBlock) 1306 } 1307 var addresses []common.Address 1308 if args.Filter.Addresses != nil { 1309 addresses = *args.Filter.Addresses 1310 } 1311 var topics [][]common.Hash 1312 if args.Filter.Topics != nil { 1313 topics = *args.Filter.Topics 1314 } 1315 // Construct the range filter 1316 filter := r.filterSystem.NewRangeFilter(begin, end, addresses, topics) 1317 return runFilter(ctx, r, filter) 1318 } 1319 1320 func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) { 1321 tipcap, err := r.backend.SuggestGasTipCap(ctx) 1322 if err != nil { 1323 return hexutil.Big{}, err 1324 } 1325 if head := r.backend.CurrentHeader(); head.BaseFee != nil { 1326 tipcap.Add(tipcap, head.BaseFee) 1327 } 1328 return (hexutil.Big)(*tipcap), nil 1329 } 1330 1331 func (r *Resolver) MaxPriorityFeePerGas(ctx context.Context) (hexutil.Big, error) { 1332 tipcap, err := r.backend.SuggestGasTipCap(ctx) 1333 if err != nil { 1334 return hexutil.Big{}, err 1335 } 1336 return (hexutil.Big)(*tipcap), nil 1337 } 1338 1339 func (r *Resolver) ChainID(ctx context.Context) (hexutil.Big, error) { 1340 return hexutil.Big(*r.backend.ChainConfig().ChainID), nil 1341 } 1342 1343 // SyncState represents the synchronisation status returned from the `syncing` accessor. 1344 type SyncState struct { 1345 progress ethereum.SyncProgress 1346 } 1347 1348 func (s *SyncState) StartingBlock() hexutil.Uint64 { 1349 return hexutil.Uint64(s.progress.StartingBlock) 1350 } 1351 func (s *SyncState) CurrentBlock() hexutil.Uint64 { 1352 return hexutil.Uint64(s.progress.CurrentBlock) 1353 } 1354 func (s *SyncState) HighestBlock() hexutil.Uint64 { 1355 return hexutil.Uint64(s.progress.HighestBlock) 1356 } 1357 func (s *SyncState) SyncedAccounts() hexutil.Uint64 { 1358 return hexutil.Uint64(s.progress.SyncedAccounts) 1359 } 1360 func (s *SyncState) SyncedAccountBytes() hexutil.Uint64 { 1361 return hexutil.Uint64(s.progress.SyncedAccountBytes) 1362 } 1363 func (s *SyncState) SyncedBytecodes() hexutil.Uint64 { 1364 return hexutil.Uint64(s.progress.SyncedBytecodes) 1365 } 1366 func (s *SyncState) SyncedBytecodeBytes() hexutil.Uint64 { 1367 return hexutil.Uint64(s.progress.SyncedBytecodeBytes) 1368 } 1369 func (s *SyncState) SyncedStorage() hexutil.Uint64 { 1370 return hexutil.Uint64(s.progress.SyncedStorage) 1371 } 1372 func (s *SyncState) SyncedStorageBytes() hexutil.Uint64 { 1373 return hexutil.Uint64(s.progress.SyncedStorageBytes) 1374 } 1375 func (s *SyncState) HealedTrienodes() hexutil.Uint64 { 1376 return hexutil.Uint64(s.progress.HealedTrienodes) 1377 } 1378 func (s *SyncState) HealedTrienodeBytes() hexutil.Uint64 { 1379 return hexutil.Uint64(s.progress.HealedTrienodeBytes) 1380 } 1381 func (s *SyncState) HealedBytecodes() hexutil.Uint64 { 1382 return hexutil.Uint64(s.progress.HealedBytecodes) 1383 } 1384 func (s *SyncState) HealedBytecodeBytes() hexutil.Uint64 { 1385 return hexutil.Uint64(s.progress.HealedBytecodeBytes) 1386 } 1387 func (s *SyncState) HealingTrienodes() hexutil.Uint64 { 1388 return hexutil.Uint64(s.progress.HealingTrienodes) 1389 } 1390 func (s *SyncState) HealingBytecode() hexutil.Uint64 { 1391 return hexutil.Uint64(s.progress.HealingBytecode) 1392 } 1393 1394 // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not 1395 // yet received the latest block headers from its pears. In case it is synchronizing: 1396 // - startingBlock: block number this node started to synchronise from 1397 // - currentBlock: block number this node is currently importing 1398 // - highestBlock: block number of the highest block header this node has received from peers 1399 // - syncedAccounts: number of accounts downloaded 1400 // - syncedAccountBytes: number of account trie bytes persisted to disk 1401 // - syncedBytecodes: number of bytecodes downloaded 1402 // - syncedBytecodeBytes: number of bytecode bytes downloaded 1403 // - syncedStorage: number of storage slots downloaded 1404 // - syncedStorageBytes: number of storage trie bytes persisted to disk 1405 // - healedTrienodes: number of state trie nodes downloaded 1406 // - healedTrienodeBytes: number of state trie bytes persisted to disk 1407 // - healedBytecodes: number of bytecodes downloaded 1408 // - healedBytecodeBytes: number of bytecodes persisted to disk 1409 // - healingTrienodes: number of state trie nodes pending 1410 // - healingBytecode: number of bytecodes pending 1411 func (r *Resolver) Syncing() (*SyncState, error) { 1412 progress := r.backend.SyncProgress() 1413 1414 // Return not syncing if the synchronisation already completed 1415 if progress.CurrentBlock >= progress.HighestBlock { 1416 return nil, nil 1417 } 1418 // Otherwise gather the block sync stats 1419 return &SyncState{progress}, nil 1420 }