all: run RPC APIs on top of the stable Go API

In this commit, the RPC API is adapted to run on top of the new Go API.
This required lots of changes to many packages, but has a few side
benefits:

- Ethereum and LightEthereum can now be used as a contract backend.
- Some duplicated code could be removed (there is added duplication in
  other places though)
- It is now much easier to see which operations are unsupported with the
  light client. Package les previously relied on the full node RPC API
  backend, which masked several issues because the RPC API performed
  direct access to the chain database.

Changes to packages in detail:

accounts/abi/bind:
  - Contract call boilerplate has moved to package core.

cmd/utils:
  - les now inherits the event.TypeMux from the Node instance

contracts/release:
  - The ReleaseService now uses Ethereum and LightEthereum as backend.

core:
  - MissingNumber is exposed so it can be used in package eth.
  - GetTransaction now returns the index as an int, for convenience
    reasons.
  - ApplyCallMessage has been added as the new one and only
    implementation of read-only contract calls.
  - TxPool exposes NonceAt instead of the more general accessor for the
    ManagedState.

eth:
  - EthApiBackend is gone. In its place, Ethereum gains many new methods
    which implement a large portion of the new Go API. It does not
    yet support event subscriptions and log filtering.
  - Some accessors for internal objects are gone.
  - ethapi.PrivateDebugAPI and ethapi.TxPoolDebugAPI are now created in
    package eth for dependency reasons.

eth/downloader:
  - Progress returns a pointer to simplify callers.

eth/filters:
  - The Backend interface is simpler and uses the stable Go API where
    possible. The new BlockReceipts method saves one DB read because
    BlockReceipts also takes a block number argument.
  - ChainDB is no longer passed via the Backend interface.
  - EventSystem now relies on HeaderByHash for reorgs in light client mode
    instead of reading from the chain database.

eth/gasprice:
  - The LightPriceOracle now uses ethereum.ChainReader instead of
    ethapi.Backend.

ethclient:
  - TransactionByHash is adapted for the last-minute API change which
    adds the isPending return value.

internal/ethapi:
  - PublicTxPoolAPI is now called TxPoolDebugAPI, moved to its own file
    and talks to the transaction pool instead of using the main Backend.
  - The API no longer accesses the chain database directly. All access
    is mediated through Backend methods.
  - The backend is now split into three interfaces.
    Implementing Backend is mandatory but does not require the pending
    state. The other two (PendingState, TransactionInclusionBlock) are
    optional and discovered at runtime.

les:
  - LesApiBackend is gone, LightEthereum gets all the methods.
  - Weird accessors copied from package eth are now gone as well.

light:
  - TxPool.Stats now returns a queued count of zero. It implements the
    ethapi.TxPool interface and can be used with TxPoolDebugAPI.
This commit is contained in:
Felix Lange 2016-11-11 05:20:21 +01:00
parent af65d52c27
commit 6353cddea2
30 changed files with 938 additions and 1013 deletions

View file

@ -164,7 +164,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if err != nil { if err != nil {
return nil, err return nil, err
} }
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) rval, _, err := b.callContract(call, b.blockchain.CurrentBlock(), state)
return rval, err return rval, err
} }
@ -174,7 +174,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
defer b.mu.Unlock() defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) rval, _, err := b.callContract(call, b.pendingBlock, b.pendingState)
return rval, err return rval, err
} }
@ -200,32 +200,16 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
defer b.mu.Unlock() defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) _, gas, err := b.callContract(call, b.pendingBlock, b.pendingState)
return gas, err return gas, err
} }
// callContract implemens common code between normal and pending contract calls. // callContract implemens common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary. // state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { func (b *SimulatedBackend) callContract(call ethereum.CallMsg, block *types.Block, state *state.StateDB) ([]byte, *big.Int, error) {
// Ensure message is initialized properly. return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment {
if call.GasPrice == nil { return core.NewEnv(state, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
call.GasPrice = big.NewInt(1) })
}
if call.Gas == nil || call.Gas.BitLen() == 0 {
call.Gas = big.NewInt(50000000)
}
if call.Value == nil {
call.Value = new(big.Int)
}
// Set infinite balance to the fake caller account.
from := statedb.GetOrNewStateObject(call.From)
from.SetBalance(common.MaxBig)
// Execute the call.
msg := callmsg{call}
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return ret, gasUsed, err
} }
// SendTransaction updates the pending block to include the given transaction. // SendTransaction updates the pending block to include the given transaction.
@ -253,18 +237,3 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
return nil return nil
} }
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
ethereum.CallMsg
}
func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }

View file

@ -768,7 +768,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
fullNode, err := eth.New(ctx, ethConf) fullNode, err := eth.New(ctx, ethConf)
if fullNode != nil && ethConf.LightServ > 0 { if fullNode != nil && ethConf.LightServ > 0 {
ls, _ := les.NewLesServer(fullNode, ethConf) ls, _ := les.NewLesServer(fullNode, stack.EventMux(), ethConf)
fullNode.AddLesServer(ls) fullNode.AddLesServer(ls)
} }
return fullNode, err return fullNode, err

View file

