Merge branch 'develop' of github.com:ethereum/go-ethereum into develop

Conflicts:
	rpc/jeth.go
This commit is contained in:
Daniel A. Nagy 2015-05-11 12:47:14 +02:00
commit a9e1d38612
22 changed files with 372 additions and 103 deletions

View File

@ -70,6 +70,7 @@ func (js *jsre) adminBindings() {
miner.Set("stop", js.stopMining) miner.Set("stop", js.stopMining)
miner.Set("hashrate", js.hashrate) miner.Set("hashrate", js.hashrate)
miner.Set("setExtra", js.setExtra) miner.Set("setExtra", js.setExtra)
miner.Set("setGasPrice", js.setGasPrice)
admin.Set("debug", struct{}{}) admin.Set("debug", struct{}{})
t, _ = admin.Get("debug") t, _ = admin.Get("debug")
@ -236,6 +237,17 @@ func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value {
gasPrice, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
js.ethereum.Miner().SetGasPrice(common.String2Big(gasPrice))
return otto.UndefinedValue()
}
func (js *jsre) hashrate(otto.FunctionCall) otto.Value { func (js *jsre) hashrate(otto.FunctionCall) otto.Value {
return js.re.ToVal(js.ethereum.Miner().HashRate()) return js.re.ToVal(js.ethereum.Miner().HashRate())
} }

View File

@ -51,7 +51,7 @@ import _ "net/http/pprof"
const ( const (
ClientIdentifier = "Geth" ClientIdentifier = "Geth"
Version = "0.9.17" Version = "0.9.19"
) )
var ( var (
@ -244,6 +244,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.MaxPeersFlag, utils.MaxPeersFlag,
utils.MaxPendingPeersFlag, utils.MaxPendingPeersFlag,
utils.EtherbaseFlag, utils.EtherbaseFlag,
utils.GasPriceFlag,
utils.MinerThreadsFlag, utils.MinerThreadsFlag,
utils.MiningEnabledFlag, utils.MiningEnabledFlag,
utils.NATFlag, utils.NATFlag,
@ -258,7 +259,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.ProtocolVersionFlag, utils.ProtocolVersionFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.LogLevelFlag, utils.VerbosityFlag,
utils.BacktraceAtFlag, utils.BacktraceAtFlag,
utils.LogToStdErrFlag, utils.LogToStdErrFlag,
utils.LogVModuleFlag, utils.LogVModuleFlag,

View File

@ -37,7 +37,7 @@ import (
const ( const (
ClientIdentifier = "Mist" ClientIdentifier = "Mist"
Version = "0.9.0" Version = "0.9.19"
) )
var ( var (
@ -73,7 +73,7 @@ func init() {
utils.DataDirFlag, utils.DataDirFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.LogFileFlag, utils.LogFileFlag,
utils.LogLevelFlag, utils.VerbosityFlag,
utils.MaxPeersFlag, utils.MaxPeersFlag,
utils.MaxPendingPeersFlag, utils.MaxPendingPeersFlag,
utils.MinerThreadsFlag, utils.MinerThreadsFlag,

View File

@ -4,6 +4,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"log" "log"
"math/big"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -116,6 +117,11 @@ var (
Usage: "Public address for block mining rewards. By default the address of your primary account is used", Usage: "Public address for block mining rewards. By default the address of your primary account is used",
Value: "primary", Value: "primary",
} }
GasPriceFlag = cli.StringFlag{
Name: "gasprice",
Usage: "Sets the minimal gasprice when mining transactions",
Value: new(big.Int).Mul(big.NewInt(10), common.Szabo).String(),
}
UnlockedAccountFlag = cli.StringFlag{ UnlockedAccountFlag = cli.StringFlag{
Name: "unlock", Name: "unlock",
@ -133,8 +139,8 @@ var (
Name: "logfile", Name: "logfile",
Usage: "Send log output to a file", Usage: "Send log output to a file",
} }
LogLevelFlag = cli.IntFlag{ VerbosityFlag = cli.IntFlag{
Name: "loglevel", Name: "verbosity",
Usage: "Logging verbosity: 0-6 (0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=debug detail)", Usage: "Logging verbosity: 0-6 (0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=debug detail)",
Value: int(logger.InfoLevel), Value: int(logger.InfoLevel),
} }
@ -270,7 +276,7 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
// Set verbosity on glog // Set verbosity on glog
glog.SetV(ctx.GlobalInt(LogLevelFlag.Name)) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
// Set the log type // Set the log type
//glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name)) //glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name))
glog.SetToStderr(true) glog.SetToStderr(true)
@ -290,7 +296,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
SkipBcVersionCheck: false, SkipBcVersionCheck: false,
NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), NetworkId: ctx.GlobalInt(NetworkIdFlag.Name),
LogFile: ctx.GlobalString(LogFileFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name),
LogLevel: ctx.GlobalInt(LogLevelFlag.Name), Verbosity: ctx.GlobalInt(VerbosityFlag.Name),
LogJSON: ctx.GlobalString(LogJSONFlag.Name), LogJSON: ctx.GlobalString(LogJSONFlag.Name),
Etherbase: ctx.GlobalString(EtherbaseFlag.Name), Etherbase: ctx.GlobalString(EtherbaseFlag.Name),
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
@ -305,6 +311,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
Dial: true, Dial: true,
BootNodes: ctx.GlobalString(BootnodesFlag.Name), BootNodes: ctx.GlobalString(BootnodesFlag.Name),
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
} }
} }

View File

@ -44,12 +44,6 @@ func CurrencyToString(num *big.Int) string {
) )
switch { switch {
case num.Cmp(Douglas) >= 0:
fin = new(big.Int).Div(num, Douglas)
denom = "Douglas"
case num.Cmp(Einstein) >= 0:
fin = new(big.Int).Div(num, Einstein)
denom = "Einstein"
case num.Cmp(Ether) >= 0: case num.Cmp(Ether) >= 0:
fin = new(big.Int).Div(num, Ether) fin = new(big.Int).Div(num, Ether)
denom = "Ether" denom = "Ether"

View File

@ -25,8 +25,6 @@ func (s *SizeSuite) TestStorageSizeString(c *checker.C) {
} }
func (s *CommonSuite) TestCommon(c *checker.C) { func (s *CommonSuite) TestCommon(c *checker.C) {
douglas := CurrencyToString(BigPow(10, 43))
einstein := CurrencyToString(BigPow(10, 22))
ether := CurrencyToString(BigPow(10, 19)) ether := CurrencyToString(BigPow(10, 19))
finney := CurrencyToString(BigPow(10, 16)) finney := CurrencyToString(BigPow(10, 16))
szabo := CurrencyToString(BigPow(10, 13)) szabo := CurrencyToString(BigPow(10, 13))
@ -35,8 +33,6 @@ func (s *CommonSuite) TestCommon(c *checker.C) {
ada := CurrencyToString(BigPow(10, 4)) ada := CurrencyToString(BigPow(10, 4))
wei := CurrencyToString(big.NewInt(10)) wei := CurrencyToString(big.NewInt(10))
c.Assert(douglas, checker.Equals, "10 Douglas")
c.Assert(einstein, checker.Equals, "10 Einstein")
c.Assert(ether, checker.Equals, "10 Ether") c.Assert(ether, checker.Equals, "10 Ether")
c.Assert(finney, checker.Equals, "10 Finney") c.Assert(finney, checker.Equals, "10 Finney")
c.Assert(szabo, checker.Equals, "10 Szabo") c.Assert(szabo, checker.Equals, "10 Szabo")
@ -45,13 +41,3 @@ func (s *CommonSuite) TestCommon(c *checker.C) {
c.Assert(ada, checker.Equals, "10 Ada") c.Assert(ada, checker.Equals, "10 Ada")
c.Assert(wei, checker.Equals, "10 Wei") c.Assert(wei, checker.Equals, "10 Wei")
} }
func (s *CommonSuite) TestLarge(c *checker.C) {
douglaslarge := CurrencyToString(BigPow(100000000, 43))
adalarge := CurrencyToString(BigPow(100000000, 4))
weilarge := CurrencyToString(big.NewInt(100000000))
c.Assert(douglaslarge, checker.Equals, "10000E298 Douglas")
c.Assert(adalarge, checker.Equals, "10000E7 Einstein")
c.Assert(weilarge, checker.Equals, "100 Babbage")
}

View File

@ -1,8 +1,10 @@
package core package core
import ( import (
"github.com/ethereum/go-ethereum/core/types" "math/big"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
) )
// TxPreEvent is posted when a transaction enters the transaction pool. // TxPreEvent is posted when a transaction enters the transaction pool.
@ -44,6 +46,8 @@ type ChainUncleEvent struct {
type ChainHeadEvent struct{ Block *types.Block } type ChainHeadEvent struct{ Block *types.Block }
type GasPriceChanged struct{ Price *big.Int }
// Mining operation events // Mining operation events
type StartMining struct{} type StartMining struct{}
type TopMining struct{} type TopMining struct{}

View File

@ -1,12 +1,14 @@
package core package core
import ( import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
) )
type Backend interface { type Backend interface {
AccountManager() *accounts.Manager
BlockProcessor() *BlockProcessor BlockProcessor() *BlockProcessor
ChainManager() *ChainManager ChainManager() *ChainManager
TxPool() *TxPool TxPool() *TxPool

View File

@ -21,7 +21,7 @@ var (
ErrInvalidSender = errors.New("Invalid sender") ErrInvalidSender = errors.New("Invalid sender")
ErrNonce = errors.New("Nonce too low") ErrNonce = errors.New("Nonce too low")
ErrBalance = errors.New("Insufficient balance") ErrBalance = errors.New("Insufficient balance")
ErrNonExistentAccount = errors.New("Account does not exist") ErrNonExistentAccount = errors.New("Account does not exist or account balance too low")
ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value") ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value")
ErrIntrinsicGas = errors.New("Intrinsic gas too low") ErrIntrinsicGas = errors.New("Intrinsic gas too low")
ErrGasLimit = errors.New("Exceeds block gas limit") ErrGasLimit = errors.New("Exceeds block gas limit")

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -55,7 +56,7 @@ type Config struct {
DataDir string DataDir string
LogFile string LogFile string
LogLevel int Verbosity int
LogJSON string LogJSON string
VmDebug bool VmDebug bool
NatSpec bool NatSpec bool
@ -76,6 +77,7 @@ type Config struct {
Dial bool Dial bool
Etherbase string Etherbase string
GasPrice *big.Int
MinerThreads int MinerThreads int
AccountManager *accounts.Manager AccountManager *accounts.Manager
@ -200,7 +202,7 @@ type Ethereum struct {
func New(config *Config) (*Ethereum, error) { func New(config *Config) (*Ethereum, error) {
// Bootstrap database // Bootstrap database
logger.New(config.DataDir, config.LogFile, config.LogLevel) logger.New(config.DataDir, config.LogFile, config.Verbosity)
if len(config.LogJSON) > 0 { if len(config.LogJSON) > 0 {
logger.NewJSONsystem(config.DataDir, config.LogJSON) logger.NewJSONsystem(config.DataDir, config.LogJSON)
} }
@ -266,6 +268,8 @@ func New(config *Config) (*Ethereum, error) {
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor) eth.chainManager.SetProcessor(eth.blockProcessor)
eth.miner = miner.New(eth, eth.pow, config.MinerThreads) eth.miner = miner.New(eth, eth.pow, config.MinerThreads)
eth.miner.SetGasPrice(config.GasPrice)
eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.chainManager, eth.downloader) eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.chainManager, eth.downloader)
if config.Shh { if config.Shh {
eth.whisper = whisper.New() eth.whisper = whisper.New()
@ -447,6 +451,8 @@ func (s *Ethereum) Start() error {
return nil return nil
} }
// sync databases every minute. If flushing fails we exit immediatly. The system
// may not continue under any circumstances.
func (s *Ethereum) syncDatabases() { func (s *Ethereum) syncDatabases() {
ticker := time.NewTicker(1 * time.Minute) ticker := time.NewTicker(1 * time.Minute)
done: done:
@ -455,13 +461,13 @@ done:
case <-ticker.C: case <-ticker.C:
// don't change the order of database flushes // don't change the order of database flushes
if err := s.extraDb.Flush(); err != nil { if err := s.extraDb.Flush(); err != nil {
glog.V(logger.Error).Infof("error: flush extraDb: %v\n", err) glog.Fatalf("fatal error: flush extraDb: %v\n", err)
} }
if err := s.stateDb.Flush(); err != nil { if err := s.stateDb.Flush(); err != nil {
glog.V(logger.Error).Infof("error: flush stateDb: %v\n", err) glog.Fatalf("fatal error: flush stateDb: %v\n", err)
} }
if err := s.blockDb.Flush(); err != nil { if err := s.blockDb.Flush(); err != nil {
glog.V(logger.Error).Infof("error: flush blockDb: %v\n", err) glog.Fatalf("fatal error: flush blockDb: %v\n", err)
} }
case <-s.shutdownChan: case <-s.shutdownChan:
break done break done

View File

@ -34,6 +34,9 @@ var (
errPeersUnavailable = errors.New("no peers available or all peers tried for block download process") errPeersUnavailable = errors.New("no peers available or all peers tried for block download process")
errAlreadyInPool = errors.New("hash already in pool") errAlreadyInPool = errors.New("hash already in pool")
errBlockNumberOverflow = errors.New("received block which overflows") errBlockNumberOverflow = errors.New("received block which overflows")
errCancelHashFetch = errors.New("hash fetching cancelled (requested)")
errCancelBlockFetch = errors.New("block downloading cancelled (requested)")
errNoSyncActive = errors.New("no sync active")
) )
type hashCheckFn func(common.Hash) bool type hashCheckFn func(common.Hash) bool
@ -74,6 +77,7 @@ type Downloader struct {
newPeerCh chan *peer newPeerCh chan *peer
hashCh chan hashPack hashCh chan hashPack
blockCh chan blockPack blockCh chan blockPack
cancelCh chan struct{}
} }
func New(hasBlock hashCheckFn, getBlock getBlockFn) *Downloader { func New(hasBlock hashCheckFn, getBlock getBlockFn) *Downloader {
@ -129,6 +133,9 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
} }
defer atomic.StoreInt32(&d.synchronising, 0) defer atomic.StoreInt32(&d.synchronising, 0)
// Create cancel channel for aborting midflight
d.cancelCh = make(chan struct{})
// Abort if the queue still contains some leftover data // Abort if the queue still contains some leftover data
if _, cached := d.queue.Size(); cached > 0 && d.queue.GetHeadBlock() != nil { if _, cached := d.queue.Size(); cached > 0 && d.queue.GetHeadBlock() != nil {
return errPendingQueue return errPendingQueue
@ -161,7 +168,6 @@ func (d *Downloader) Has(hash common.Hash) bool {
} }
func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool) (err error) { func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool) (err error) {
d.activePeer = p.id d.activePeer = p.id
defer func() { defer func() {
// reset on error // reset on error
@ -191,6 +197,42 @@ func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool)
return nil return nil
} }
// Cancel cancels all of the operations and resets the queue. It returns true
// if the cancel operation was completed.
func (d *Downloader) Cancel() bool {
hs, bs := d.queue.Size()
// If we're not syncing just return.
if atomic.LoadInt32(&d.synchronising) == 0 && hs == 0 && bs == 0 {
return false
}
close(d.cancelCh)
// clean up
hashDone:
for {
select {
case <-d.hashCh:
default:
break hashDone
}
}
blockDone:
for {
select {
case <-d.blockCh:
default:
break blockDone
}
}
// reset the queue
d.queue.Reset()
return true
}
// XXX Make synchronous // XXX Make synchronous
func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial bool) error { func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial bool) error {
glog.V(logger.Debug).Infof("Downloading hashes (%x) from %s", h[:4], p.id) glog.V(logger.Debug).Infof("Downloading hashes (%x) from %s", h[:4], p.id)
@ -217,6 +259,8 @@ func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial b
out: out:
for { for {
select { select {
case <-d.cancelCh:
return errCancelHashFetch
case hashPack := <-d.hashCh: case hashPack := <-d.hashCh:
// Make sure the active peer is giving us the hashes // Make sure the active peer is giving us the hashes
if hashPack.peerId != activePeer.id { if hashPack.peerId != activePeer.id {
@ -305,6 +349,8 @@ func (d *Downloader) startFetchingBlocks(p *peer) error {
out: out:
for { for {
select { select {
case <-d.cancelCh:
return errCancelBlockFetch
case blockPack := <-d.blockCh: case blockPack := <-d.blockCh:
// If the peer was previously banned and failed to deliver it's pack // If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message. // in a reasonable time frame, ignore it's message.
@ -394,11 +440,23 @@ out:
// Deliver a chunk to the downloader. This is usually done through the BlocksMsg by // Deliver a chunk to the downloader. This is usually done through the BlocksMsg by
// the protocol handler. // the protocol handler.
func (d *Downloader) DeliverChunk(id string, blocks []*types.Block) { func (d *Downloader) DeliverChunk(id string, blocks []*types.Block) error {
// Make sure the downloader is active
if atomic.LoadInt32(&d.synchronising) == 0 {
return errNoSyncActive
}
d.blockCh <- blockPack{id, blocks} d.blockCh <- blockPack{id, blocks}
return nil
} }
func (d *Downloader) AddHashes(id string, hashes []common.Hash) error { func (d *Downloader) AddHashes(id string, hashes []common.Hash) error {
// Make sure the downloader is active
if atomic.LoadInt32(&d.synchronising) == 0 {
return errNoSyncActive
}
// make sure that the hashes that are being added are actually from the peer // make sure that the hashes that are being added are actually from the peer
// that's the current active peer. hashes that have been received from other // that's the current active peer. hashes that have been received from other
// peers are dropped and ignored. // peers are dropped and ignored.

View File

@ -182,6 +182,49 @@ func TestTaking(t *testing.T) {
} }
} }
func TestInactiveDownloader(t *testing.T) {
targetBlocks := 1000
hashes := createHashes(0, targetBlocks)
blocks := createBlocksFromHashSet(createHashSet(hashes))
tester := newTester(t, hashes, nil)
err := tester.downloader.AddHashes("bad peer 001", hashes)
if err != errNoSyncActive {
t.Error("expected no sync error, got", err)
}
err = tester.downloader.DeliverChunk("bad peer 001", blocks)
if err != errNoSyncActive {
t.Error("expected no sync error, got", err)
}
}
func TestCancel(t *testing.T) {
minDesiredPeerCount = 4
blockTtl = 1 * time.Second
targetBlocks := 1000
hashes := createHashes(0, targetBlocks)
blocks := createBlocksFromHashes(hashes)
tester := newTester(t, hashes, blocks)
tester.newPeer("peer1", big.NewInt(10000), hashes[0])
err := tester.sync("peer1", hashes[0])
if err != nil {
t.Error("download error", err)
}
if !tester.downloader.Cancel() {
t.Error("cancel operation unsuccessfull")
}
hashSize, blockSize := tester.downloader.queue.Size()
if hashSize > 0 || blockSize > 0 {
t.Error("block (", blockSize, ") or hash (", hashSize, ") not 0")
}
}
func TestThrottling(t *testing.T) { func TestThrottling(t *testing.T) {
minDesiredPeerCount = 4 minDesiredPeerCount = 4
blockTtl = 1 * time.Second blockTtl = 1 * time.Second

View File

@ -63,6 +63,9 @@ func (pm *ProtocolManager) processBlocks() error {
max := int(math.Min(float64(len(blocks)), float64(blockProcAmount))) max := int(math.Min(float64(len(blocks)), float64(blockProcAmount)))
_, err := pm.chainman.InsertChain(blocks[:max]) _, err := pm.chainman.InsertChain(blocks[:max])
if err != nil { if err != nil {
// cancel download process
pm.downloader.Cancel()
return err return err
} }
blocks = blocks[max:] blocks = blocks[max:]

View File

@ -8,8 +8,11 @@ import (
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
) )
const openFileLimit = 128
type LDBDatabase struct { type LDBDatabase struct {
fn string fn string
@ -23,7 +26,7 @@ type LDBDatabase struct {
func NewLDBDatabase(file string) (*LDBDatabase, error) { func NewLDBDatabase(file string) (*LDBDatabase, error) {
// Open the db // Open the db
db, err := leveldb.OpenFile(file, nil) db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -7,6 +7,8 @@ import (
"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/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
) )
@ -37,7 +39,18 @@ func (self *Miner) Mining() bool {
return self.mining return self.mining
} }
func (m *Miner) SetGasPrice(price *big.Int) {
// FIXME block tests set a nil gas price. Quick dirty fix
if price == nil {
return
}
m.worker.gasPrice = price
}
func (self *Miner) Start(coinbase common.Address) { func (self *Miner) Start(coinbase common.Address) {
glog.V(logger.Info).Infoln("Starting mining operation")
self.mining = true self.mining = true
self.worker.coinbase = coinbase self.worker.coinbase = coinbase
self.worker.start() self.worker.start()

View File

@ -7,6 +7,7 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"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/state" "github.com/ethereum/go-ethereum/core/state"
@ -27,6 +28,12 @@ type environment struct {
block *types.Block block *types.Block
family *set.Set family *set.Set
uncles *set.Set uncles *set.Set
remove *set.Set
tcount int
ignoredTransactors *set.Set
lowGasTransactors *set.Set
ownedAccounts *set.Set
lowGasTxs types.Transactions
} }
func env(block *types.Block, eth core.Backend) *environment { func env(block *types.Block, eth core.Backend) *environment {
@ -72,6 +79,7 @@ type worker struct {
proc *core.BlockProcessor proc *core.BlockProcessor
coinbase common.Address coinbase common.Address
gasPrice *big.Int
extra []byte extra []byte
currentMu sync.Mutex currentMu sync.Mutex
@ -93,6 +101,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
eth: eth, eth: eth,
mux: eth.EventMux(), mux: eth.EventMux(),
recv: make(chan *types.Block), recv: make(chan *types.Block),
gasPrice: new(big.Int),
chain: eth.ChainManager(), chain: eth.ChainManager(),
proc: eth.BlockProcessor(), proc: eth.BlockProcessor(),
possibleUncles: make(map[common.Hash]*types.Block), possibleUncles: make(map[common.Hash]*types.Block),
@ -123,15 +132,22 @@ func (self *worker) pendingBlock() *types.Block {
} }
func (self *worker) start() { func (self *worker) start() {
self.mu.Lock()
defer self.mu.Unlock()
atomic.StoreInt32(&self.mining, 1)
// spin up agents // spin up agents
for _, agent := range self.agents { for _, agent := range self.agents {
agent.Start() agent.Start()
} }
atomic.StoreInt32(&self.mining, 1)
} }
func (self *worker) stop() { func (self *worker) stop() {
self.mu.Lock()
defer self.mu.Unlock()
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
// stop all agents // stop all agents
for _, agent := range self.agents { for _, agent := range self.agents {
@ -144,6 +160,9 @@ func (self *worker) stop() {
} }
func (self *worker) register(agent Agent) { func (self *worker) register(agent Agent) {
self.mu.Lock()
defer self.mu.Unlock()
self.agents = append(self.agents, agent) self.agents = append(self.agents, agent)
agent.SetReturnCh(self.recv) agent.SetReturnCh(self.recv)
} }
@ -163,8 +182,11 @@ out:
self.possibleUncles[ev.Block.Hash()] = ev.Block self.possibleUncles[ev.Block.Hash()] = ev.Block
self.uncleMu.Unlock() self.uncleMu.Unlock()
case core.TxPreEvent: case core.TxPreEvent:
// Apply transaction to the pending state if we're not mining
if atomic.LoadInt32(&self.mining) == 0 { if atomic.LoadInt32(&self.mining) == 0 {
self.commitNewWork() self.mu.Lock()
self.commitTransactions(types.Transactions{ev.Tx})
self.mu.Unlock()
} }
} }
case <-self.quit: case <-self.quit:
@ -230,13 +252,33 @@ func (self *worker) makeCurrent() {
} }
block.Header().Extra = self.extra block.Header().Extra = self.extra
self.current = env(block, self.eth) current := env(block, self.eth)
for _, ancestor := range self.chain.GetAncestors(block, 7) { for _, ancestor := range self.chain.GetAncestors(block, 7) {
self.current.family.Add(ancestor.Hash()) current.family.Add(ancestor.Hash())
}
accounts, _ := self.eth.AccountManager().Accounts()
// Keep track of transactions which return errors so they can be removed
current.remove = set.New()
current.tcount = 0
current.ignoredTransactors = set.New()
current.lowGasTransactors = set.New()
current.ownedAccounts = accountAddressesSet(accounts)
parent := self.chain.GetBlock(current.block.ParentHash())
current.coinbase.SetGasPool(core.CalcGasLimit(parent))
self.current = current
} }
parent := self.chain.GetBlock(self.current.block.ParentHash()) func (w *worker) setGasPrice(p *big.Int) {
self.current.coinbase.SetGasPool(core.CalcGasLimit(parent)) w.mu.Lock()
defer w.mu.Unlock()
// calculate the minimal gas price the miner accepts when sorting out transactions.
const pct = int64(90)
w.gasPrice = gasprice(p, pct)
w.mux.Post(core.GasPriceChanged{w.gasPrice})
} }
func (self *worker) commitNewWork() { func (self *worker) commitNewWork() {
@ -248,54 +290,14 @@ func (self *worker) commitNewWork() {
defer self.currentMu.Unlock() defer self.currentMu.Unlock()
self.makeCurrent() self.makeCurrent()
current := self.current
transactions := self.eth.TxPool().GetTransactions() transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByNonce{transactions}) sort.Sort(types.TxByNonce{transactions})
// Keep track of transactions which return errors so they can be removed // commit transactions for this run
var ( self.commitTransactions(transactions)
remove = set.New() self.eth.TxPool().RemoveTransactions(current.lowGasTxs)
tcount = 0
ignoredTransactors = set.New()
)
for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from, _ := tx.From()
// Move on to the next transaction when the transactor is in ignored transactions set
// This may occur when a transaction hits the gas limit. When a gas limit is hit and
// the transaction is processed (that could potentially be included in the block) it
// will throw a nonce error because the previous transaction hasn't been processed.
// Therefor we need to ignore any transaction after the ignored one.
if ignoredTransactors.Has(from) {
continue
}
self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0)
err := self.commitTransaction(tx)
switch {
case core.IsNonceErr(err) || core.IsInvalidTxErr(err):
// Remove invalid transactions
from, _ := tx.From()
self.chain.TxState().RemoveNonce(from, tx.Nonce())
remove.Add(tx.Hash())
if glog.V(logger.Detail) {
glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
}
case state.IsGasLimitErr(err):
from, _ := tx.From()
// ignore the transactor so no nonce errors will be thrown for this account
// next time the worker is run, they'll be picked up again.
ignoredTransactors.Add(from)
glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
default:
tcount++
}
}
var ( var (
uncles []*types.Header uncles []*types.Header
@ -321,7 +323,7 @@ func (self *worker) commitNewWork() {
// We only care about logging if we're actually mining // We only care about logging if we're actually mining
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", self.current.block.Number(), tcount, len(uncles)) glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", current.block.Number(), current.tcount, len(uncles))
} }
for _, hash := range badUncles { for _, hash := range badUncles {
@ -361,6 +363,71 @@ func (self *worker) commitUncle(uncle *types.Header) error {
return nil return nil
} }
func (self *worker) commitTransactions(transactions types.Transactions) {
current := self.current
for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from, _ := tx.From()
// check if it falls within margin
if tx.GasPrice().Cmp(self.gasPrice) < 0 {
// ignore the transaction and transactor. We ignore the transactor
// because nonce will fail after ignoring this transaction so there's
// no point
current.lowGasTransactors.Add(from)
glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4])
}
// Continue with the next transaction if the transaction sender is included in
// the low gas tx set. This will also remove the tx and all sequential transaction
// from this transactor
if current.lowGasTransactors.Has(from) {
// add tx to the low gas set. This will be removed at the end of the run
// owned accounts are ignored
if !current.ownedAccounts.Has(from) {
current.lowGasTxs = append(current.lowGasTxs, tx)
}
continue
}
// Move on to the next transaction when the transactor is in ignored transactions set
// This may occur when a transaction hits the gas limit. When a gas limit is hit and
// the transaction is processed (that could potentially be included in the block) it
// will throw a nonce error because the previous transaction hasn't been processed.
// Therefor we need to ignore any transaction after the ignored one.
if current.ignoredTransactors.Has(from) {
continue
}
self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0)
err := self.commitTransaction(tx)
switch {
case core.IsNonceErr(err) || core.IsInvalidTxErr(err):
// Remove invalid transactions
from, _ := tx.From()
self.chain.TxState().RemoveNonce(from, tx.Nonce())
current.remove.Add(tx.Hash())
if glog.V(logger.Detail) {
glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
}
case state.IsGasLimitErr(err):
from, _ := tx.From()
// ignore the transactor so no nonce errors will be thrown for this account
// next time the worker is run, they'll be picked up again.
current.ignoredTransactors.Add(from)
glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
default:
current.tcount++
}
}
}
func (self *worker) commitTransaction(tx *types.Transaction) error { func (self *worker) commitTransaction(tx *types.Transaction) error {
snap := self.current.state.Copy() snap := self.current.state.Copy()
receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
@ -383,3 +450,20 @@ func (self *worker) HashRate() int64 {
return tot return tot
} }
// gasprice calculates a reduced gas price based on the pct
// XXX Use big.Rat?
func gasprice(price *big.Int, pct int64) *big.Int {
p := new(big.Int).Set(price)
p.Div(p, big.NewInt(100))
p.Mul(p, big.NewInt(pct))
return p
}
func accountAddressesSet(accounts []accounts.Account) *set.Set {
accountSet := set.New()
for _, account := range accounts {
accountSet.Add(common.BytesToAddress(account.Address))
}
return accountSet
}

View File

@ -450,10 +450,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = newHexData(res) *reply = newHexData(res)
case "shh_version": case "shh_version":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Retrieves the currently running whisper protocol version // Retrieves the currently running whisper protocol version
*reply = api.xeth().WhisperVersion() *reply = api.xeth().WhisperVersion()
case "shh_post": case "shh_post":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Injects a new message into the whisper network // Injects a new message into the whisper network
args := new(WhisperMessageArgs) args := new(WhisperMessageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -466,10 +474,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = true *reply = true
case "shh_newIdentity": case "shh_newIdentity":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Creates a new whisper identity to use for sending/receiving messages // Creates a new whisper identity to use for sending/receiving messages
*reply = api.xeth().Whisper().NewIdentity() *reply = api.xeth().Whisper().NewIdentity()
case "shh_hasIdentity": case "shh_hasIdentity":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Checks if an identity if owned or not // Checks if an identity if owned or not
args := new(WhisperIdentityArgs) args := new(WhisperIdentityArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -478,6 +494,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().Whisper().HasIdentity(args.Identity) *reply = api.xeth().Whisper().HasIdentity(args.Identity)
case "shh_newFilter": case "shh_newFilter":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Create a new filter to watch and match messages with // Create a new filter to watch and match messages with
args := new(WhisperFilterArgs) args := new(WhisperFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -487,6 +507,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = newHexNum(big.NewInt(int64(id)).Bytes()) *reply = newHexNum(big.NewInt(int64(id)).Bytes())
case "shh_uninstallFilter": case "shh_uninstallFilter":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Remove an existing filter watching messages // Remove an existing filter watching messages
args := new(FilterIdArgs) args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -495,6 +519,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().UninstallWhisperFilter(args.Id) *reply = api.xeth().UninstallWhisperFilter(args.Id)
case "shh_getFilterChanges": case "shh_getFilterChanges":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Retrieve all the new messages arrived since the last request // Retrieve all the new messages arrived since the last request
args := new(FilterIdArgs) args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -503,12 +531,17 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().WhisperMessagesChanged(args.Id) *reply = api.xeth().WhisperMessagesChanged(args.Id)
case "shh_getMessages": case "shh_getMessages":
// Short circuit if whisper is not running
if api.xeth().Whisper() == nil {
return NewNotAvailableError(req.Method, "whisper offline")
}
// Retrieve all the cached messages matching a specific, existing filter // Retrieve all the cached messages matching a specific, existing filter
args := new(FilterIdArgs) args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
*reply = api.xeth().WhisperMessages(args.Id) *reply = api.xeth().WhisperMessages(args.Id)
case "eth_hashrate": case "eth_hashrate":
*reply = newHexNum(api.xeth().HashRate()) *reply = newHexNum(api.xeth().HashRate())

View File

@ -116,7 +116,7 @@ func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
switch reserr.(type) { switch reserr.(type) {
case nil: case nil:
response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply} response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
case *NotImplementedError: case *NotImplementedError, *NotAvailableError:
jsonerr := &RpcErrorObject{-32601, reserr.Error()} jsonerr := &RpcErrorObject{-32601, reserr.Error()}
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:

View File

@ -2,6 +2,7 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/jsre"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
@ -50,6 +51,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
var respif interface{} var respif interface{}
err = self.ethApi.GetRequestReply(&req, &respif) err = self.ethApi.GetRequestReply(&req, &respif)
if err != nil { if err != nil {
fmt.Println("Error response:", err)
return self.err(call, -32603, err.Error(), req.Id) return self.err(call, -32603, err.Error(), req.Id)
} }
call.Otto.Set("ret_jsonrpc", jsonrpcver) call.Otto.Set("ret_jsonrpc", jsonrpcver)

View File

@ -209,6 +209,22 @@ func NewNotImplementedError(method string) *NotImplementedError {
} }
} }
type NotAvailableError struct {
Method string
Reason string
}
func (e *NotAvailableError) Error() string {
return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
}
func NewNotAvailableError(method string, reason string) *NotAvailableError {
return &NotAvailableError{
Method: method,
Reason: reason,
}
}
type DecodeParamError struct { type DecodeParamError struct {
err string err string
} }

View File

@ -103,7 +103,7 @@ func testEthConfig() *eth.Config {
return &eth.Config{ return &eth.Config{
DataDir: common.DefaultDataDir(), DataDir: common.DefaultDataDir(),
LogLevel: 5, Verbosity: 5,
Etherbase: "primary", Etherbase: "primary",
AccountManager: accounts.NewManager(ks), AccountManager: accounts.NewManager(ks),
NewDB: func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }, NewDB: func(path string) (common.Database, error) { return ethdb.NewMemDatabase() },

View File

@ -79,7 +79,6 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth {
xeth := &XEth{ xeth := &XEth{
backend: eth, backend: eth,
frontend: frontend, frontend: frontend,
whisper: NewWhisper(eth.Whisper()),
quit: make(chan struct{}), quit: make(chan struct{}),
filterManager: filter.NewFilterManager(eth.EventMux()), filterManager: filter.NewFilterManager(eth.EventMux()),
logQueue: make(map[int]*logQueue), logQueue: make(map[int]*logQueue),
@ -88,6 +87,9 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth {
messages: make(map[int]*whisperFilter), messages: make(map[int]*whisperFilter),
agent: miner.NewRemoteAgent(), agent: miner.NewRemoteAgent(),
} }
if eth.Whisper() != nil {
xeth.whisper = NewWhisper(eth.Whisper())
}
eth.Miner().Register(xeth.agent) eth.Miner().Register(xeth.agent)
if frontend == nil { if frontend == nil {
xeth.frontend = dummyFrontend{} xeth.frontend = dummyFrontend{}