Merge pull request #2159 from zsfelfoldi/light-backend

eth: separate common and full node-specific API and backend service
This commit is contained in:
Péter Szilágyi
2016-06-30 12:57:50 +03:00
committed by GitHub
22 changed files with 2296 additions and 1805 deletions

1607
eth/api.go

File diff suppressed because it is too large Load Diff

201
eth/api_backend.go Normal file
View File

@ -0,0 +1,201 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"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/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
// EthApiBackend implements ethapi.Backend for full nodes
type EthApiBackend struct {
eth *FullNodeService
gpo *gasprice.GasPriceOracle
}
func (b *EthApiBackend) SetHead(number uint64) {
b.eth.blockchain.SetHead(number)
}
func (b *EthApiBackend) HeaderByNumber(blockNr rpc.BlockNumber) *types.Header {
// Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, _ := b.eth.miner.Pending()
return block.Header()
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock().Header()
}
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr))
}
func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
// Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, _ := b.eth.miner.Pending()
return block, nil
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock(), nil
}
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}
func (b *EthApiBackend) StateAndHeaderByNumber(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
header := b.HeaderByNumber(blockNr)
if header == nil {
return nil, nil, nil
}
stateDb, err := state.New(header.Root, b.eth.chainDb)
return EthApiState{stateDb}, header, err
}
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
return b.eth.blockchain.GetBlockByHash(blockHash), nil
}
func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil
}
func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
stateDb := state.(EthApiState).state.Copy()
addr, _ := msg.From()
from := stateDb.GetOrNewStateObject(addr)
from.SetBalance(common.MaxBig)
vmError := func() error { return nil }
return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
b.eth.txPool.SetLocal(signedTx)
return b.eth.txPool.Add(signedTx)
}
func (b *EthApiBackend) RemoveTx(txHash common.Hash) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
b.eth.txPool.RemoveTx(txHash)
}
func (b *EthApiBackend) GetPoolTransactions() types.Transactions {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.GetTransactions()
}
func (b *EthApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.GetTransaction(txHash)
}
func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.State().GetNonce(addr), nil
}
func (b *EthApiBackend) Stats() (pending int, queued int) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.Stats()
}
func (b *EthApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.TxPool().Content()
}
func (b *EthApiBackend) Downloader() *downloader.Downloader {
return b.eth.Downloader()
}
func (b *EthApiBackend) ProtocolVersion() int {
return b.eth.EthVersion()
}
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(), nil
}
func (b *EthApiBackend) ChainDb() ethdb.Database {
return b.eth.ChainDb()
}
func (b *EthApiBackend) EventMux() *event.TypeMux {
return b.eth.EventMux()
}
func (b *EthApiBackend) AccountManager() *accounts.Manager {
return b.eth.AccountManager()
}
type EthApiState struct {
state *state.StateDB
}
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