@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -62,20 +61,20 @@ type ReleaseService struct {
// releases and notify the user of such. // releases and notify the user of such.
func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) {
// Retrieve the Ethereum service dependency to access the blockchain // Retrieve the Ethereum service dependency to access the blockchain
var apiBackend ethapi.Backend var apiBackend bind.ContractBackend
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
if err := ctx.Service(&ethereum); err == nil { if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend apiBackend = ethereum
} else { } else {
var ethereum *les.LightEthereum var ethereum *les.LightEthereum
if err := ctx.Service(&ethereum); err == nil { if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend apiBackend = ethereum
} else { } else {
return nil, err return nil, err
} }
} }
// Construct the release service // Construct the release service
contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) contract, err := NewReleaseOracle(config.Oracle, apiBackend)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -82,7 +82,7 @@ func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
// missingNumber is returned by GetBlockNumber if no header with the // missingNumber is returned by GetBlockNumber if no header with the
// given block hash has been stored in the database // given block hash has been stored in the database
const missingNumber = uint64(0xffffffffffffffff) const MissingNumber = uint64(0xffffffffffffffff)
// GetBlockNumber returns the block number assigned to a block hash // GetBlockNumber returns the block number assigned to a block hash
// if the corresponding header is present in the database // if the corresponding header is present in the database
@ -91,7 +91,7 @@ func GetBlockNumber(db ethdb.Database, hash common.Hash) uint64 {
if len(data) != 8 { if len(data) != 8 {
data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...))
if len(data) == 0 { if len(data) == 0 {
return missingNumber return MissingNumber
} }
header := new(types.Header) header := new(types.Header)
if err := rlp.Decode(bytes.NewReader(data), header); err != nil { if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
@ -247,7 +247,7 @@ func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types.
// GetTransaction retrieves a specific transaction from the database, along with // GetTransaction retrieves a specific transaction from the database, along with
// its added positional metadata. // its added positional metadata.
func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, int) {
// Retrieve the transaction itself from the database // Retrieve the transaction itself from the database
data, _ := db.Get(hash.Bytes()) data, _ := db.Get(hash.Bytes())
if len(data) == 0 { if len(data) == 0 {
@ -270,7 +270,7 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co
if err := rlp.DecodeBytes(data, &meta); err != nil { if err := rlp.DecodeBytes(data, &meta); err != nil {
return nil, common.Hash{}, 0, 0 return nil, common.Hash{}, 0, 0
} }
return &tx, meta.BlockHash, meta.BlockIndex, meta.Index return &tx, meta.BlockHash, meta.BlockIndex, int(meta.Index)
} }
// GetReceipt returns a receipt by hash // GetReceipt returns a receipt by hash

View file

@ -368,7 +368,7 @@ func TestTransactionStorage(t *testing.T) {
if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
} else { } else {
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { if hash != block.Hash() || number != block.NumberU64() || index != i {
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
} }
if tx.String() != txn.String() { if tx.String() != txn.String() {

View file

@ -123,7 +123,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 {
return cached.(uint64) return cached.(uint64)
} }
number := GetBlockNumber(hc.chainDb, hash) number := GetBlockNumber(hc.chainDb, hash)
if number != missingNumber { if number != MissingNumber {
hc.numberCache.Add(hash, number) hc.numberCache.Add(hash, number)
} }
return number return number

View file

@ -1,96 +0,0 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"container/list"
"fmt"
"github.com/ethereum/go-ethereum/core/types"
// "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
)
// Implement our EthTest Manager
type TestManager struct {
// stateManager *StateManager
eventMux *event.TypeMux
db ethdb.Database
txPool *TxPool
blockChain *BlockChain
Blocks []*types.Block
}
func (s *TestManager) IsListening() bool {
return false
}
func (s *TestManager) IsMining() bool {
return false
}
func (s *TestManager) PeerCount() int {
return 0
}
func (s *TestManager) Peers() *list.List {
return list.New()
}
func (s *TestManager) BlockChain() *BlockChain {
return s.blockChain
}
func (tm *TestManager) TxPool() *TxPool {
return tm.txPool
}
// func (tm *TestManager) StateManager() *StateManager {
// return tm.stateManager
// }
func (tm *TestManager) EventMux() *event.TypeMux {
return tm.eventMux
}
// func (tm *TestManager) KeyManager() *crypto.KeyManager {
// return nil
// }
func (tm *TestManager) Db() ethdb.Database {
return tm.db
}
func NewTestManager() *TestManager {
db, err := ethdb.NewMemDatabase()
if err != nil {
fmt.Println("Could not create mem-db, failing")
return nil
}
testManager := &TestManager{}
testManager.eventMux = new(event.TypeMux)
testManager.db = db
// testManager.txPool = NewTxPool(testManager)
// testManager.blockChain = NewBlockChain(testManager)
// testManager.stateManager = NewStateManager(testManager)
return testManager
}

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -134,6 +135,39 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In
return ret, gasUsed, err return ret, gasUsed, err
} }
// ApplyCallMessage runs the given message as a read-only contract call.
//
// It modifies the environment to provide a near infinite amount of ether to the sender
// account and runs the message with the amount of gas configured in the message. This
// function is meant to be used outside of regular block processing.
//
// It returns the bytes returned by any EVM execution (if it took place), the gas used
// (not including gas refunds) and an error if it failed. An error always indicates a core
// error meaning that the message would always fail for that particular state and would
// never be accepted within a block.
func ApplyCallMessage(call ethereum.CallMsg, newEnv func(Message) vm.Environment) ([]byte, *big.Int, error) {
// Ensure message is initialized properly.
if call.GasPrice == nil {
call.GasPrice = big.NewInt(1)
}
if call.Gas == nil || call.Gas.BitLen() == 0 {
call.Gas = big.NewInt(50000000)
}
if call.Value == nil {
call.Value = new(big.Int)
}
msg := callmsg{call}
env := newEnv(msg)
// Provide ether to the caller account.
have := env.Db().GetBalance(call.From)
need := new(big.Int).Sub(common.MaxBig, have)
env.Db().AddBalance(call.From, need)
// Execute the call.
gaspool := new(GasPool).AddGas(call.Gas)
ret, gasUsed, _, err := NewStateTransition(env, msg, gaspool).TransitionDb()
return ret, gasUsed, err
}
func (self *StateTransition) from() (vm.Account, error) { func (self *StateTransition) from() (vm.Account, error) {
var ( var (
f common.Address f common.Address
@ -300,3 +334,18 @@ func (self *StateTransition) refundGas() {
func (self *StateTransition) gasUsed() *big.Int { func (self *StateTransition) gasUsed() *big.Int {
return new(big.Int).Sub(self.initialGas, self.gas) return new(big.Int).Sub(self.initialGas, self.gas)
} }
// callmsg wraps ethereum.CallMsg so it implements Message.
type callmsg struct {
ethereum.CallMsg
}
func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }

View file

@ -190,11 +190,12 @@ func (pool *TxPool) Stop() {
glog.V(logger.Info).Infoln("Transaction pool stopped") glog.V(logger.Info).Infoln("Transaction pool stopped")
} }
func (pool *TxPool) State() *state.ManagedState { // NonceAt returns the next valid nonce for the given account.
pool.mu.RLock() func (pool *TxPool) NonceAt(addr common.Address) uint64 {
defer pool.mu.RUnlock() pool.mu.Lock()
defer pool.mu.Unlock()
return pool.pendingState return pool.pendingState.GetNonce(addr)
} }
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending and the

View file

@ -521,7 +521,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
data: tx.Data(), data: tx.Data(),
} }
// Mutate the state if we haven't reached the tracing transaction yet // Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex { if idx < txIndex {
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {

View file

@ -17,189 +17,299 @@
package eth package eth
import ( import (
"errors"
"fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// EthApiBackend implements ethapi.Backend for full nodes // Assert at compile time that this implementation has all optional API features.
type EthApiBackend struct { var (
eth *Ethereum _ ethapi.TransactionInclusionBlock = (*Ethereum)(nil)
gpo *gasprice.GasPriceOracle _ ethapi.PendingState = (*Ethereum)(nil)
} _ bind.ContractBackend = (*Ethereum)(nil)
_ bind.PendingContractCaller = (*Ethereum)(nil)
)
func (b *EthApiBackend) SetHead(number uint64) { var (
b.eth.blockchain.SetHead(number) ErrBlockNotFound = errors.New("block not found")
} ErrTxNotFound = errors.New("transaction not found")
)
func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { // HeaderByNumber returns headers from the canonical chain.
// Pending block is only known by the miner func (eth *Ethereum) HeaderByNumber(ctx context.Context, num *big.Int) (*types.Header, error) {
if blockNr == rpc.PendingBlockNumber { if num == nil {
block, _ := b.eth.miner.Pending() return eth.blockchain.CurrentBlock().Header(), nil
return block.Header(), nil
} }
// Otherwise resolve and return the block if h := eth.blockchain.GetHeaderByNumber(num.Uint64()); h != nil {
if blockNr == rpc.LatestBlockNumber { return h, nil
return b.eth.blockchain.CurrentBlock().Header(), nil
} }
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil return nil, ErrBlockNotFound
} }
func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { // HeaderByHash returns the header with the given hash.
// Pending block is only known by the miner func (eth *Ethereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) {
if blockNr == rpc.PendingBlockNumber { if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil {
block, _ := b.eth.miner.Pending() return h, nil
return block, nil
} }
// Otherwise resolve and return the block return nil, ErrBlockNotFound
if blockNr == rpc.LatestBlockNumber { }
return b.eth.blockchain.CurrentBlock(), nil
// BlockByNumber returns blocks from the canonical chain.
func (eth *Ethereum) BlockByNumber(ctx context.Context, num *big.Int) (*types.Block, error) {
if num == nil {
return eth.blockchain.CurrentBlock(), nil
} }
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil if b := eth.blockchain.GetBlockByNumber(num.Uint64()); b != nil {
} return b, nil
func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
// Pending state is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return EthApiState{state}, block.Header(), nil
} }
// Otherwise resolve the block number and return its state return nil, ErrBlockNotFound
header, err := b.HeaderByNumber(ctx, blockNr) }
if header == nil || err != nil {
return nil, nil, err // BlockByHash returns the block with the given hash.
func (eth *Ethereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) {
if b := eth.blockchain.GetBlockByHash(blockhash); b != nil {
return b, nil
} }
stateDb, err := b.eth.BlockChain().StateAt(header.Root) return nil, ErrBlockNotFound
return EthApiState{stateDb}, header, err
} }
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { // BlockReceipts returns all receipts contained in the given block.
return b.eth.blockchain.GetBlockByHash(blockHash), nil func (eth *Ethereum) BlockReceipts(ctx context.Context, blockhash common.Hash, number uint64) ([]*types.Receipt, error) {
r := core.GetBlockReceipts(eth.chainDb, blockhash, number)
if r == nil {
return nil, errors.New("database has no valid receipts for the given block")
}
return r, nil
} }
func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { // TransactionCount returns the number of transactions in a block.
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil func (eth *Ethereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) {
b := eth.blockchain.GetBlockByHash(blockhash)
if b == nil {
return 0, nil
}
return uint(len(b.Transactions())), nil
} }
func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { // TransactionInBlock returns the i'th transaction in the given block.
return b.eth.blockchain.GetTdByHash(blockHash) func (eth *Ethereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, i uint) (*types.Transaction, error) {
b := eth.blockchain.GetBlockByHash(blockhash)
if b == nil {
return nil, fmt.Errorf("transaction index %d out of range for non-existent block", i)
}
if i >= uint(len(b.Transactions())) {
return nil, fmt.Errorf("transaction index %d out of range", i)
}
return b.Transactions()[i], nil
} }
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { // TransactionByHash returns the transaction with the given hash.
statedb := state.(EthApiState).state func (eth *Ethereum) TransactionByHash(ctx context.Context, txhash common.Hash) (tx *types.Transaction, isPending bool, err error) {
addr, _ := msg.From() if tx = eth.txPool.Get(txhash); tx != nil {
from := statedb.GetOrNewStateObject(addr) return tx, true, nil
from.SetBalance(common.MaxBig) }
vmError := func() error { return nil } if tx, _, _, _ = core.GetTransaction(eth.chainDb, txhash); tx != nil {
return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil return tx, false, nil
}
return nil, false, ErrTxNotFound
} }
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { // TransactionInclusionBlock returns the block in which the given transaction was included.
b.eth.txMu.Lock() func (eth *Ethereum) TransactionInclusionBlock(txhash common.Hash) (bhash common.Hash, bnum uint64, index int, err error) {
defer b.eth.txMu.Unlock() var tx *types.Transaction
if tx, bhash, bnum, index = core.GetTransaction(eth.chainDb, txhash); tx == nil {
b.eth.txPool.SetLocal(signedTx) err = ErrTxNotFound
return b.eth.txPool.Add(signedTx) }
return bhash, bnum, index, err
} }
func (b *EthApiBackend) RemoveTx(txHash common.Hash) { // TransactionReceipt returns the receipt of a transaction.
b.eth.txMu.Lock() func (eth *Ethereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) {
defer b.eth.txMu.Unlock() r := core.GetReceipt(eth.chainDb, txhash)
if r == nil {
b.eth.txPool.Remove(txHash) return nil, ErrTxNotFound
}
return r, nil
} }
func (b *EthApiBackend) GetPoolTransactions() types.Transactions { // BlockTD returns the total difficulty of a certain block.
b.eth.txMu.Lock() func (eth *Ethereum) BlockTD(blockhash common.Hash) *big.Int {
defer b.eth.txMu.Unlock() return eth.blockchain.GetTdByHash(blockhash)
}
// BalanceAt returns the balance of the given account.
func (eth *Ethereum) BalanceAt(ctx context.Context, addr common.Address, block *big.Int) (bal *big.Int, err error) {
err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { bal = st.GetBalance(addr) })
return bal, err
}
// CodeAt returns the code of the given account.
func (eth *Ethereum) CodeAt(ctx context.Context, addr common.Address, block *big.Int) (code []byte, err error) {
err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { code = st.GetCode(addr) })
return code, err
}
// NonceAt returns the nonce of the given account.
func (eth *Ethereum) NonceAt(ctx context.Context, addr common.Address, block *big.Int) (nonce uint64, err error) {
err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { nonce = st.GetNonce(addr) })
return nonce, err
}
// StorageAt returns a storage value of the given account.
func (eth *Ethereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, block *big.Int) (val []byte, err error) {
err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] })
return val, err
}
// PendingBalanceAt returns the balance of the given account in the pending state.
func (eth *Ethereum) PendingBalanceAt(ctx context.Context, addr common.Address) (bal *big.Int, err error) {
err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { bal = st.GetBalance(addr) })
return bal, err
}
// PendingBalanceAt returns the code of the given account in the pending state.
func (eth *Ethereum) PendingCodeAt(ctx context.Context, addr common.Address) (code []byte, err error) {
err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { code = st.GetCode(addr) })
return code, err
}
// PendingBalanceAt returns a storage value of the given account in the pending state.
func (eth *Ethereum) PendingStorageAt(ctx context.Context, addr common.Address, key common.Hash) (val []byte, err error) {
err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] })
return val, err
}
// PendingTransaction count returns the number of transactions in the pending block.
func (eth *Ethereum) PendingTransactionCount(ctx context.Context) (uint, error) {
b, _ := eth.miner.Pending()
return uint(len(b.Transactions())), nil
}
func (eth *Ethereum) withStateAt(ctx context.Context, block *big.Int, pending bool, fn func(st *state.StateDB)) error {
var st *state.StateDB
if pending {
_, st = eth.miner.Pending()
} else {
header, err := eth.HeaderByNumber(ctx, block)
if err != nil {
return err
}
st, err = eth.BlockChain().StateAt(header.Root)
if err != nil {
return err
}
}
fn(st)
return nil
}
// PendingBlock returns the next block as envisioned by the pending state.
func (eth *Ethereum) PendingBlock() (*types.Block, error) {
b, _ := eth.miner.Pending()
return b, nil
}
// PendingNonceAt returns the next valid nonce according to the local transaction pool.
func (eth *Ethereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) {
return eth.txPool.NonceAt(addr), nil
}
// PendingTransactions returns all known pending transactions.
func (eth *Ethereum) PendingTransactions() []*types.Transaction {
eth.txMu.Lock()
defer eth.txMu.Unlock()
var txs types.Transactions var txs types.Transactions
for _, batch := range b.eth.txPool.Pending() { for _, batch := range eth.txPool.Pending() {
txs = append(txs, batch...) txs = append(txs, batch...)
} }
return txs return txs
} }
func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { // SendTransaction queues a transaction in the pool.
b.eth.txMu.Lock() func (eth *Ethereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error {
defer b.eth.txMu.Unlock() eth.txMu.Lock()
defer eth.txMu.Unlock()
return b.eth.txPool.Get(hash) eth.txPool.SetLocal(signedTx)
return eth.txPool.Add(signedTx)
} }
func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { // SyncProgress returns sync status information.
b.eth.txMu.Lock() func (b *Ethereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
defer b.eth.txMu.Unlock() return b.protocolManager.downloader.Progress(), nil
return b.eth.txPool.State().GetNonce(addr), nil
} }
func (b *EthApiBackend) Stats() (pending int, queued int) { // SuggestGasPrice returns a suitable gas price based on the content of recently seen blocks.
b.eth.txMu.Lock() func (eth *Ethereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
defer b.eth.txMu.Unlock() return eth.gpo.SuggestPrice(), nil
return b.eth.txPool.Stats()
} }
func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { // RemoveTransaction removes the given transaction from the local pool.
b.eth.txMu.Lock() func (eth *Ethereum) RemoveTransaction(txHash common.Hash) {
defer b.eth.txMu.Unlock() eth.txMu.Lock()
defer eth.txMu.Unlock()
return b.eth.TxPool().Content() eth.txPool.Remove(txHash)
} }
func (b *EthApiBackend) Downloader() *downloader.Downloader { // ProtocolVersion returns the active protocol version.
return b.eth.Downloader() func (eth *Ethereum) ProtocolVersion() int {
return int(eth.protocolManager.SubProtocols[0].Version)
} }
func (b *EthApiBackend) ProtocolVersion() int { // AccountManager returns the internal account manager that accesses the data directory.
return b.eth.EthVersion() // Deprecated: get the account manager through node.Node instead.
func (eth *Ethereum) AccountManager() *accounts.Manager {
return eth.accountManager
} }
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { // ResetHeadBlock resets the blockchain to the given number.
return b.gpo.SuggestPrice(), nil // Use this method if you know what you're doing.
func (eth *Ethereum) ResetHeadBlock(blocknum uint64) {
eth.blockchain.SetHead(blocknum)
} }
func (b *EthApiBackend) ChainDb() ethdb.Database { func (eth *Ethereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) {
return b.eth.ChainDb() block, state := eth.miner.Pending()
_, gas, err := eth.callContract(call, block.Header(), state)
return gas, err
} }
func (b *EthApiBackend) EventMux() *event.TypeMux { func (eth *Ethereum) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
return b.eth.EventMux() block, state := eth.miner.Pending()
val, _, err := eth.callContract(call, block.Header(), state)
return val, err
} }
func (b *EthApiBackend) AccountManager() *accounts.Manager { func (eth *Ethereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) {
return b.eth.AccountManager() var head *types.Header
if blocknum == nil {
head = eth.blockchain.CurrentHeader()
} else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil {
return nil, ErrBlockNotFound
}
state, err := eth.blockchain.StateAt(head.Root)
if err != nil {
return nil, err
}
val, _, err := eth.callContract(call, head, state)
return val, err
} }
type EthApiState struct { func (eth *Ethereum) callContract(call ethereum.CallMsg, head *types.Header, state *state.StateDB) ([]byte, *big.Int, error) {
state *state.StateDB return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment {
} return core.NewEnv(state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{})
})
func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {
return s.state.GetBalance(addr), nil
}
func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) {
return s.state.GetCode(addr), nil
}
func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) {
return s.state.GetState(a, b), nil
}
func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
return s.state.GetNonce(addr), nil
} }

