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