From 530f78e22de9580c3779d33fd0e3b3c8e677c8d0 Mon Sep 17 00:00:00 2001 From: gary rong Date: Tue, 23 Jul 2019 21:52:24 +0800 Subject: [PATCH] eth, internal, les: add getHeaderBy* APIs (#19669) * eth, interal, les: add getHeaderBy* APIs * internal: address the comment * eth, internal, les: getHeader nits, missing TD, console callable --- eth/api_backend.go | 26 +++++----- internal/ethapi/api.go | 94 +++++++++++++++++++++++++++---------- internal/ethapi/backend.go | 15 +++--- internal/web3ext/web3ext.go | 24 +++++++++- les/api_backend.go | 22 +++++---- 5 files changed, 126 insertions(+), 55 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index ee5b51cf5..708e5afaf 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -59,44 +59,44 @@ func (b *EthAPIBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } -func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { +func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { + if number == rpc.PendingBlockNumber { block := b.eth.miner.PendingBlock() return block.Header(), nil } // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { + if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentBlock().Header(), nil } - return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil + return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil } func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { return b.eth.blockchain.GetHeaderByHash(hash), nil } -func (b *EthAPIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { +func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { + if number == rpc.PendingBlockNumber { block := b.eth.miner.PendingBlock() return block, nil } // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { + if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentBlock(), nil } - return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil + return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil } -func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { + if number == rpc.PendingBlockNumber { block, state := b.eth.miner.Pending() return state, block.Header(), nil } // Otherwise resolve the block number and return its state - header, err := b.HeaderByNumber(ctx, blockNr) + header, err := b.HeaderByNumber(ctx, number) if err != nil { return nil, nil, err } @@ -107,6 +107,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. return stateDb, header, err } +func (b *EthAPIBackend) GetHeader(ctx context.Context, hash common.Hash) *types.Header { + return b.eth.blockchain.GetHeaderByHash(hash) +} + func (b *EthAPIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { return b.eth.blockchain.GetBlockByHash(hash), nil } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 04df794ec..8b0489e7a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -616,13 +616,43 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre }, state.Error() } -// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all -// transactions in the block are returned in full detail, otherwise only the transaction hash is returned. -func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) - if block != nil { - response, err := s.rpcOutputBlock(block, true, fullTx) - if err == nil && blockNr == rpc.PendingBlockNumber { +// GetHeaderByNumber returns the requested canonical block header. +// * When blockNr is -1 the chain head is returned. +// * When blockNr is -2 the pending chain head is returned. +func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) { + header, err := s.b.HeaderByNumber(ctx, number) + if header != nil && err == nil { + response := s.rpcMarshalHeader(header) + if number == rpc.PendingBlockNumber { + // Pending header need to nil out a few fields + for _, field := range []string{"hash", "nonce", "miner"} { + response[field] = nil + } + } + return response, err + } + return nil, err +} + +// GetHeaderByHash returns the requested header by hash. +func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} { + header := s.b.GetHeader(ctx, hash) + if header != nil { + return s.rpcMarshalHeader(header) + } + return nil +} + +// GetBlockByNumber returns the requested canonical block. +// * When blockNr is -1 the chain head is returned. +// * When blockNr is -2 the pending chain head is returned. +// * When fullTx is true all transactions in the block are returned, otherwise +// only the transaction hash is returned. +func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { + block, err := s.b.BlockByNumber(ctx, number) + if block != nil && err == nil { + response, err := s.rpcMarshalBlock(block, true, fullTx) + if err == nil && number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil @@ -635,10 +665,10 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // detail, otherwise only the transaction hash is returned. -func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) +func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { + block, err := s.b.GetBlock(ctx, hash) if block != nil { - return s.rpcOutputBlock(block, true, fullTx) + return s.rpcMarshalBlock(block, true, fullTx) } return nil, err } @@ -654,7 +684,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcOutputBlock(block, false, false) + return s.rpcMarshalBlock(block, false, false) } return nil, err } @@ -670,7 +700,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcOutputBlock(block, false, false) + return s.rpcMarshalBlock(block, false, false) } return nil, err } @@ -933,14 +963,11 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes { return formatted } -// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are -// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain -// transaction hashes. -func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { - head := b.Header() // copies the header once - fields := map[string]interface{}{ +// RPCMarshalHeader converts the given header to the RPC output . +func RPCMarshalHeader(head *types.Header) map[string]interface{} { + return map[string]interface{}{ "number": (*hexutil.Big)(head.Number), - "hash": b.Hash(), + "hash": head.Hash(), "parentHash": head.ParentHash, "nonce": head.Nonce, "mixHash": head.MixDigest, @@ -950,13 +977,21 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter "miner": head.Coinbase, "difficulty": (*hexutil.Big)(head.Difficulty), "extraData": hexutil.Bytes(head.Extra), - "size": hexutil.Uint64(b.Size()), + "size": hexutil.Uint64(head.Size()), "gasLimit": hexutil.Uint64(head.GasLimit), "gasUsed": hexutil.Uint64(head.GasUsed), "timestamp": hexutil.Uint64(head.Time), "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, } +} + +// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are +// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain +// transaction hashes. +func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { + fields := RPCMarshalHeader(block.Header()) + fields["size"] = block.Size() if inclTx { formatTx := func(tx *types.Transaction) (interface{}, error) { @@ -964,10 +999,10 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter } if fullTx { formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransactionFromBlockHash(b, tx.Hash()), nil + return newRPCTransactionFromBlockHash(block, tx.Hash()), nil } } - txs := b.Transactions() + txs := block.Transactions() transactions := make([]interface{}, len(txs)) var err error for i, tx := range txs { @@ -977,8 +1012,7 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter } fields["transactions"] = transactions } - - uncles := b.Uncles() + uncles := block.Uncles() uncleHashes := make([]common.Hash, len(uncles)) for i, uncle := range uncles { uncleHashes[i] = uncle.Hash() @@ -988,9 +1022,17 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter return fields, nil } -// rpcOutputBlock uses the generalized output filler, then adds the total difficulty field, which requires +// rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field, which requires // a `PublicBlockchainAPI`. -func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func (s *PublicBlockChainAPI) rpcMarshalHeader(header *types.Header) map[string]interface{} { + fields := RPCMarshalHeader(header) + fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(header.Hash())) + return fields +} + +// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires +// a `PublicBlockchainAPI`. +func (s *PublicBlockChainAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { fields, err := RPCMarshalBlock(b, inclTx, fullTx) if err != nil { return nil, err diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 28ec69897..0869d986c 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -50,13 +50,14 @@ type Backend interface { // Blockchain API SetHead(number uint64) - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetTd(blockHash common.Hash) *big.Int + HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) + StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) + GetHeader(ctx context.Context, hash common.Hash) *types.Header + GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) + GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) + GetTd(hash common.Hash) *big.Int GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 0abcd5a8a..4a5d4b4dd 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -483,6 +483,26 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.inputTransactionFormatter] }), + new web3._extend.Method({ + name: 'getHeaderByNumber', + call: 'eth_getHeaderByNumber', + params: 1 + }), + new web3._extend.Method({ + name: 'getHeaderByHash', + call: 'eth_getHeaderByHash', + params: 1 + }), + new web3._extend.Method({ + name: 'getBlockByNumber', + call: 'eth_getBlockByNumber', + params: 2 + }), + new web3._extend.Method({ + name: 'getBlockByHash', + call: 'eth_getBlockByHash', + params: 2 + }), new web3._extend.Method({ name: 'getRawTransaction', call: 'eth_getRawTransactionByHash', @@ -765,7 +785,7 @@ web3._extend({ const LESJs = ` web3._extend({ property: 'les', - methods: + methods: [ new web3._extend.Method({ name: 'getCheckpoint', @@ -773,7 +793,7 @@ web3._extend({ params: 1 }), ], - properties: + properties: [ new web3._extend.Property({ name: 'latestCheckpoint', diff --git a/les/api_backend.go b/les/api_backend.go index 6de15e7bd..f1ac8ed5a 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -58,27 +58,27 @@ func (b *LesApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } -func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { +func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber { return b.eth.blockchain.CurrentHeader(), nil } - return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) + return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number)) } func (b *LesApiBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { return b.eth.blockchain.GetHeaderByHash(hash), nil } -func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - header, err := b.HeaderByNumber(ctx, blockNr) +func (b *LesApiBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + header, err := b.HeaderByNumber(ctx, number) if header == nil || err != nil { return nil, err } return b.GetBlock(ctx, header.Hash()) } -func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) { - header, err := b.HeaderByNumber(ctx, blockNr) +func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { + header, err := b.HeaderByNumber(ctx, number) if err != nil { return nil, nil, err } @@ -88,8 +88,12 @@ func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. return light.NewState(ctx, header, b.eth.odr), header, nil } -func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(ctx, blockHash) +func (b *LesApiBackend) GetHeader(ctx context.Context, hash common.Hash) *types.Header { + return b.eth.blockchain.GetHeaderByHash(hash) +} + +func (b *LesApiBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { + return b.eth.blockchain.GetBlockByHash(ctx, hash) } func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {