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