View file

@ -31,7 +31,6 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/httpclient"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -128,14 +127,13 @@ type Ethereum struct {
pow *ethash.Ethash pow *ethash.Ethash
accountManager *accounts.Manager accountManager *accounts.Manager
ApiBackend *EthApiBackend
miner *miner.Miner miner *miner.Miner
Mining bool Mining bool
MinerThreads int MinerThreads int
AutoDAG bool AutoDAG bool
autodagquit chan bool autodagquit chan bool
etherbase common.Address etherbase common.Address
gpo *gasprice.GasPriceOracle
solcPath string solcPath string
NatSpec bool NatSpec bool
@ -219,14 +217,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
ForceJit: config.ForceJit, ForceJit: config.ForceJit,
} }
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.eventMux)
if err != nil { if err != nil {
if err == core.ErrNoGenesis { if err == core.ErrNoGenesis {
return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`)
} }
return nil, err return nil, err
} }
newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) newPool := core.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool eth.txPool = newPool
maxPeers := config.MaxPeers maxPeers := config.MaxPeers
@ -243,10 +241,13 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
return nil, err return nil, err
} }
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow)
// Set up the miner.
eth.miner = miner.New(eth, eth.chainConfig, eth.eventMux, eth.pow)
eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)
// Set up the gas price oracle.
gpoParams := &gasprice.GpoParams{ gpoParams := &gasprice.GpoParams{
GpoMinGasPrice: config.GpoMinGasPrice, GpoMinGasPrice: config.GpoMinGasPrice,
GpoMaxGasPrice: config.GpoMaxGasPrice, GpoMaxGasPrice: config.GpoMaxGasPrice,
@ -255,8 +256,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
GpobaseStepUp: config.GpobaseStepUp, GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
} }
gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) eth.gpo = gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
eth.ApiBackend = &EthApiBackend{eth, gpo}
return eth, nil return eth, nil
} }
@ -310,50 +310,58 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) {
// APIs returns the collection of RPC services the ethereum package offers. // APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else. // NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API { func (eth *Ethereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{
{ {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: NewPublicEthereumAPI(s), Service: NewPublicEthereumAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: NewPublicMinerAPI(s), Service: NewPublicMinerAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux),
Public: true, Public: true,
}, { }, {
Namespace: "miner", Namespace: "miner",
Version: "1.0", Version: "1.0",
Service: NewPrivateMinerAPI(s), Service: NewPrivateMinerAPI(eth),
Public: false, Public: false,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: filters.NewPublicFilterAPI(s.ApiBackend, false), Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, false),
Public: true, Public: true,
}, { }, {
Namespace: "admin", Namespace: "admin",
Version: "1.0", Version: "1.0",
Service: NewPrivateAdminAPI(s), Service: NewPrivateAdminAPI(eth),
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPublicDebugAPI(s), Service: NewPublicDebugAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPrivateDebugAPI(s.chainConfig, s), Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(eth.chainConfig, eth),
}, {
Namespace: "txpool",
Version: "1.0",
Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool},
}, { }, {
Namespace: "net", Namespace: "net",
Version: "1.0", Version: "1.0",
Service: s.netRPCService, Service: eth.netRPCService,
Public: true, Public: true,
}, },
}...) }...)
@ -394,18 +402,14 @@ func (s *Ethereum) StartMining(threads int) error {
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }
func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } // Deprecated Accessors
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
func (s *Ethereum) NetVersion() int { return s.netVersionId } func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured // Protocols implements node.Service, returning all the currently configured
// network protocols to start. // network protocols to start.
@ -420,7 +424,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the // Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start(srvr *p2p.Server) error { func (s *Ethereum) Start(srvr *p2p.Server) error {
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId)
if s.AutoDAG { if s.AutoDAG {
s.StartAutoDAG() s.StartAutoDAG()
} }
@ -522,12 +526,6 @@ func (self *Ethereum) StopAutoDAG() {
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
} }
// HTTPClient returns the light http client used for fetching offchain docs
// (natspec, source for verification)
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
return self.httpclient
}
// dagFiles(epoch) returns the two alternative DAG filenames (not a path) // dagFiles(epoch) returns the two alternative DAG filenames (not a path)
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])> // 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
func dagFiles(epoch uint64) (string, string) { func dagFiles(epoch uint64) (string, string) {

View file

@ -1,136 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
// ContractBackend implements bind.ContractBackend with direct calls to Ethereum
// internals to support operating on contracts within subprotocols like eth and
// swarm.
//
// Internally this backend uses the already exposed API endpoints of the Ethereum
// object. These should be rewritten to internal Go method calls when the Go API
// is refactored to support a clean library use.
type ContractBackend struct {
eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data
txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
}
// NewContractBackend creates a new native contract backend using an existing
// Etheruem object.
func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend {
return &ContractBackend{
eapi: ethapi.NewPublicEthereumAPI(apiBackend),
bcapi: ethapi.NewPublicBlockChainAPI(apiBackend),
txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend),
}
}
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum))
return common.FromHex(out), err
}
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber)
return common.FromHex(out), err
}
// ContractCall implements bind.ContractCaller executing an Ethereum contract
// call with the specified data as the input. The pending flag requests execution
// against the pending block, not the stable head of the chain.
func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum))
return common.FromHex(out), err
}
// ContractCall implements bind.ContractCaller executing an Ethereum contract
// call with the specified data as the input. The pending flag requests execution
// against the pending block, not the stable head of the chain.
func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber)
return common.FromHex(out), err
}
func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs {
args := ethapi.CallArgs{
To: msg.To,
From: msg.From,
Data: common.ToHex(msg.Data),
}
if msg.Gas != nil {
args.Gas = *rpc.NewHexNumber(msg.Gas)
}
if msg.GasPrice != nil {
args.GasPrice = *rpc.NewHexNumber(msg.GasPrice)
}
if msg.Value != nil {
args.Value = *rpc.NewHexNumber(msg.Value)
}
return args
}
func toBlockNumber(num *big.Int) rpc.BlockNumber {
if num == nil {
return rpc.LatestBlockNumber
}
return rpc.BlockNumber(num.Int64())
}
// PendingAccountNonce implements bind.ContractTransactor retrieving the current
// pending nonce associated with an account.
func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
return out.Uint64(), err
}
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
// suggested gas price to allow a timely execution of a transaction.
func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return b.eapi.GasPrice(ctx)
}
// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas
// needed to execute a specific transaction based on the current pending state of
// the backend blockchain. There is no guarantee that this is the true gas limit
// requirement as other transactions may be added or removed by miners, but it
// should provide a basis for setting a reasonable default.
func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg))
return out.BigInt(), err
}
// SendTransaction implements bind.ContractTransactor injects the transaction
// into the pending pool for execution.
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
raw, _ := rlp.EncodeToBytes(tx)
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
return err
}

View file

@ -76,7 +76,7 @@ func (api *PublicDownloaderAPI) eventLoop() {
case StartEvent: case StartEvent:
notification = &SyncingResult{ notification = &SyncingResult{
Syncing: true, Syncing: true,
Status: api.d.Progress(), Status: *api.d.Progress(),
} }
case DoneEvent, FailedEvent: case DoneEvent, FailedEvent:
notification = false notification = false

View file

@ -212,7 +212,7 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader he
// In addition, during the state download phase of fast synchronisation the number // In addition, during the state download phase of fast synchronisation the number
// of processed and the total number of known states are also returned. Otherwise // of processed and the total number of known states are also returned. Otherwise
// these are zero. // these are zero.
func (d *Downloader) Progress() ethereum.SyncProgress { func (d *Downloader) Progress() *ethereum.SyncProgress {
// Fetch the pending state count outside of the lock to prevent unforeseen deadlocks // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks
pendingStates := uint64(d.queue.PendingNodeData()) pendingStates := uint64(d.queue.PendingNodeData())
@ -229,7 +229,7 @@ func (d *Downloader) Progress() ethereum.SyncProgress {
case LightSync: case LightSync:
current = d.headHeader().Number.Uint64() current = d.headHeader().Number.Uint64()
} }
return ethereum.SyncProgress{ return &ethereum.SyncProgress{
StartingBlock: d.syncStatsChainOrigin, StartingBlock: d.syncStatsChainOrigin,
CurrentBlock: current, CurrentBlock: current,
HighestBlock: d.syncStatsChainHeight, HighestBlock: d.syncStatsChainHeight,

View file

@ -56,20 +56,20 @@ type PublicFilterAPI struct {
useMipMap bool useMipMap bool
mux *event.TypeMux mux *event.TypeMux
quit chan struct{} quit chan struct{}
chainDb ethdb.Database chaindb ethdb.Database
events *EventSystem events *EventSystem
filtersMu sync.Mutex filtersMu sync.Mutex
filters map[rpc.ID]*filter filters map[rpc.ID]*filter
} }
// NewPublicFilterAPI returns a new PublicFilterAPI instance. // NewPublicFilterAPI returns a new PublicFilterAPI instance.
func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { func NewPublicFilterAPI(backend Backend, mux *event.TypeMux, chaindb ethdb.Database, lightMode bool) *PublicFilterAPI {
api := &PublicFilterAPI{ api := &PublicFilterAPI{
backend: backend, backend: backend,
useMipMap: !lightMode, useMipMap: !lightMode,
mux: backend.EventMux(), mux: mux,
chainDb: backend.ChainDb(), chaindb: chaindb,
events: NewEventSystem(backend.EventMux(), backend, lightMode), events: NewEventSystem(mux, backend, lightMode),
filters: make(map[rpc.ID]*filter), filters: make(map[rpc.ID]*filter),
} }
@ -326,7 +326,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
} }
filter := New(api.backend, api.useMipMap) filter := New(api.backend, api.chaindb, api.useMipMap)
filter.SetBeginBlock(crit.FromBlock.Int64()) filter.SetBeginBlock(crit.FromBlock.Int64())
filter.SetEndBlock(crit.ToBlock.Int64()) filter.SetEndBlock(crit.ToBlock.Int64())
filter.SetAddresses(crit.Addresses) filter.SetAddresses(crit.Addresses)
@ -366,7 +366,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log
return []Log{}, nil return []Log{}, nil
} }
filter := New(api.backend, api.useMipMap) filter := New(api.backend, api.chaindb, api.useMipMap)
filter.SetBeginBlock(f.crit.FromBlock.Int64()) filter.SetBeginBlock(f.crit.FromBlock.Int64())
filter.SetEndBlock(f.crit.ToBlock.Int64()) filter.SetEndBlock(f.crit.ToBlock.Int64())
filter.SetAddresses(f.crit.Addresses) filter.SetAddresses(f.crit.Addresses)

View file

@ -18,22 +18,20 @@ package filters
import ( import (
"math" "math"
"math/big"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type Backend interface { type Backend interface {
ChainDb() ethdb.Database HeaderByHash(context.Context, common.Hash) (*types.Header, error)
EventMux() *event.TypeMux HeaderByNumber(context.Context, *big.Int) (*types.Header, error)
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
} }
// Filter can be used to retrieve and filter logs // Filter can be used to retrieve and filter logs
@ -51,11 +49,11 @@ type Filter struct {
// New creates a new filter which uses a bloom filter on blocks to figure out whether // New creates a new filter which uses a bloom filter on blocks to figure out whether
// a particular block is interesting or not. // a particular block is interesting or not.
func New(backend Backend, useMipMap bool) *Filter { func New(backend Backend, chaindb ethdb.Database, useMipMap bool) *Filter {
return &Filter{ return &Filter{
backend: backend, backend: backend,
useMipMap: useMipMap, useMipMap: useMipMap,
db: backend.ChainDb(), db: chaindb,
} }
} }
@ -84,7 +82,7 @@ func (f *Filter) SetTopics(topics [][]common.Hash) {
// Run filters logs with the current parameters set // Run filters logs with the current parameters set
func (f *Filter) Find(ctx context.Context) ([]Log, error) { func (f *Filter) Find(ctx context.Context) ([]Log, error) {
head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) head, _ := f.backend.HeaderByNumber(ctx, nil)
if head == nil { if head == nil {
return nil, nil return nil, nil
} }
@ -141,7 +139,7 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) {
func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) {
for i := start; i <= end; i++ { for i := start; i <= end; i++ {
header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) header, err := f.backend.HeaderByNumber(ctx, new(big.Int).SetUint64(i))
if header == nil || err != nil { if header == nil || err != nil {
return logs, err return logs, err
} }
@ -150,7 +148,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
// current parameters // current parameters
if f.bloomFilter(header.Bloom) { if f.bloomFilter(header.Bloom) {
// Get the logs of the block // Get the logs of the block
receipts, err := f.backend.GetReceipts(ctx, header.Hash()) receipts, err := f.backend.BlockReceipts(ctx, header.Hash(), i)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -301,27 +303,44 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) {
oldh := es.lastHead oldh := es.lastHead
es.lastHead = newHeader
if oldh == nil { if oldh == nil {
return return
} }
newh := newHeader ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
// find common ancestor, create list of rolled back and new block hashes // find common ancestor, create list of rolled back and new block hashes
var oldHeaders, newHeaders []*types.Header var (
oldHeaders, newHeaders []*types.Header
newh = newHeader
err error
)
for oldh.Hash() != newh.Hash() { for oldh.Hash() != newh.Hash() {
if oldh.Number.Uint64() >= newh.Number.Uint64() { if oldh.Number.Uint64() >= newh.Number.Uint64() {
oldHeaders = append(oldHeaders, oldh) oldHeaders = append(oldHeaders, oldh)
oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) oldh, err = es.backend.HeaderByHash(ctx, oldh.ParentHash)
if err != nil {
break
}
} }
if oldh.Number.Uint64() < newh.Number.Uint64() { if oldh.Number.Uint64() < newh.Number.Uint64() {
newHeaders = append(newHeaders, newh) newHeaders = append(newHeaders, newh)
newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) newh, err = es.backend.HeaderByHash(ctx, newh.ParentHash)
if err != nil {
break
}
if newh == nil { if newh == nil {
// happens when CHT syncing, nothing to do // happens when CHT syncing, nothing to do
newh = oldh newh = oldh
} }
} }
} }
if err != nil {
if glog.V(logger.Error) {
glog.Errorf("aborted rollback, can't fetch header: %v", err)
}
return
}
// roll back old blocks // roll back old blocks
for _, h := range oldHeaders { for _, h := range oldHeaders {
callBack(h, true) callBack(h, true)
@ -330,6 +349,7 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
for i := len(newHeaders) - 1; i >= 0; i-- { for i := len(newHeaders) - 1; i >= 0; i-- {
callBack(newHeaders[i], false) callBack(newHeaders[i], false)
} }
es.lastHead = newh
} }
// filter logs of a single header in light client mode // filter logs of a single header in light client mode
@ -339,7 +359,7 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.
//fmt.Println("bloom match") //fmt.Println("bloom match")
// Get the logs of the block // Get the logs of the block
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
receipts, err := es.backend.GetReceipts(ctx, header.Hash()) receipts, err := es.backend.BlockReceipts(ctx, header.Hash(), header.Number.Uint64())
if err != nil { if err != nil {
return nil return nil
} }

View file

@ -37,7 +37,7 @@ var (
mux = new(event.TypeMux) mux = new(event.TypeMux)
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db} backend = &testBackend{mux, db}
api = NewPublicFilterAPI(backend, false) api = NewPublicFilterAPI(backend, mux, db, false)
) )
type testBackend struct { type testBackend struct {
@ -45,30 +45,26 @@ type testBackend struct {
db ethdb.Database db ethdb.Database
} }
func (b *testBackend) ChainDb() ethdb.Database { func (b *testBackend) HeaderByNumber(ctx context.Context, blockNum *big.Int) (*types.Header, error) {
return b.db
}
func (b *testBackend) EventMux() *event.TypeMux {
return b.mux
}
func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
var hash common.Hash var hash common.Hash
var num uint64 var num uint64
if blockNr == rpc.LatestBlockNumber { if blockNum == nil {
hash = core.GetHeadBlockHash(b.db) hash = core.GetHeadBlockHash(b.db)
num = core.GetBlockNumber(b.db, hash) num = core.GetBlockNumber(b.db, hash)
} else { } else {
num = uint64(blockNr) num = blockNum.Uint64()
hash = core.GetCanonicalHash(b.db, num) hash = core.GetCanonicalHash(b.db, num)
} }
return core.GetHeader(b.db, hash, num), nil return core.GetHeader(b.db, hash, num), nil
} }
func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { func (b *testBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) {
num := core.GetBlockNumber(b.db, blockHash) num := core.GetBlockNumber(b.db, blockHash)
return core.GetBlockReceipts(b.db, blockHash, num), nil return core.GetHeader(b.db, blockHash, num), nil
}
func (b *testBackend) BlockReceipts(ctx context.Context, blockHash common.Hash, blockNum uint64) ([]*types.Receipt, error) {
return core.GetBlockReceipts(b.db, blockHash, blockNum), nil
} }
// TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events.

View file

@ -103,7 +103,7 @@ func BenchmarkMipmaps(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
filter := New(backend, true) filter := New(backend, db, true)
filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -205,7 +205,7 @@ func TestFilters(t *testing.T) {
} }
} }
filter := New(backend, true) filter := New(backend, db, true)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
@ -216,7 +216,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 4 log, got", len(logs)) t.Error("expected 4 log, got", len(logs))
} }
filter = New(backend, true) filter = New(backend, db, true)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(900) filter.SetBeginBlock(900)
@ -229,7 +229,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(backend, true) filter = New(backend, db, true)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(990) filter.SetBeginBlock(990)
@ -242,7 +242,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(backend, true) filter = New(backend, db, true)
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
filter.SetBeginBlock(1) filter.SetBeginBlock(1)
filter.SetEndBlock(10) filter.SetEndBlock(10)
@ -253,7 +253,7 @@ func TestFilters(t *testing.T) {
} }
failHash := common.BytesToHash([]byte("fail")) failHash := common.BytesToHash([]byte("fail"))
filter = New(backend, true) filter = New(backend, db, true)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -264,7 +264,7 @@ func TestFilters(t *testing.T) {
} }
failAddr := common.BytesToAddress([]byte("failmenow")) failAddr := common.BytesToAddress([]byte("failmenow"))
filter = New(backend, true) filter = New(backend, db, true)
filter.SetAddresses([]common.Address{failAddr}) filter.SetAddresses([]common.Address{failAddr})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -274,7 +274,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 0 log, got", len(logs)) t.Error("expected 0 log, got", len(logs))
} }
filter = New(backend, true) filter = New(backend, db, true)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)

View file

@ -21,9 +21,8 @@ import (
"sort" "sort"
"sync" "sync"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -38,7 +37,7 @@ const (
// LightPriceOracle recommends gas prices based on the content of recent // LightPriceOracle recommends gas prices based on the content of recent
// blocks. Suitable for both light and full clients. // blocks. Suitable for both light and full clients.
type LightPriceOracle struct { type LightPriceOracle struct {
backend ethapi.Backend backend ethereum.ChainReader
lastHead common.Hash lastHead common.Hash
lastPrice *big.Int lastPrice *big.Int
cacheLock sync.RWMutex cacheLock sync.RWMutex
@ -46,7 +45,7 @@ type LightPriceOracle struct {
} }
// NewLightPriceOracle returns a new oracle. // NewLightPriceOracle returns a new oracle.
func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { func NewLightPriceOracle(backend ethereum.ChainReader) *LightPriceOracle {
return &LightPriceOracle{ return &LightPriceOracle{
backend: backend, backend: backend,
lastPrice: big.NewInt(LpoDefaultPrice), lastPrice: big.NewInt(LpoDefaultPrice),
@ -60,7 +59,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error
lastPrice := self.lastPrice lastPrice := self.lastPrice
self.cacheLock.RUnlock() self.cacheLock.RUnlock()
head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) head, _ := self.backend.HeaderByNumber(ctx, nil)
headHash := head.Hash() headHash := head.Hash()
if headHash == lastHead { if headHash == lastHead {
return lastPrice, nil return lastPrice, nil
@ -132,7 +131,7 @@ type lpResult struct {
// getLowestPrice calculates the lowest transaction gas price in a given block // getLowestPrice calculates the lowest transaction gas price in a given block
// and sends it to the result channel. If the block is empty, price is nil. // and sends it to the result channel. If the block is empty, price is nil.
func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) { func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) {
block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) block, err := self.backend.BlockByNumber(ctx, new(big.Int).SetUint64(blockNum))
if block == nil { if block == nil {
chn <- lpResult{nil, err} chn <- lpResult{nil, err}
return return

View file

@ -143,15 +143,18 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
} }
// TransactionByHash returns the transaction with the given hash. // TransactionByHash returns the transaction with the given hash.
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
var tx *types.Transaction var rtx struct {
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) *types.Transaction
BlockHash *common.Hash
}
err = ec.c.CallContext(ctx, &rtx, "eth_getTransactionByHash", hash)
if err == nil { if err == nil {
if _, r, _ := tx.SignatureValues(); r == nil { if _, r, _ := rtx.SignatureValues(); r == nil {
return nil, fmt.Errorf("server returned transaction without signature") return nil, false, fmt.Errorf("server returned transaction without signature")
} }
} }
return tx, err return rtx.Transaction, rtx.BlockHash == nil, err
} }
// TransactionCount returns the total number of transactions in the given block. // TransactionCount returns the total number of transactions in the given block.

View file

@ -53,8 +53,8 @@ type ChainReader interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error)
TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error)
TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error)
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error)
} }
// ChainStateReader wraps access to the state trie of the canonical blockchain. Note that // ChainStateReader wraps access to the state trie of the canonical blockchain. Note that
@ -164,6 +164,9 @@ type PendingStateReader interface {
PendingTransactionCount(ctx context.Context) (uint, error) PendingTransactionCount(ctx context.Context) (uint, error)
} }
// TODO(fjl): PendingNonceAt should have its own interface because it is provided by the tx pool
// and more widely available than the others. We could also make it part of TransactionSender.
// PendingContractCaller can be used to perform calls against the pending state. // PendingContractCaller can be used to perform calls against the pending state.
type PendingContractCaller interface { type PendingContractCaller interface {
PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error)

View file

@ -20,15 +20,16 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"math/big" "math/big"
"strings" "strings"
"time" "time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -45,6 +46,8 @@ import (
const defaultGas = uint64(90000) const defaultGas = uint64(90000)
var ErrNoPendingState = errors.New("API backend does not provide access to pending state")
// PublicEthereumAPI provides an API to access Ethereum related information. // PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct { type PublicEthereumAPI struct {
@ -58,7 +61,7 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI {
// GasPrice returns a suggestion for a gas price. // GasPrice returns a suggestion for a gas price.
func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) {
return s.b.SuggestPrice(ctx) return s.b.SuggestGasPrice(ctx)
} }
// ProtocolVersion returns the current Ethereum protocol version this node supports // ProtocolVersion returns the current Ethereum protocol version this node supports
@ -73,8 +76,11 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber {
// - highestBlock: block number of the highest block header this node has received from peers // - highestBlock: block number of the highest block header this node has received from peers
// - pulledStates: number of state entries processed until now // - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled // - knownStates: number of known state entries that still need to be pulled
func (s *PublicEthereumAPI) Syncing() (interface{}, error) { func (s *PublicEthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
progress := s.b.Downloader().Progress() progress, err := s.b.SyncProgress(ctx)
if err != nil {
return nil, err
}
// Return not syncing if the synchronisation already completed // Return not syncing if the synchronisation already completed
if progress.CurrentBlock >= progress.HighestBlock { if progress.CurrentBlock >= progress.HighestBlock {
@ -90,87 +96,6 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
}, nil }, nil
} }
// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential.
type PublicTxPoolAPI struct {
b Backend
}
// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool.
func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI {
return &PublicTxPoolAPI{b}
}
// Content returns the transactions contained within the transaction pool.
func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction {
content := map[string]map[string]map[string]*RPCTransaction{
"pending": make(map[string]map[string]*RPCTransaction),
"queued": make(map[string]map[string]*RPCTransaction),
}
pending, queue := s.b.TxPoolContent()
// Flatten the pending transactions
for account, txs := range pending {
dump := make(map[string]*RPCTransaction)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx)
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, txs := range queue {
dump := make(map[string]*RPCTransaction)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx)
}
content["queued"][account.Hex()] = dump
}
return content
}
// Status returns the number of pending and queued transaction in the pool.
func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber {
pending, queue := s.b.Stats()
return map[string]*rpc.HexNumber{
"pending": rpc.NewHexNumber(pending),
"queued": rpc.NewHexNumber(queue),
}
}
// Inspect retrieves the content of the transaction pool and flattens it into an
// easily inspectable list.
func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string {
content := map[string]map[string]map[string]string{
"pending": make(map[string]map[string]string),
"queued": make(map[string]map[string]string),
}
pending, queue := s.b.TxPoolContent()
// Define a formatter to flatten a transaction into a string
var format = func(tx *types.Transaction) string {
if to := tx.To(); to != nil {
return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
}
return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice())
}
// Flatten the pending transactions
for account, txs := range pending {
dump := make(map[string]string)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = format(tx)
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, txs := range queue {
dump := make(map[string]string)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = format(tx)
}
content["queued"][account.Hex()] = dump
}
return content
}
// PublicAccountAPI provides an API to access accounts managed by this node. // PublicAccountAPI provides an API to access accounts managed by this node.
// It offers only methods that can retrieve accounts. // It offers only methods that can retrieve accounts.
type PublicAccountAPI struct { type PublicAccountAPI struct {
@ -265,7 +190,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -366,28 +291,16 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI {
// BlockNumber returns the block number of the chain head. // BlockNumber returns the block number of the chain head.
func (s *PublicBlockChainAPI) BlockNumber() *big.Int { func (s *PublicBlockChainAPI) BlockNumber() *big.Int {
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available header, _ := s.b.HeaderByNumber(context.Background(), nil) // latest header should always be available
return header.Number return header.Number
} }
// GetBalance returns the amount of wei for the given address in the state of the
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed.
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return nil, err
}
return state.GetBalance(ctx, address)
}
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all // 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. // 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) { func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr) block, err := blockByNumber(ctx, s.b, blockNr)
if block != nil { if block != nil {
response, err := s.rpcOutputBlock(block, true, fullTx) response, err := s.rpcOutputBlock(ctx, block, true, fullTx)
if err == nil && blockNr == rpc.PendingBlockNumber { if err == nil && blockNr == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields // Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} { for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} {
@ -399,12 +312,27 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.
return nil, err return nil, err
} }
func blockByNumber(ctx context.Context, b Backend, blockNr rpc.BlockNumber) (*types.Block, error) {
switch blockNr {
case rpc.PendingBlockNumber:
ps, ok := b.(PendingState)
if !ok {
return nil, fmt.Errorf("can't access the pending block with the current backend")
}
return ps.PendingBlock()
case rpc.LatestBlockNumber:
return b.BlockByNumber(ctx, nil)
default:
return b.BlockByNumber(ctx, big.NewInt(int64(blockNr)))
}
}
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // 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. // detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.GetBlock(ctx, blockHash) block, err := s.b.BlockByHash(ctx, blockHash)
if block != nil { if block != nil {
return s.rpcOutputBlock(block, true, fullTx) return s.rpcOutputBlock(ctx, block, true, fullTx)
} }
return nil, err return nil, err
} }
@ -412,7 +340,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash comm
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr) block, err := blockByNumber(ctx, s.b, blockNr)
if block != nil { if block != nil {
uncles := block.Uncles() uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) { if index.Int() < 0 || index.Int() >= len(uncles) {
@ -420,7 +348,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context,
return nil, nil return nil, nil
} }
block = types.NewBlockWithHeader(uncles[index.Int()]) block = types.NewBlockWithHeader(uncles[index.Int()])
return s.rpcOutputBlock(block, false, false) return s.rpcOutputBlock(ctx, block, false, false)
} }
return nil, err return nil, err
} }
@ -428,7 +356,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context,
// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true // GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (map[string]interface{}, error) {
block, err := s.b.GetBlock(ctx, blockHash) block, err := s.b.BlockByHash(ctx, blockHash)
if block != nil { if block != nil {
uncles := block.Uncles() uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) { if index.Int() < 0 || index.Int() >= len(uncles) {
@ -436,14 +364,14 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b
return nil, nil return nil, nil
} }
block = types.NewBlockWithHeader(uncles[index.Int()]) block = types.NewBlockWithHeader(uncles[index.Int()])
return s.rpcOutputBlock(block, false, false) return s.rpcOutputBlock(ctx, block, false, false)
} }
return nil, err return nil, err
} }
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Uncles())) return rpc.NewHexNumber(len(block.Uncles()))
} }
return nil return nil
@ -451,60 +379,78 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
return rpc.NewHexNumber(len(block.Uncles())) return rpc.NewHexNumber(len(block.Uncles()))
} }
return nil return nil
} }
// GetBalance returns the amount of wei for the given address in the state of the
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed.
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) {
switch blockNr {
case rpc.PendingBlockNumber:
ps, ok := s.b.(PendingState)
if !ok {
return nil, ErrNoPendingState
}
return ps.PendingBalanceAt(ctx, address)
case rpc.LatestBlockNumber:
return s.b.BalanceAt(ctx, address, nil)
default:
return s.b.BalanceAt(ctx, address, big.NewInt(int64(blockNr)))
}
}
// GetCode returns the code stored at the given address in the state for the given block number. // GetCode returns the code stored at the given address in the state for the given block number.
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) { func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var code []byte
if state == nil || err != nil { var err error
return "", err switch blockNr {
case rpc.PendingBlockNumber:
ps, ok := s.b.(PendingState)
if !ok {
return "", ErrNoPendingState
} }
res, err := state.GetCode(ctx, address) code, err = ps.PendingCodeAt(ctx, address)
if len(res) == 0 || err != nil { // backwards compatibility case rpc.LatestBlockNumber:
code, err = s.b.CodeAt(ctx, address, nil)
default:
code, err = s.b.CodeAt(ctx, address, big.NewInt(int64(blockNr)))
}
if len(code) == 0 || err != nil { // backwards compatibility
return "0x", err return "0x", err
} }
return common.ToHex(res), nil return common.ToHex(code), nil
} }
// GetStorageAt returns the storage from the state at the given address, key and // GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed. // numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key common.Hash, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var val []byte
if state == nil || err != nil { var err error
switch blockNr {
case rpc.PendingBlockNumber:
ps, ok := s.b.(PendingState)
if !ok {
return "", ErrNoPendingState
}
val, err = ps.PendingStorageAt(ctx, address, key)
case rpc.LatestBlockNumber:
val, err = s.b.StorageAt(ctx, address, key, nil)
default:
val, err = s.b.StorageAt(ctx, address, key, big.NewInt(int64(blockNr)))
}
if len(val) == 0 || err != nil { // backwards compatibility
return "0x", err return "0x", err
} }
res, err := state.GetState(ctx, address, common.HexToHash(key)) return common.ToHex(val), nil
if err != nil {
return "0x", err
}
return res.Hex(), nil
} }
// callmsg is the message type used for call transations.
type callmsg struct {
addr common.Address
to *common.Address
gas, gasPrice *big.Int
value *big.Int
data []byte
}
// accessor boilerplate to implement core.Message
func (m callmsg) From() (common.Address, error) { return m.addr, nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.to }
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gas }
func (m callmsg) Value() *big.Int { return m.value }
func (m callmsg) Data() []byte { return m.data }
// CallArgs represents the arguments for a call. // CallArgs represents the arguments for a call.
type CallArgs struct { type CallArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
@ -512,74 +458,43 @@ type CallArgs struct {
Gas rpc.HexNumber `json:"gas"` Gas rpc.HexNumber `json:"gas"`
GasPrice rpc.HexNumber `json:"gasPrice"` GasPrice rpc.HexNumber `json:"gasPrice"`
Value rpc.HexNumber `json:"value"` Value rpc.HexNumber `json:"value"`
Data string `json:"data"` Data rpc.HexBytes `json:"data"`
} }
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { func (args CallArgs) Msg() ethereum.CallMsg {
defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) return ethereum.CallMsg{
From: args.From,
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) To: args.To,
if state == nil || err != nil { Gas: args.Gas.BigInt(),
return "0x", common.Big0, err GasPrice: args.GasPrice.BigInt(),
Value: args.Value.BigInt(),
Data: args.Data,
} }
// Set the account address to interact with
var addr common.Address
if args.From == (common.Address{}) {
accounts := s.b.AccountManager().Accounts()
if len(accounts) == 0 {
addr = common.Address{}
} else {
addr = accounts[0].Address
}
} else {
addr = args.From
}
// Assemble the CALL invocation
msg := callmsg{
addr: addr,
to: args.To,
gas: args.Gas.BigInt(),
gasPrice: args.GasPrice.BigInt(),
value: args.Value.BigInt(),
data: common.FromHex(args.Data),
}
if msg.gas.Cmp(common.Big0) == 0 {
msg.gas = big.NewInt(50000000)
}
if msg.gasPrice.Cmp(common.Big0) == 0 {
msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
}
// Execute the call and return
vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header)
if err != nil {
return "0x", common.Big0, err
}
gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
if err := vmError(); err != nil {
return "0x", common.Big0, err
}
if len(res) == 0 { // backwards compatability
return "0x", gas, err
}
return common.ToHex(res), gas, err
} }
// Call executes the given transaction on the state for the given block number. // Call executes the given transaction on the state for the given block number.
// It doesn't make and changes in the state/blockchain and is usefull to execute and retrieve values. // It doesn't make and changes in the state/blockchain and is usefull to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) { func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) {
result, _, err := s.doCall(ctx, args, blockNr) var result []byte
return result, err var err error
switch blockNr {
case rpc.PendingBlockNumber:
ps, ok := s.b.(PendingState)
if !ok {
return "", ErrNoPendingState
}
result, err = ps.PendingCallContract(ctx, args.Msg())
case rpc.LatestBlockNumber:
result, err = s.b.CallContract(ctx, args.Msg(), nil)
default:
result, err = s.b.CallContract(ctx, args.Msg(), big.NewInt(int64(blockNr)))
}
return common.ToHex(result), err
} }
// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction.
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*rpc.HexNumber, error) { func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*rpc.HexNumber, error) {
_, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber) gas, err := s.b.EstimateGas(ctx, args.Msg())
return rpc.NewHexNumber(gas), err return rpc.NewHexNumber(gas), err
} }
@ -639,7 +554,7 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes {
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // rpcOutputBlock 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 // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes. // transaction hashes.
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) rpcOutputBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
head := b.Header() // copies the header once head := b.Header() // copies the header once
fields := map[string]interface{}{ fields := map[string]interface{}{
"number": rpc.NewHexNumber(head.Number), "number": rpc.NewHexNumber(head.Number),
@ -652,7 +567,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
"stateRoot": head.Root, "stateRoot": head.Root,
"miner": head.Coinbase, "miner": head.Coinbase,
"difficulty": rpc.NewHexNumber(head.Difficulty), "difficulty": rpc.NewHexNumber(head.Difficulty),
"totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), "totalDifficulty": rpc.NewHexNumber(s.b.BlockTD(b.Hash())),
"extraData": rpc.HexBytes(head.Extra), "extraData": rpc.HexBytes(head.Extra),
"size": rpc.NewHexNumber(b.Size().Int64()), "size": rpc.NewHexNumber(b.Size().Int64()),
"gasLimit": rpc.NewHexNumber(head.GasLimit), "gasLimit": rpc.NewHexNumber(head.GasLimit),
@ -663,22 +578,15 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
} }
if inclTx { if inclTx {
formatTx := func(tx *types.Transaction) (interface{}, error) {
return tx.Hash(), nil
}
if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
return newRPCTransaction(b, tx.Hash())
}
}
txs := b.Transactions() txs := b.Transactions()
transactions := make([]interface{}, len(txs)) transactions := make([]interface{}, len(txs))
var err error
for i, tx := range b.Transactions() { for i, tx := range b.Transactions() {
if transactions[i], err = formatTx(tx); err != nil { if fullTx {
return nil, err rtx := newRPCTransaction(tx)
rtx.setInclusionBlock(b.Hash(), b.NumberU64(), i)
transactions = append(transactions, rtx)
} else {
transactions = append(transactions, tx.Hash())
} }
} }
fields["transactions"] = transactions fields["transactions"] = transactions
@ -712,8 +620,14 @@ type RPCTransaction struct {
S *rpc.HexNumber `json:"s"` S *rpc.HexNumber `json:"s"`
} }
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func (tx *RPCTransaction) setInclusionBlock(blockhash common.Hash, blocknum uint64, index int) {
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { tx.BlockHash = blockhash
tx.BlockNumber = rpc.NewHexNumber(blocknum)
tx.TransactionIndex = rpc.NewHexNumber(index)
}
// newRPCTransaction returns a pending transaction that will serialize to the RPC representation
func newRPCTransaction(tx *types.Transaction) *RPCTransaction {
from, _ := tx.FromFrontier() from, _ := tx.FromFrontier()
v, r, s := tx.SignatureValues() v, r, s := tx.SignatureValues()
return &RPCTransaction{ return &RPCTransaction{
@ -732,33 +646,13 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
} }
// newRPCTransaction returns a transaction that will serialize to the RPC representation. // newRPCTransaction returns a transaction that will serialize to the RPC representation.
func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) *RPCTransaction {
if txIndex >= 0 && txIndex < len(b.Transactions()) { if txIndex >= 0 && txIndex < len(b.Transactions()) {
tx := b.Transactions()[txIndex] tx := newRPCTransaction(b.Transactions()[txIndex])
from, err := tx.FromFrontier() tx.setInclusionBlock(b.Hash(), b.NumberU64(), txIndex)
if err != nil { return tx
return nil, err
} }
v, r, s := tx.SignatureValues() return nil
return &RPCTransaction{
BlockHash: b.Hash(),
BlockNumber: rpc.NewHexNumber(b.Number()),
From: from,
Gas: rpc.NewHexNumber(tx.Gas()),
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
Hash: tx.Hash(),
Input: rpc.HexBytes(tx.Data()),
Nonce: rpc.NewHexNumber(tx.Nonce()),
To: tx.To(),
TransactionIndex: rpc.NewHexNumber(txIndex),
Value: rpc.NewHexNumber(tx.Value()),
V: rpc.NewHexNumber(v),
R: rpc.NewHexNumber(r),
S: rpc.NewHexNumber(s),
}, nil
}
return nil, nil
} }
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
@ -767,18 +661,6 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexByt
tx := b.Transactions()[txIndex] tx := b.Transactions()[txIndex]
return rlp.EncodeToBytes(tx) return rlp.EncodeToBytes(tx)
} }
return nil, nil
}
// newRPCTransaction returns a transaction that will serialize to the RPC representation.
func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) {
for idx, tx := range b.Transactions() {
if tx.Hash() == txHash {
return newRPCTransactionFromBlockIndex(b, idx)
}
}
return nil, nil return nil, nil
} }
@ -792,27 +674,9 @@ func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI {
return &PublicTransactionPoolAPI{b} return &PublicTransactionPoolAPI{b}
} }
func getTransaction(chainDb ethdb.Database, b Backend, txHash common.Hash) (*types.Transaction, bool, error) {
txData, err := chainDb.Get(txHash.Bytes())
isPending := false
tx := new(types.Transaction)
if err == nil && len(txData) > 0 {
if err := rlp.DecodeBytes(txData, tx); err != nil {
return nil, isPending, err
}
} else {
// pending transaction?
tx = b.GetPoolTransaction(txHash)
isPending = true
}
return tx, isPending, nil
}
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Transactions())) return rpc.NewHexNumber(len(block.Transactions()))
} }
return nil return nil
@ -820,31 +684,32 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { count, err := s.b.TransactionCount(ctx, blockHash)
return rpc.NewHexNumber(len(block.Transactions())) if err == nil {
return rpc.NewHexNumber(count)
} }
return nil return nil
} }
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) *RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil {
return newRPCTransactionFromBlockIndex(block, index.Int()) return newRPCTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil
} }
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (*RPCTransaction, error) { func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) *RPCTransaction {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
return newRPCTransactionFromBlockIndex(block, index.Int()) return newRPCTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil
} }
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil {
return newRPCRawTransactionFromBlockIndex(block, index.Int()) return newRPCRawTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil, nil
@ -852,7 +717,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx co
// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. // GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index.
func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
return newRPCRawTransactionFromBlockIndex(block, index.Int()) return newRPCRawTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil, nil
@ -860,11 +725,16 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont
// GetTransactionCount returns the number of transactions the given address has sent for the given block number // GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var nonce uint64
if state == nil || err != nil { var err error
return nil, err switch blockNr {
case rpc.PendingBlockNumber:
nonce, err = s.b.PendingNonceAt(ctx, address)
case rpc.LatestBlockNumber:
nonce, err = s.b.NonceAt(ctx, address, nil)
default:
nonce, err = s.b.NonceAt(ctx, address, big.NewInt(int64(blockNr)))
} }
nonce, err := state.GetNonce(ctx, address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -894,82 +764,50 @@ func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common
} }
// GetTransactionByHash returns the transaction for the given hash // GetTransactionByHash returns the transaction for the given hash
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*RPCTransaction, error) { func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txhash common.Hash) (*RPCTransaction, error) {
var tx *types.Transaction tx, isPending, err := s.b.TransactionByHash(ctx, txhash)
var isPending bool if tx == nil || err != nil {
var err error
if tx, isPending, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil {
glog.V(logger.Debug).Infof("%v\n", err)
return nil, nil
} else if tx == nil {
return nil, nil return nil, nil
} }
rtx := newRPCTransaction(tx)
if isPending { if isPending {
return newRPCPendingTransaction(tx), nil return rtx, nil
} } else if tib, ok := s.b.(TransactionInclusionBlock); ok {
bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash)
blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), txHash)
if err != nil { if err != nil {
glog.V(logger.Debug).Infof("%v\n", err) return nil, err
return nil, nil
} }
rtx.setInclusionBlock(bhash, bnum, index)
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil {
return newRPCTransaction(block, txHash)
} }
return rtx, nil
return nil, nil
} }
// GetRawTransactionByHash returns the bytes of the transaction for the given hash. // GetRawTransactionByHash returns the bytes of the transaction for the given hash.
func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txhash common.Hash) (rpc.HexBytes, error) {
var tx *types.Transaction tx, _, err := s.b.TransactionByHash(ctx, txhash)
var err error if tx == nil || err != nil {
if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil {
glog.V(logger.Debug).Infof("%v\n", err)
return nil, nil
} else if tx == nil {
return nil, nil return nil, nil
} }
return rlp.EncodeToBytes(tx) return rlp.EncodeToBytes(tx)
} }
// GetTransactionReceipt returns the transaction receipt for the given transaction hash. // GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, txhash common.Hash) (map[string]interface{}, error) {
receipt := core.GetReceipt(s.b.ChainDb(), txHash) receipt, err := s.b.TransactionReceipt(ctx, txhash)
if receipt == nil {
glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex())
return nil, nil
}
tx, _, err := getTransaction(s.b.ChainDb(), s.b, txHash)
if err != nil { if err != nil {
glog.V(logger.Debug).Infof("%v\n", err) glog.V(logger.Debug).Infof("receipt not found for transaction %s: %v", txhash.Hex(), err)
return nil, nil return nil, nil
} }
tx, isPending, err := s.b.TransactionByHash(ctx, txhash)
txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash)
if err != nil { if err != nil {
glog.V(logger.Debug).Infof("%v\n", err) glog.V(logger.Debug).Infof("can't get transaction %s: %v\n", txhash.Hex(), err)
return nil, nil
}
from, err := tx.FromFrontier()
if err != nil {
glog.V(logger.Debug).Infof("%v\n", err)
return nil, nil return nil, nil
} }
from, _ := tx.FromFrontier()
fields := map[string]interface{}{ fields := map[string]interface{}{
"root": rpc.HexBytes(receipt.PostState), "root": rpc.HexBytes(receipt.PostState),
"blockHash": txBlock, "transactionHash": txhash,
"blockNumber": rpc.NewHexNumber(blockIndex),
"transactionHash": txHash,
"transactionIndex": rpc.NewHexNumber(index),
"from": from, "from": from,
"to": tx.To(), "to": tx.To(),
"gasUsed": rpc.NewHexNumber(receipt.GasUsed), "gasUsed": rpc.NewHexNumber(receipt.GasUsed),
@ -978,10 +816,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
"logs": receipt.Logs, "logs": receipt.Logs,
"logsBloom": receipt.Bloom, "logsBloom": receipt.Bloom,
} }
// Set block inclusion information if available.
if tib, ok := s.b.(TransactionInclusionBlock); !isPending && ok {
bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash)
if err != nil {
glog.V(logger.Debug).Infof("%v\n", err)
return nil, nil
}
fields["blockHash"] = bhash
fields["blockNumber"] = rpc.NewHexNumber(bnum)
fields["transactionIndex"] = rpc.NewHexNumber(index)
}
if receipt.Logs == nil { if receipt.Logs == nil {
fields["logs"] = []vm.Logs{} fields["logs"] = []vm.Logs{}
} }
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt.ContractAddress != (common.Address{}) { if receipt.ContractAddress != (common.Address{}) {
fields["contractAddress"] = receipt.ContractAddress fields["contractAddress"] = receipt.ContractAddress
} }
@ -1014,7 +863,7 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA
args.Gas = rpc.NewHexNumber(defaultGas) args.Gas = rpc.NewHexNumber(defaultGas)
} }
if args.GasPrice == nil { if args.GasPrice == nil {
price, err := b.SuggestPrice(ctx) price, err := b.SuggestGasPrice(ctx)
if err != nil { if err != nil {
return args, err return args, err
} }
@ -1033,7 +882,7 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si
return common.Hash{}, err return common.Hash{}, err
} }
if err := b.SendTx(ctx, signedTx); err != nil { if err := b.SendTransaction(ctx, signedTx); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1058,7 +907,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1088,7 +937,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
return "", err return "", err
} }
if err := s.b.SendTx(ctx, tx); err != nil { if err := s.b.SendTransaction(ctx, tx); err != nil {
return "", err return "", err
} }
@ -1224,7 +1073,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig
args.Gas = rpc.NewHexNumber(defaultGas) args.Gas = rpc.NewHexNumber(defaultGas)
} }
if args.GasPrice == nil { if args.GasPrice == nil {
price, err := s.b.SuggestPrice(ctx) price, err := s.b.SuggestGasPrice(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1235,7 +1084,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1265,12 +1114,12 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig
// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of // PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
// the accounts this node manages. // the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
pending := s.b.GetPoolTransactions() pending := s.b.PendingTransactions()
transactions := make([]*RPCTransaction, 0, len(pending)) transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending { for _, tx := range pending {
from, _ := tx.FromFrontier() from, _ := tx.FromFrontier()
if s.b.AccountManager().HasAddress(from) { if s.b.AccountManager().HasAddress(from) {
transactions = append(transactions, newRPCPendingTransaction(tx)) transactions = append(transactions, newRPCTransaction(tx))
} }
} }
return transactions return transactions
@ -1279,7 +1128,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
// Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the
// pool and reinsert it with the new gas price and limit. // pool and reinsert it with the new gas price and limit.
func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) {
pending := s.b.GetPoolTransactions() pending := s.b.PendingTransactions()
for _, p := range pending { for _, p := range pending {
if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() {
if gasPrice == nil { if gasPrice == nil {
@ -1301,8 +1150,8 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice,
return common.Hash{}, err return common.Hash{}, err
} }
s.b.RemoveTx(tx.Hash) s.b.RemoveTransaction(tx.Hash)
if err = s.b.SendTx(ctx, signedTx); err != nil { if err = s.b.SendTransaction(ctx, signedTx); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1326,8 +1175,8 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI {
} }
// GetBlockRlp retrieves the RLP encoded for of a single block. // GetBlockRlp retrieves the RLP encoded for of a single block.
func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number rpc.BlockNumber) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) block, _ := blockByNumber(ctx, api.b, number)
if block == nil { if block == nil {
return "", fmt.Errorf("block #%d not found", number) return "", fmt.Errorf("block #%d not found", number)
} }
@ -1339,8 +1188,8 @@ func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (stri
} }
// PrintBlock retrieves a block and returns its pretty printed form. // PrintBlock retrieves a block and returns its pretty printed form.
func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number rpc.BlockNumber) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) block, _ := blockByNumber(ctx, api.b, number)
if block == nil { if block == nil {
return "", fmt.Errorf("block #%d not found", number) return "", fmt.Errorf("block #%d not found", number)
} }
@ -1349,10 +1198,6 @@ func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (strin
// SeedHash retrieves the seed hash of a block. // SeedHash retrieves the seed hash of a block.
func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number))
if block == nil {
return "", fmt.Errorf("block #%d not found", number)
}
hash, err := ethash.GetSeedHash(number) hash, err := ethash.GetSeedHash(number)
if err != nil { if err != nil {
return "", err return "", err
@ -1360,21 +1205,22 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string,
return fmt.Sprintf("0x%x", hash), nil return fmt.Sprintf("0x%x", hash), nil
} }
// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private // PrivateDebugAPI is the collection of Ethereum APIs exposed over the private
// debugging endpoint. // debugging endpoint.
type PrivateDebugAPI struct { type PrivateDebugAPI struct {
b Backend b Backend
chaindb ethdb.Database
} }
// NewPrivateDebugAPI creates a new API definition for the private debug methods // NewPrivateDebugAPI creates a new API definition for the private debug methods
// of the Ethereum service. // of the Ethereum service.
func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { func NewPrivateDebugAPI(b Backend, chaindb ethdb.Database) *PrivateDebugAPI {
return &PrivateDebugAPI{b: b} return &PrivateDebugAPI{b, chaindb}
} }
// ChaindbProperty returns leveldb properties of the chain database. // ChaindbProperty returns leveldb properties of the chain database.
func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
ldb, ok := api.b.ChainDb().(interface { ldb, ok := api.chaindb.(interface {
LDB() *leveldb.DB LDB() *leveldb.DB
}) })
if !ok { if !ok {
@ -1389,7 +1235,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
} }
func (api *PrivateDebugAPI) ChaindbCompact() error { func (api *PrivateDebugAPI) ChaindbCompact() error {
ldb, ok := api.b.ChainDb().(interface { ldb, ok := api.chaindb.(interface {
LDB() *leveldb.DB LDB() *leveldb.DB
}) })
if !ok { if !ok {
@ -1408,7 +1254,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error {
// SetHead rewinds the head of the blockchain to a previous block. // SetHead rewinds the head of the blockchain to a previous block.
func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) {
api.b.SetHead(uint64(number.Int64())) api.b.ResetHeadBlock(number.Uint64())
} }
// PublicNetAPI offers network related RPC methods // PublicNetAPI offers network related RPC methods

View file

@ -20,52 +20,44 @@ package ethapi
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// Backend interface provides the common API services (that are provided by // Backend defines most methods required for the RPC API.
// both full and light clients) with access to necessary functions.
type Backend interface { type Backend interface {
// general Ethereum API ethereum.ChainReader
Downloader() *downloader.Downloader ethereum.ChainStateReader
ethereum.ChainSyncReader
ethereum.TransactionSender
ethereum.GasPricer
ethereum.GasEstimator
ethereum.ContractCaller
ProtocolVersion() int ProtocolVersion() int
SuggestPrice(ctx context.Context) (*big.Int, error) AccountManager() *accounts.Manager // TODO(fjl): this should be a constructor argb
ChainDb() ethdb.Database BlockTD(common.Hash) *big.Int
EventMux() *event.TypeMux RemoveTransaction(txhash common.Hash)
AccountManager() *accounts.Manager PendingTransactions() []*types.Transaction
// BlockChain API PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
SetHead(number uint64) ResetHeadBlock(number uint64) // for admin API
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *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
GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error)
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
RemoveTx(txHash common.Hash)
GetPoolTransactions() types.Transactions
GetPoolTransaction(txHash common.Hash) *types.Transaction
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int)
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
} }
type State interface { // PendingState is implemented by the eth.Ethereum backend and provides access to optional
GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) // features that can only be provided by a full pending state.
GetCode(ctx context.Context, addr common.Address) ([]byte, error) type PendingState interface {
GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) PendingBlock() (*types.Block, error)
GetNonce(ctx context.Context, addr common.Address) (uint64, error) ethereum.PendingStateReader
ethereum.PendingContractCaller
}
type TransactionInclusionBlock interface {
// returns the block at which the given transaction was included in the blockchain
TransactionInclusionBlock(txhash common.Hash) (blockhash common.Hash, blocknum uint64, index int, err error)
} }
func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { func GetAPIs(apiBackend Backend, solcPath string) []rpc.API {
@ -86,20 +78,11 @@ func GetAPIs(apiBackend Backend, solcPath string) []rpc.API {
Version: "1.0", Version: "1.0",
Service: NewPublicTransactionPoolAPI(apiBackend), Service: NewPublicTransactionPoolAPI(apiBackend),
Public: true, Public: true,
}, {
Namespace: "txpool",
Version: "1.0",
Service: NewPublicTxPoolAPI(apiBackend),
Public: true,
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPublicDebugAPI(apiBackend), Service: NewPublicDebugAPI(apiBackend),
Public: true, Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(apiBackend),
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",

105
internal/ethapi/txpool.go Normal file
View file

@ -0,0 +1,105 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package ethapi
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)
type TxPool interface {
Stats() (pending int, queued int)
Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
}
// TxPoolDebugAPI offers and API for the transaction pool. It only operates on data that
// is non confidential.
type TxPoolDebugAPI struct{ Pool TxPool }
// Content returns the transactions contained within the transaction pool.
func (s TxPoolDebugAPI) Content() map[string]map[string]map[string]*RPCTransaction {
content := map[string]map[string]map[string]*RPCTransaction{
"pending": make(map[string]map[string]*RPCTransaction),
"queued": make(map[string]map[string]*RPCTransaction),
}
pending, queue := s.Pool.Content()
// Flatten the pending transactions
for account, txs := range pending {
dump := make(map[string]*RPCTransaction)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx)
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, txs := range queue {
dump := make(map[string]*RPCTransaction)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx)
}
content["queued"][account.Hex()] = dump
}
return content
}
// Status returns the number of pending and queued transaction in the pool.
func (s TxPoolDebugAPI) Status() map[string]*rpc.HexNumber {
pending, queue := s.Pool.Stats()
return map[string]*rpc.HexNumber{
"pending": rpc.NewHexNumber(pending),
"queued": rpc.NewHexNumber(queue),
}
}
// Inspect retrieves the content of the transaction pool and flattens it into an
// easily inspectable list.
func (s TxPoolDebugAPI) Inspect() map[string]map[string]map[string]string {
content := map[string]map[string]map[string]string{
"pending": make(map[string]map[string]string),
"queued": make(map[string]map[string]string),
}
pending, queue := s.Pool.Content()
// Define a formatter to flatten a transaction into a string
var format = func(tx *types.Transaction) string {
if to := tx.To(); to != nil {
return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
}
return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice())
}
// Flatten the pending transactions
for account, txs := range pending {
dump := make(map[string]string)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = format(tx)
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, txs := range queue {
dump := make(map[string]string)
for nonce, tx := range txs {
dump[fmt.Sprintf("%d", nonce)] = format(tx)
}
content["queued"][account.Hex()] = dump
}
return content
}

View file

@ -17,128 +17,202 @@
package les package les
import ( import (
"errors"
"fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type LesApiBackend struct { var (
eth *LightEthereum ErrBlockNotFound = errors.New("block not found")
gpo *gasprice.LightPriceOracle ErrTxNotFound = errors.New("the light client cannot retrieve past transactions by hash")
} ErrReceiptNotFound = errors.New("the light client cannot retrieve receipts by hash")
)
func (b *LesApiBackend) SetHead(number uint64) { func (eth *LightEthereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) {
b.eth.blockchain.SetHead(number) if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil {
} return h, nil
func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber {
return b.eth.blockchain.CurrentHeader(), nil
} }
return nil, ErrBlockNotFound
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
} }
func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { func (eth *LightEthereum) HeaderByNumber(ctx context.Context, blocknum *big.Int) (*types.Header, error) {
header, err := b.HeaderByNumber(ctx, blockNr) if blocknum == nil {
if header == nil || err != nil { return eth.blockchain.CurrentHeader(), nil
}
h, err := eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blocknum.Uint64()))
if err != nil {
return nil, err return nil, err
} }
return b.GetBlock(ctx, header.Hash()) if h == nil {
} return nil, ErrBlockNotFound
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
header, err := b.HeaderByNumber(ctx, blockNr)
if header == nil || err != nil {
return nil, nil, err
} }
return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil return h, nil
} }
func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { func (eth *LightEthereum) BlockByNumber(ctx context.Context, blocknum *big.Int) (*types.Block, error) {
return b.eth.blockchain.GetBlockByHash(ctx, blockHash) header, err := eth.HeaderByNumber(ctx, blocknum)
}
func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash))
}
func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
stateDb := state.(*light.LightState).Copy()
addr, _ := msg.From()
from, err := stateDb.GetOrNewStateObject(ctx, addr)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
from.SetBalance(common.MaxBig) return eth.BlockByHash(ctx, header.Hash())
env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig)
return env, env.Error, nil
} }
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { func (eth *LightEthereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) {
return b.eth.txPool.Add(ctx, signedTx) return eth.blockchain.GetBlockByHash(ctx, blockhash)
} }
func (b *LesApiBackend) RemoveTx(txHash common.Hash) { func (eth *LightEthereum) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) {
b.eth.txPool.RemoveTx(txHash) return light.GetBlockReceipts(ctx, eth.odr, blockhash, blocknum)
} }
func (b *LesApiBackend) GetPoolTransactions() types.Transactions { func (eth *LightEthereum) BlockTD(blockHash common.Hash) *big.Int {
return b.eth.txPool.GetTransactions() return eth.blockchain.GetTdByHash(blockHash)
} }
func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { func (eth *LightEthereum) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) {
return b.eth.txPool.GetTransaction(txHash) if tx = eth.txPool.GetTransaction(txHash); tx != nil {
return tx, true, nil
}
return nil, false, ErrTxNotFound
} }
func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { func (eth *LightEthereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, index uint) (*types.Transaction, error) {
return b.eth.txPool.GetNonce(ctx, addr) b, err := eth.blockchain.GetBlockByHash(ctx, blockhash)
if err != nil {
return nil, err
}
if index >= uint(len(b.Transactions())) {
return nil, fmt.Errorf("transaction index out of range")
}
return b.Transactions()[index], nil
} }
func (b *LesApiBackend) Stats() (pending int, queued int) { func (eth *LightEthereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) {
return b.eth.txPool.Stats(), 0 return nil, ErrReceiptNotFound
} }
func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { func (eth *LightEthereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) {
return b.eth.txPool.Content() b, err := eth.blockchain.GetBlockByHash(ctx, blockhash)
if err != nil {
return 0, err
}
return uint(len(b.Transactions())), nil
} }
func (b *LesApiBackend) Downloader() *downloader.Downloader { func (eth *LightEthereum) BalanceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (*big.Int, error) {
return b.eth.Downloader() st, err := eth.state(ctx, blocknum)
if err != nil {
return nil, err
}
return st.GetBalance(ctx, addr)
} }
func (b *LesApiBackend) ProtocolVersion() int { func (eth *LightEthereum) CodeAt(ctx context.Context, addr common.Address, blocknum *big.Int) ([]byte, error) {
return b.eth.LesVersion() + 10000 st, err := eth.state(ctx, blocknum)
if err != nil {
return nil, err
}
return st.GetCode(ctx, addr)
} }
func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { func (eth *LightEthereum) NonceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (uint64, error) {
return b.gpo.SuggestPrice(ctx) st, err := eth.state(ctx, blocknum)
if err != nil {
return 0, err
}
return st.GetNonce(ctx, addr)
} }
func (b *LesApiBackend) ChainDb() ethdb.Database { func (eth *LightEthereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, blocknum *big.Int) ([]byte, error) {
return b.eth.chainDb st, err := eth.state(ctx, blocknum)
if err != nil {
return nil, err
}
v, err := st.GetState(ctx, addr, key)
if err != nil {
return nil, err
}
return v[:], nil
} }
func (b *LesApiBackend) EventMux() *event.TypeMux { func (eth *LightEthereum) PendingCodeAt(ctx context.Context, addr common.Address) ([]byte, error) {
return b.eth.eventMux // TODO(fjl): find a way to get rid of PendingCodeAt here. CodeAt is a bad emulation
// of PendingCodeAt because it forces users to wait for transactions to get mined.
return eth.CodeAt(ctx, addr, nil)
} }
func (b *LesApiBackend) AccountManager() *accounts.Manager { func (eth *LightEthereum) state(ctx context.Context, blocknum *big.Int) (*light.LightState, error) {
return b.eth.accountManager header, err := eth.HeaderByNumber(ctx, blocknum)
if err != nil {
return nil, err
}
return light.NewLightState(light.StateTrieID(header), eth.odr), nil
}
func (eth *LightEthereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error {
return eth.txPool.Add(ctx, signedTx)
}
func (eth *LightEthereum) RemoveTransaction(txHash common.Hash) {
eth.txPool.RemoveTx(txHash)
}
func (eth *LightEthereum) PendingTransactions() []*types.Transaction {
return eth.txPool.GetTransactions()
}
func (eth *LightEthereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) {
return eth.txPool.GetNonce(ctx, addr)
}
func (eth *LightEthereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
return eth.protocolManager.downloader.Progress(), nil
}
func (eth *LightEthereum) ProtocolVersion() int {
return int(eth.protocolManager.SubProtocols[0].Version) + 10000
}
func (eth *LightEthereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return eth.gpo.SuggestPrice(ctx)
}
func (eth *LightEthereum) AccountManager() *accounts.Manager {
return eth.accountManager
}
func (eth *LightEthereum) ResetHeadBlock(number uint64) {
eth.blockchain.SetHead(number)
}
func (eth *LightEthereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) {
_, gas, err := eth.callContract(ctx, call, nil)
return gas, err
}
func (eth *LightEthereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) {
val, _, err := eth.callContract(ctx, call, blocknum)
return val, err
}
func (eth *LightEthereum) callContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, *big.Int, error) {
var head *types.Header
if blocknum == nil {
head = eth.blockchain.CurrentHeader()
} else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil {
return nil, nil, ErrBlockNotFound
}
state := light.NewLightState(light.StateTrieID(head), eth.odr)
return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment {
return light.NewEnv(ctx, state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{})
})
} }

View file

@ -58,7 +58,7 @@ type LightEthereum struct {
// DB interfaces // DB interfaces
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
ApiBackend *LesApiBackend gpo *gasprice.LightPriceOracle
eventMux *event.TypeMux eventMux *event.TypeMux
pow *ethash.Ethash pow *ethash.Ethash
@ -124,8 +124,8 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
return nil, err return nil, err
} }
eth.ApiBackend = &LesApiBackend{eth, nil} eth.gpo = gasprice.NewLightPriceOracle(eth)
eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend)
return eth, nil return eth, nil
} }
@ -153,9 +153,13 @@ func (s *LightDummyAPI) Mining() bool {
// APIs returns the collection of RPC services the ethereum package offers. // APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else. // NOTE, some of these services probably need to be moved to somewhere else.
func (s *LightEthereum) APIs() []rpc.API { func (eth *LightEthereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{
{ {
Namespace: "debug",
Version: "1.0",
Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb),
}, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: &LightDummyAPI{}, Service: &LightDummyAPI{},
@ -163,17 +167,22 @@ func (s *LightEthereum) APIs() []rpc.API {
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: filters.NewPublicFilterAPI(s.ApiBackend, true), Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, true),
Public: true, Public: true,
}, { }, {
Namespace: "txpool",
Version: "1.0",
Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool},
},
{
Namespace: "net", Namespace: "net",
Version: "1.0", Version: "1.0",
Service: s.netRPCService, Service: eth.netRPCService,
Public: true, Public: true,
}, },
}...) }...)
@ -183,11 +192,6 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb) s.blockchain.ResetWithGenesisBlock(gb)
} }
func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain }
func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool }
func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured // Protocols implements node.Service, returning all the currently configured
// network protocols to start. // network protocols to start.
func (s *LightEthereum) Protocols() []p2p.Protocol { func (s *LightEthereum) Protocols() []p2p.Protocol {

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/les/flowcontrol"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
@ -43,8 +44,8 @@ type LesServer struct {
defParams *flowcontrol.ServerParams defParams *flowcontrol.ServerParams
} }
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { func NewLesServer(eth *eth.Ethereum, mux *event.TypeMux, config *eth.Config) (*LesServer, error) {
pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, mux, eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -321,12 +321,11 @@ func (pool *TxPool) Stop() {
} }
// Stats returns the number of currently pending (locally created) transactions // Stats returns the number of currently pending (locally created) transactions
func (pool *TxPool) Stats() (pending int) { func (pool *TxPool) Stats() (pending int, queued int) {
pool.mu.RLock() pool.mu.RLock()
defer pool.mu.RUnlock() defer pool.mu.RUnlock()
pending = len(pool.pending) return len(pool.pending), 0
return
} }
// validateTx checks whether a transaction is valid according to the consensus rules. // validateTx checks whether a transaction is valid according to the consensus rules.