@ -39,8 +39,10 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters"
"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/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/miner"
@ -101,95 +103,86 @@ type Config struct {
TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
}
type Ethereum struct {
chainConfig *core.ChainConfig
// FullNodeService implements the Ethereum full node service.
type FullNodeService struct {
chainConfig *core.ChainConfig
// Channel for shutting down the service
shutdownChan chan bool // Channel for shutting down the ethereum
stopDbUpgrade func() // stop chain db sequential key upgrade
// DB interfaces
chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database
// Handlers
txPool *core.TxPool
txMu sync.Mutex
blockchain *core.BlockChain
accountManager *accounts.Manager
pow *ethash.Ethash
protocolManager *ProtocolManager
SolcPath string
solc *compiler.Solidity
gpo *GasPriceOracle
// DB interfaces
chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database
GpoMinGasPrice *big.Int
GpoMaxGasPrice *big.Int
GpoFullBlockRatio int
GpobaseStepDown int
GpobaseStepUp int
GpobaseCorrectionFactor int
eventMux *event.TypeMux
pow *ethash.Ethash
httpclient *httpclient.HTTPClient
accountManager *accounts.Manager
httpclient *httpclient.HTTPClient
apiBackend *EthApiBackend
eventMux *event.TypeMux
miner *miner.Miner
miner *miner.Miner
Mining bool
MinerThreads int
AutoDAG bool
autodagquit chan bool
etherbase common.Address
solcPath string
solc *compiler.Solidity
Mining bool
MinerThreads int
NatSpec bool
AutoDAG bool
PowTest bool
autodagquit chan bool
etherbase common.Address
netVersionId int
netRPCService *PublicNetAPI
netRPCService *ethapi.PublicNetAPI
}
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
// Open the chain database and perform any upgrades needed
chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles)
// New creates a new FullNodeService object (including the
// initialisation of the common Ethereum object)
func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) {
chainDb, dappDb, err := CreateDBs(ctx, config)
if err != nil {
return nil, err
}
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
db.Meter("eth/db/chaindata/")
stopDbUpgrade := upgradeSequentialKeys(chainDb)
if err := SetupGenesisBlock(&chainDb, config); err != nil {
return nil, err
}
pow, err := CreatePoW(config)
if err != nil {
return nil, err
}
eth := &FullNodeService{
chainDb: chainDb,
dappDb: dappDb,
eventMux: ctx.EventMux,
accountManager: config.AccountManager,
pow: pow,
shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade,
httpclient: httpclient.New(config.DocRoot),
netVersionId: config.NetworkId,
NatSpec: config.NatSpec,
PowTest: config.PowTest,
etherbase: config.Etherbase,
MinerThreads: config.MinerThreads,
AutoDAG: config.AutoDAG,
solcPath: config.SolcPath,
}
if err := upgradeChainDatabase(chainDb); err != nil {
return nil, err
}
if err := addMipmapBloomBins(chainDb); err != nil {
return nil, err
}
stopDbUpgrade := upgradeSequentialKeys(chainDb)
dappDb, err := ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles)
if err != nil {
return nil, err
}
if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
db.Meter("eth/db/dapp/")
}
glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
// Load up any custom genesis block if requested
if len(config.Genesis) > 0 {
block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis))
if err != nil {
return nil, err
}
glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash())
}
// Load up a test setup if directly injected
if config.TestGenesisState != nil {
chainDb = config.TestGenesisState
}
if config.TestGenesisBlock != nil {
core.WriteTd(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty())
core.WriteBlock(chainDb, config.TestGenesisBlock)
core.WriteCanonicalHash(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64())
core.WriteHeadBlockHash(chainDb, config.TestGenesisBlock.Hash())
}
if !config.SkipBcVersionCheck {
bcVersion := core.GetBlockChainVersion(chainDb)
if bcVersion != config.BlockChainVersion && bcVersion != 0 {
@ -197,44 +190,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
core.WriteBlockChainVersion(chainDb, config.BlockChainVersion)
}
glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion)
eth := &Ethereum{
shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade,
chainDb: chainDb,
dappDb: dappDb,
eventMux: ctx.EventMux,
accountManager: config.AccountManager,
etherbase: config.Etherbase,
netVersionId: config.NetworkId,
NatSpec: config.NatSpec,
MinerThreads: config.MinerThreads,
SolcPath: config.SolcPath,
AutoDAG: config.AutoDAG,
PowTest: config.PowTest,
GpoMinGasPrice: config.GpoMinGasPrice,
GpoMaxGasPrice: config.GpoMaxGasPrice,
GpoFullBlockRatio: config.GpoFullBlockRatio,
GpobaseStepDown: config.GpobaseStepDown,
GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
httpclient: httpclient.New(config.DocRoot),
}
switch {
case config.PowTest:
glog.V(logger.Info).Infof("ethash used in test mode")
eth.pow, err = ethash.NewForTesting()
if err != nil {
return nil, err
}
case config.PowShared:
glog.V(logger.Info).Infof("ethash used in shared mode")
eth.pow = ethash.NewShared()
default:
eth.pow = ethash.New()
}
// load the genesis block or write a new one if no genesis
// block is prenent in the database.
@ -263,8 +218,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
return nil, err
}
eth.gpo = NewGasPriceOracle(eth)
newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool
@ -275,37 +228,87 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData)
gpoParams := &gasprice.GpoParams{
GpoMinGasPrice: config.GpoMinGasPrice,
GpoMaxGasPrice: config.GpoMaxGasPrice,
GpoFullBlockRatio: config.GpoFullBlockRatio,
GpobaseStepDown: config.GpobaseStepDown,
GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
}
gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
eth.apiBackend = &EthApiBackend{eth, gpo}
return eth, nil
}
// CreateDBs creates the chain and dapp databases for an Ethereum service
func CreateDBs(ctx *node.ServiceContext, config *Config) (chainDb, dappDb ethdb.Database, err error) {
// Open the chain database and perform any upgrades needed
chainDb, err = ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles)
if err != nil {
return nil, nil, err
}
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
db.Meter("eth/db/chaindata/")
}
dappDb, err = ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles)
if err != nil {
return nil, nil, err
}
if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
db.Meter("eth/db/dapp/")
}
return
}
// SetupGenesisBlock initializes the genesis block for an Ethereum service
func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error {
// Load up any custom genesis block if requested
if len(config.Genesis) > 0 {
block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis))
if err != nil {
return err
}
glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash())
}
// Load up a test setup if directly injected
if config.TestGenesisState != nil {
*chainDb = config.TestGenesisState
}
if config.TestGenesisBlock != nil {
core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty())
core.WriteBlock(*chainDb, config.TestGenesisBlock)
core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64())
core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash())
}
return nil
}
// CreatePoW creates the required type of PoW instance for an Ethereum service
func CreatePoW(config *Config) (*ethash.Ethash, error) {
switch {
case config.PowTest:
glog.V(logger.Info).Infof("ethash used in test mode")
return ethash.NewForTesting()
case config.PowShared:
glog.V(logger.Info).Infof("ethash used in shared mode")
return ethash.NewShared(), nil
default:
return ethash.New(), nil
}
}
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
return []rpc.API{
func (s *FullNodeService) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.apiBackend, &s.solcPath, &s.solc), []rpc.API{
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicAccountAPI(s.accountManager),
Public: true,
}, {
Namespace: "personal",
Version: "1.0",
Service: NewPrivateAccountAPI(s),
Public: false,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicBlockChainAPI(s.chainConfig, s.blockchain, s.miner, s.chainDb, s.gpo, s.eventMux, s.accountManager),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicTransactionPoolAPI(s),
Service: NewPublicFullEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
@ -322,11 +325,6 @@ func (s *Ethereum) APIs() []rpc.API {
Version: "1.0",
Service: NewPrivateMinerAPI(s),
Public: false,
}, {
Namespace: "txpool",
Version: "1.0",
Service: NewPublicTxPoolAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
@ -335,16 +333,16 @@ func (s *Ethereum) APIs() []rpc.API {
}, {
Namespace: "admin",
Version: "1.0",
Service: NewPrivateAdminAPI(s),
Service: NewPrivateFullAdminAPI(s),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPublicDebugAPI(s),
Service: NewPublicFullDebugAPI(s),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(s.chainConfig, s),
Service: NewPrivateFullDebugAPI(s.chainConfig, s),
}, {
Namespace: "net",
Version: "1.0",
@ -355,14 +353,14 @@ func (s *Ethereum) APIs() []rpc.API {
Version: "1.0",
Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
},
}
}...)
}
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
func (s *FullNodeService) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb)
}
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
func (s *FullNodeService) Etherbase() (eb common.Address, err error) {
eb = s.etherbase
if (eb == common.Address{}) {
firstAccount, err := s.AccountManager().AccountByIndex(0)
@ -375,46 +373,47 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
}
// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
func (self *FullNodeService) SetEtherbase(etherbase common.Address) {
self.etherbase = etherbase
self.miner.SetEtherbase(etherbase)
}
func (s *Ethereum) StopMining() { s.miner.Stop() }
func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *FullNodeService) StopMining() { s.miner.Stop() }
func (s *FullNodeService) IsMining() bool { return s.miner.Mining() }
func (s *FullNodeService) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
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) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *Ethereum) NetVersion() int { return s.netVersionId }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
func (s *FullNodeService) AccountManager() *accounts.Manager { return s.accountManager }
func (s *FullNodeService) BlockChain() *core.BlockChain { return s.blockchain }
func (s *FullNodeService) TxPool() *core.TxPool { return s.txPool }
func (s *FullNodeService) EventMux() *event.TypeMux { return s.eventMux }
func (s *FullNodeService) Pow() *ethash.Ethash { return s.pow }
func (s *FullNodeService) ChainDb() ethdb.Database { return s.chainDb }
func (s *FullNodeService) DappDb() ethdb.Database { return s.dappDb }
func (s *FullNodeService) IsListening() bool { return true } // Always listening
func (s *FullNodeService) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *FullNodeService) NetVersion() int { return s.netVersionId }
func (s *FullNodeService) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured
// network protocols to start.
func (s *Ethereum) Protocols() []p2p.Protocol {
func (s *FullNodeService) Protocols() []p2p.Protocol {
return s.protocolManager.SubProtocols
}
// Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation.
func (s *Ethereum) Start(srvr *p2p.Server) error {
// FullNodeService protocol implementation.
func (s *FullNodeService) Start(srvr *p2p.Server) error {
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
if s.AutoDAG {
s.StartAutoDAG()
}
s.protocolManager.Start()
s.netRPCService = NewPublicNetAPI(srvr, s.NetVersion())
return nil
}
// Stop implements node.Service, terminating all internal goroutines used by the
// Ethereum protocol.
func (s *Ethereum) Stop() error {
func (s *FullNodeService) Stop() error {
if s.stopDbUpgrade != nil {
s.stopDbUpgrade()
}
@ -434,7 +433,7 @@ func (s *Ethereum) Stop() error {
}
// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
func (s *FullNodeService) WaitForShutdown() {
<-s.shutdownChan
}
@ -447,7 +446,7 @@ func (s *Ethereum) WaitForShutdown() {
// stop any number of times.
// For any more sophisticated pattern of DAG generation, use CLI subcommand
// makedag
func (self *Ethereum) StartAutoDAG() {
func (self *FullNodeService) StartAutoDAG() {
if self.autodagquit != nil {
return // already started
}
@ -493,7 +492,7 @@ func (self *Ethereum) StartAutoDAG() {
}
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
func (self *Ethereum) StopAutoDAG() {
func (self *FullNodeService) StopAutoDAG() {
if self.autodagquit != nil {
close(self.autodagquit)
self.autodagquit = nil
@ -503,25 +502,10 @@ func (self *Ethereum) StopAutoDAG() {
// HTTPClient returns the light http client used for fetching offchain docs
// (natspec, source for verification)
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
func (self *FullNodeService) HTTPClient() *httpclient.HTTPClient {
return self.httpclient
}
func (self *Ethereum) Solc() (*compiler.Solidity, error) {
var err error
if self.solc == nil {
self.solc, err = compiler.New(self.SolcPath)
}
return self.solc, err
}
// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) {
self.SolcPath = solcPath
self.solc = nil
return self.Solc()
}
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
func dagFiles(epoch uint64) (string, string) {

View File

@ -21,8 +21,10 @@ import (
"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
@ -33,38 +35,44 @@ import (
// 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 *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
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(eth *Ethereum) *ContractBackend {
func NewContractBackend(eth *FullNodeService) *ContractBackend {
return &ContractBackend{
eapi: NewPublicEthereumAPI(eth),
bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager),
txapi: NewPublicTransactionPoolAPI(eth),
eapi: ethapi.NewPublicEthereumAPI(eth.apiBackend, nil, nil),
bcapi: ethapi.NewPublicBlockChainAPI(eth.apiBackend),
txapi: ethapi.NewPublicTransactionPoolAPI(eth.apiBackend),
}
}
// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated
// with the contract from the local API, and checking its size.
func (b *ContractBackend) HasCode(contract common.Address, pending bool) (bool, error) {
func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
if ctx == nil {
ctx = context.Background()
}
block := rpc.LatestBlockNumber
if pending {
block = rpc.PendingBlockNumber
}
out, err := b.bcapi.GetCode(contract, block)
out, err := b.bcapi.GetCode(ctx, contract, block)
return len(common.FromHex(out)) > 0, 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) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
if ctx == nil {
ctx = context.Background()
}
// Convert the input args to the API spec
args := CallArgs{
args := ethapi.CallArgs{
To: &contract,
Data: common.ToHex(data),
}
@ -73,21 +81,27 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen
block = rpc.PendingBlockNumber
}
// Execute the call and convert the output back to Go types
out, err := b.bcapi.Call(args, block)
out, err := b.bcapi.Call(ctx, args, block)
return common.FromHex(out), err
}
// PendingAccountNonce implements bind.ContractTransactor retrieving the current
// pending nonce associated with an account.
func (b *ContractBackend) PendingAccountNonce(account common.Address) (uint64, error) {
out, err := b.txapi.GetTransactionCount(account, rpc.PendingBlockNumber)
func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
if ctx == nil {
ctx = context.Background()
}
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() (*big.Int, error) {
return b.eapi.GasPrice(), nil
func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
if ctx == nil {
ctx = context.Background()
}
return b.eapi.GasPrice(ctx)
}
// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas
@ -95,8 +109,11 @@ func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) {
// 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) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
out, err := b.bcapi.EstimateGas(CallArgs{
func (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
if ctx == nil {
ctx = context.Background()
}
out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{
From: sender,
To: contract,
Value: *rpc.NewHexNumber(value),
@ -107,8 +124,11 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm
// SendTransaction implements bind.ContractTransactor injects the transaction
// into the pending pool for execution.
func (b *ContractBackend) SendTransaction(tx *types.Transaction) error {
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
if ctx == nil {
ctx = context.Background()
}
raw, _ := rlp.EncodeToBytes(tx)
_, err := b.txapi.SendRawTransaction(common.ToHex(raw))
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
return err
}

View File

@ -28,7 +28,7 @@ import (
const disabledInfo = "Set GO_OPENCL and re-build to enable."
func (s *Ethereum) StartMining(threads int, gpus string) error {
func (s *FullNodeService) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase()
if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)

View File

@ -14,7 +14,7 @@
// 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
package gasprice
import (
"math/big"
@ -23,6 +23,8 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
@ -39,10 +41,22 @@ type blockPriceInfo struct {
baseGasPrice *big.Int
}
type GpoParams struct {
GpoMinGasPrice *big.Int
GpoMaxGasPrice *big.Int
GpoFullBlockRatio int
GpobaseStepDown int
GpobaseStepUp int
GpobaseCorrectionFactor int
}
// GasPriceOracle recommends gas prices based on the content of recent
// blocks.
type GasPriceOracle struct {
eth *Ethereum
chain *core.BlockChain
db ethdb.Database
evmux *event.TypeMux
params *GpoParams
initOnce sync.Once
minPrice *big.Int
lastBaseMutex sync.Mutex
@ -55,17 +69,20 @@ type GasPriceOracle struct {
}
// NewGasPriceOracle returns a new oracle.
func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle {
minprice := eth.GpoMinGasPrice
func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle {
minprice := params.GpoMinGasPrice
if minprice == nil {
minprice = big.NewInt(gpoDefaultMinGasPrice)
}
minbase := new(big.Int).Mul(minprice, big.NewInt(100))
if eth.GpobaseCorrectionFactor > 0 {
minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor)))
if params.GpobaseCorrectionFactor > 0 {
minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor)))
}
return &GasPriceOracle{
eth: eth,
chain: chain,
db: db,
evmux: evmux,
params: params,
blocks: make(map[uint64]*blockPriceInfo),
minBase: minbase,
minPrice: minprice,
@ -75,14 +92,14 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle {
func (gpo *GasPriceOracle) init() {
gpo.initOnce.Do(func() {
gpo.processPastBlocks(gpo.eth.BlockChain())
gpo.processPastBlocks()
go gpo.listenLoop()
})
}
func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
func (self *GasPriceOracle) processPastBlocks() {
last := int64(-1)
cblock := chain.CurrentBlock()
cblock := self.chain.CurrentBlock()
if cblock != nil {
last = int64(cblock.NumberU64())
}
@ -92,7 +109,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
}
self.firstProcessed = uint64(first)
for i := first; i <= last; i++ {
block := chain.GetBlockByNumber(uint64(i))
block := self.chain.GetBlockByNumber(uint64(i))
if block != nil {
self.processBlock(block)
}
@ -101,7 +118,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
}
func (self *GasPriceOracle) listenLoop() {
events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{})
events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{})
defer events.Unsubscribe()
for event := range events.Chan() {
@ -136,9 +153,9 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
}
if lastBase.Cmp(lp) < 0 {
corr = self.eth.GpobaseStepUp
corr = self.params.GpobaseStepUp
} else {
corr = -self.eth.GpobaseStepDown
corr = -self.params.GpobaseStepDown
}
crand := int64(corr * (900 + rand.Intn(201)))
@ -159,14 +176,14 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
self.lastBase = newBase
self.lastBaseMutex.Unlock()
glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", block.NumberU64(), newBase.Int64())
glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", i, newBase.Int64())
}
// returns the lowers possible price with which a tx was or could have been included
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
gasUsed := big.NewInt(0)
receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash(), block.NumberU64())
receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64())
if len(receipts) > 0 {
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
@ -174,7 +191,7 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
}
if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 {
big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 {
// block is not full, could have posted a tx with MinGasPrice
return big.NewInt(0)
}
@ -201,12 +218,12 @@ func (self *GasPriceOracle) SuggestPrice() *big.Int {
price := new(big.Int).Set(self.lastBase)
self.lastBaseMutex.Unlock()
price.Mul(price, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor)))
price.Div(price, big.NewInt(100))
if price.Cmp(self.minPrice) < 0 {
price.Set(self.minPrice)
} else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 {
price.Set(self.eth.GpoMaxGasPrice)
} else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 {
price.Set(self.params.GpoMaxGasPrice)
}
return price
}

View File

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/miner"
)
func (s *Ethereum) StartMining(threads int, gpus string) error {
func (s *FullNodeService) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase()
if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)