Merge branch 'release/1.0.1' of github.com:ethereum/go-ethereum into limits-changes

Conflicts:
	cmd/ethtest/main.go
	cmd/geth/main.go
	cmd/utils/flags.go
	core/chain_manager.go
	core/chain_util.go
	eth/backend.go
	ethdb/database.go
	params/protocol_params.go
	rpc/comms/http_net.go
This commit is contained in:
Anthony Eufemio 2015-08-06 02:28:11 +08:00
commit 6d890031ff
56 changed files with 1356 additions and 357 deletions

View file

@ -1,9 +1,6 @@
language: go language: go
go: go:
- 1.4.2 - 1.4.2
before_install:
- sudo apt-get update -qq
- sudo apt-get install -yqq libgmp3-dev
install: install:
# - go get code.google.com/p/go.tools/cmd/goimports # - go get code.google.com/p/go.tools/cmd/goimports
# - go get github.com/golang/lint/golint # - go get github.com/golang/lint/golint
@ -22,7 +19,11 @@ after_success:
env: env:
global: global:
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64=" - secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
sudo: false
addons:
apt:
packages:
- libgmp3-dev
notifications: notifications:
webhooks: webhooks:
urls: urls:

View file

@ -9,6 +9,8 @@ import (
"net/http" "net/http"
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
) )
// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical // HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical
@ -25,6 +27,7 @@ func NewHTTPUClient() (*HTTPUClient, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
fdtrack.Open("upnp")
return &HTTPUClient{conn: conn}, nil return &HTTPUClient{conn: conn}, nil
} }
@ -33,6 +36,7 @@ func NewHTTPUClient() (*HTTPUClient, error) {
func (httpu *HTTPUClient) Close() error { func (httpu *HTTPUClient) Close() error {
httpu.connLock.Lock() httpu.connLock.Lock()
defer httpu.connLock.Unlock() defer httpu.connLock.Unlock()
fdtrack.Close("upnp")
return httpu.conn.Close() return httpu.conn.Close()
} }

View file

@ -7,9 +7,12 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"github.com/ethereum/go-ethereum/fdtrack"
) )
const ( const (
@ -26,6 +29,17 @@ type SOAPClient struct {
func NewSOAPClient(endpointURL url.URL) *SOAPClient { func NewSOAPClient(endpointURL url.URL) *SOAPClient {
return &SOAPClient{ return &SOAPClient{
EndpointURL: endpointURL, EndpointURL: endpointURL,
HTTPClient: http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
c, err := net.Dial(network, addr)
if c != nil {
c = fdtrack.WrapConn("upnp", c)
}
return c, err
},
},
},
} }
} }

View file

@ -5,6 +5,8 @@ import (
"log" "log"
"net" "net"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
) )
// Implement the NAT-PMP protocol, typically supported by Apple routers and open source // Implement the NAT-PMP protocol, typically supported by Apple routers and open source
@ -102,6 +104,8 @@ func (n *Client) rpc(msg []byte, resultSize int) (result []byte, err error) {
if err != nil { if err != nil {
return return
} }
fdtrack.Open("natpmp")
defer fdtrack.Close("natpmp")
defer conn.Close() defer conn.Close()
result = make([]byte, resultSize) result = make([]byte, resultSize)

View file

@ -18,6 +18,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
) )
@ -369,6 +370,8 @@ func (fw fileWrap) Close() error {
err := fw.File.Close() err := fw.File.Close()
if err != nil { if err != nil {
f.fs.log(fmt.Sprintf("close %s.%d: %v", f.Type(), f.Num(), err)) f.fs.log(fmt.Sprintf("close %s.%d: %v", f.Type(), f.Num(), err))
} else {
fdtrack.Close("leveldb")
} }
return err return err
} }
@ -400,6 +403,7 @@ func (f *file) Open() (Reader, error) {
return nil, err return nil, err
} }
ok: ok:
fdtrack.Open("leveldb")
f.open = true f.open = true
f.fs.open++ f.fs.open++
return fileWrap{of, f}, nil return fileWrap{of, f}, nil
@ -418,6 +422,7 @@ func (f *file) Create() (Writer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
fdtrack.Open("leveldb")
f.open = true f.open = true
f.fs.open++ f.fs.open++
return fileWrap{of, f}, nil return fileWrap{of, f}, nil

View file

@ -78,8 +78,8 @@ func (am *Manager) DeleteAccount(address common.Address, auth string) error {
func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) { func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock() am.mutex.RLock()
defer am.mutex.RUnlock()
unlockedKey, found := am.unlocked[a.Address] unlockedKey, found := am.unlocked[a.Address]
am.mutex.RUnlock()
if !found { if !found {
return nil, ErrLocked return nil, ErrLocked
} }
@ -87,14 +87,17 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error)
return signature, err return signature, err
} }
// unlock indefinitely // Unlock unlocks the given account indefinitely.
func (am *Manager) Unlock(addr common.Address, keyAuth string) error { func (am *Manager) Unlock(addr common.Address, keyAuth string) error {
return am.TimedUnlock(addr, keyAuth, 0) return am.TimedUnlock(addr, keyAuth, 0)
} }
// Unlock unlocks the account with the given address. The account // TimedUnlock unlocks the account with the given address. The account
// stays unlocked for the duration of timeout // stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
// it timeout is 0 the account is unlocked for the entire session // until the program exits.
//
// If the accout is already unlocked, TimedUnlock extends or shortens
// the active unlock timeout.
func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error { func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {

View file

@ -23,9 +23,10 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy"
) )
var testSigData = make([]byte, 32)
func TestSign(t *testing.T) { func TestSign(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
@ -33,26 +34,24 @@ func TestSign(t *testing.T) {
am := NewManager(ks) am := NewManager(ks)
pass := "" // not used but required by API pass := "" // not used but required by API
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
toSign := randentropy.GetEntropyCSPRNG(32)
am.Unlock(a1.Address, "") am.Unlock(a1.Address, "")
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestTimedUnlock(t *testing.T) { func TestTimedUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase) dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
am := NewManager(ks) am := NewManager(ks)
pass := "foo" pass := "foo"
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
toSign := randentropy.GetEntropyCSPRNG(32)
// Signing without passphrase fails because account is locked // Signing without passphrase fails because account is locked
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != ErrLocked { if err != ErrLocked {
t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
} }
@ -63,28 +62,26 @@ func TestTimedUnlock(t *testing.T) {
} }
// Signing without passphrase works because account is temp unlocked // Signing without passphrase works because account is temp unlocked
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != nil { if err != nil {
t.Fatal("Signing shouldn't return an error after unlocking, got ", err) t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
} }
// Signing fails again after automatic locking // Signing fails again after automatic locking
time.Sleep(150 * time.Millisecond) time.Sleep(150 * time.Millisecond)
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != ErrLocked { if err != ErrLocked {
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
} }
} }
func TestOverrideUnlock(t *testing.T) { func TestOverrideUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase) dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
am := NewManager(ks) am := NewManager(ks)
pass := "foo" pass := "foo"
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
toSign := randentropy.GetEntropyCSPRNG(32)
// Unlock indefinitely // Unlock indefinitely
if err = am.Unlock(a1.Address, pass); err != nil { if err = am.Unlock(a1.Address, pass); err != nil {
@ -92,7 +89,7 @@ func TestOverrideUnlock(t *testing.T) {
} }
// Signing without passphrase works because account is temp unlocked // Signing without passphrase works because account is temp unlocked
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != nil { if err != nil {
t.Fatal("Signing shouldn't return an error after unlocking, got ", err) t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
} }
@ -103,20 +100,46 @@ func TestOverrideUnlock(t *testing.T) {
} }
// Signing without passphrase still works because account is temp unlocked // Signing without passphrase still works because account is temp unlocked
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != nil { if err != nil {
t.Fatal("Signing shouldn't return an error after unlocking, got ", err) t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
} }
// Signing fails again after automatic locking // Signing fails again after automatic locking
time.Sleep(150 * time.Millisecond) time.Sleep(150 * time.Millisecond)
_, err = am.Sign(a1, toSign) _, err = am.Sign(a1, testSigData)
if err != ErrLocked { if err != ErrLocked {
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
} }
} }
// // This test should fail under -race if signing races the expiration goroutine.
func TestSignRace(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain)
defer os.RemoveAll(dir)
// Create a test account.
am := NewManager(ks)
a1, err := am.NewAccount("")
if err != nil {
t.Fatal("could not create the test account", err)
}
if err := am.TimedUnlock(a1.Address, "", 15*time.Millisecond); err != nil {
t.Fatalf("could not unlock the test account", err)
}
end := time.Now().Add(500 * time.Millisecond)
for time.Now().Before(end) {
if _, err := am.Sign(a1, testSigData); err == ErrLocked {
return
} else if err != nil {
t.Errorf("Sign error: %v", err)
return
}
time.Sleep(1 * time.Millisecond)
}
t.Errorf("Account did not lock within the timeout")
}
func tmpKeyStore(t *testing.T, new func(string) crypto.KeyStore) (string, crypto.KeyStore) { func tmpKeyStore(t *testing.T, new func(string) crypto.KeyStore) (string, crypto.KeyStore) {
d, err := ioutil.TempDir("", "eth-keystore-test") d, err := ioutil.TempDir("", "eth-keystore-test")

View file

@ -36,6 +36,7 @@ var (
defaultTest = "all" defaultTest = "all"
defaultDir = "." defaultDir = "."
allTests = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"} allTests = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"}
testDirMapping = map[string]string{"BlockTests": "BlockchainTests"}
skipTests = []string{} skipTests = []string{}
TestFlag = cli.StringFlag{ TestFlag = cli.StringFlag{
@ -135,8 +136,13 @@ func runSuite(test, file string) {
var err error var err error
var files []string var files []string
if test == defaultTest { if test == defaultTest {
// check if we have an explicit directory mapping for the test
if _, ok := testDirMapping[curTest]; ok {
files, err = getFiles(filepath.Join(file, testDirMapping[curTest]))
} else {
// otherwise assume test name
files, err = getFiles(filepath.Join(file, curTest)) files, err = getFiles(filepath.Join(file, curTest))
}
} else { } else {
files, err = getFiles(file) files, err = getFiles(file)
} }

View file

@ -18,70 +18,112 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"log"
"math/big" "math/big"
"os" "os"
"runtime" "runtime"
"time" "time"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
"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/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
) )
var ( var (
code = flag.String("code", "", "evm code") app *cli.App
loglevel = flag.Int("log", 4, "log level") DebugFlag = cli.BoolFlag{
gas = flag.String("gas", "1000000000", "gas amount") Name: "debug",
price = flag.String("price", "0", "gas price") Usage: "output full trace logs",
value = flag.String("value", "0", "tx value") }
dump = flag.Bool("dump", false, "dump state after run") CodeFlag = cli.StringFlag{
data = flag.String("data", "", "data") Name: "code",
Usage: "EVM code",
}
GasFlag = cli.StringFlag{
Name: "gas",
Usage: "gas limit for the evm",
Value: "10000000000",
}
PriceFlag = cli.StringFlag{
Name: "price",
Usage: "price set for the evm",
Value: "0",
}
ValueFlag = cli.StringFlag{
Name: "value",
Usage: "value set for the evm",
Value: "0",
}
DumpFlag = cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
}
InputFlag = cli.StringFlag{
Name: "input",
Usage: "input for the EVM",
}
SysStatFlag = cli.BoolFlag{
Name: "sysstat",
Usage: "display system stats",
}
) )
func perr(v ...interface{}) { func init() {
fmt.Println(v...) app = utils.NewApp("0.2", "the evm command line interface")
//os.Exit(1) app.Flags = []cli.Flag{
DebugFlag,
SysStatFlag,
CodeFlag,
GasFlag,
PriceFlag,
ValueFlag,
DumpFlag,
InputFlag,
}
app.Action = run
} }
func main() { func run(ctx *cli.Context) {
flag.Parse() vm.Debug = ctx.GlobalBool(DebugFlag.Name)
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
vm.Debug = true
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db) statedb := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender")) sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(*code)) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value)) vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)))
tstart := time.Now() tstart := time.Now()
ret, e := vmenv.Call(
sender,
receiver.Address(),
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
vmdone := time.Since(tstart)
ret, e := vmenv.Call(sender, receiver.Address(), common.Hex2Bytes(*data), common.Big(*gas), common.Big(*price), common.Big(*value))
logger.Flush()
if e != nil { if e != nil {
perr(e) fmt.Println(e)
os.Exit(1)
} }
if *dump { if ctx.GlobalBool(DumpFlag.Name) {
fmt.Println(string(statedb.Dump())) fmt.Println(string(statedb.Dump()))
} }
vm.StdErrFormat(vmenv.StructLogs()) vm.StdErrFormat(vmenv.StructLogs())
if ctx.GlobalBool(SysStatFlag.Name) {
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", time.Since(tstart)) fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d fmt.Printf(`alloc: %d
tot alloc: %d tot alloc: %d
no. malloc: %d no. malloc: %d
@ -89,8 +131,16 @@ heap alloc: %d
heap objs: %d heap objs: %d
num gc: %d num gc: %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC) `, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
}
fmt.Printf("%x\n", ret) fmt.Printf("OUT: 0x%x\n", ret)
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
} }
type VMEnv struct { type VMEnv struct {

View file

@ -23,6 +23,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"sort" "sort"
@ -44,6 +45,10 @@ import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
var passwordRegexp = regexp.MustCompile("personal.[nu]")
const passwordRepl = ""
type prompter interface { type prompter interface {
AppendHistory(string) AppendHistory(string)
Prompt(p string) (string, error) Prompt(p string) (string, error)
@ -413,8 +418,10 @@ func (self *jsre) interactive() {
str += input + "\n" str += input + "\n"
self.setIndent() self.setIndent()
if indentCount <= 0 { if indentCount <= 0 {
hist := str[:len(str)-1] hist := hidepassword(str[:len(str)-1])
if len(hist) > 0 {
self.AppendHistory(hist) self.AppendHistory(hist)
}
self.parseInput(str) self.parseInput(str)
str = "" str = ""
} }
@ -422,6 +429,14 @@ func (self *jsre) interactive() {
} }
} }
func hidepassword(input string) string {
if passwordRegexp.MatchString(input) {
return passwordRepl
} else {
return input
}
}
func (self *jsre) withHistory(op func(*os.File)) { func (self *jsre) withHistory(op func(*os.File)) {
datadir := common.DefaultDataDir() datadir := common.DefaultDataDir()
if self.ethereum != nil { if self.ethereum != nil {

View file

@ -38,6 +38,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/fdtrack"
"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"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
@ -49,7 +50,7 @@ import (
const ( const (
ClientIdentifier = "Geth" ClientIdentifier = "Geth"
Version = "1.0.0" Version = "1.0.1"
) )
var ( var (
@ -280,6 +281,8 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.BootnodesFlag, utils.BootnodesFlag,
utils.DataDirFlag, utils.DataDirFlag,
utils.BlockchainVersionFlag, utils.BlockchainVersionFlag,
utils.OlympicFlag,
utils.CacheFlag,
utils.JSpathFlag, utils.JSpathFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.MaxPeersFlag, utils.MaxPeersFlag,
@ -345,6 +348,9 @@ func main() {
func run(ctx *cli.Context) { func run(ctx *cli.Context) {
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
if ctx.GlobalBool(utils.OlympicFlag.Name) {
utils.InitOlympic()
}
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx) cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
ethereum, err := eth.New(cfg) ethereum, err := eth.New(cfg)
@ -500,7 +506,7 @@ func blockRecovery(ctx *cli.Context) {
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx) cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
utils.CheckLegalese(cfg.DataDir) utils.CheckLegalese(cfg.DataDir)
blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain")) blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
if err != nil { if err != nil {
glog.Fatalln("could not open db:", err) glog.Fatalln("could not open db:", err)
} }
@ -527,6 +533,9 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
// Start Ethereum itself // Start Ethereum itself
utils.StartEthereum(eth) utils.StartEthereum(eth)
// Start logging file descriptor stats.
fdtrack.Start()
am := eth.AccountManager() am := eth.AccountManager()
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
accounts := strings.Split(account, " ") accounts := strings.Split(account, " ")

View file

@ -21,6 +21,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"io" "io"
"math/big"
"os" "os"
"os/signal" "os/signal"
"regexp" "regexp"
@ -32,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"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"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/peterh/liner" "github.com/peterh/liner"
) )
@ -143,6 +145,15 @@ func StartEthereum(ethereum *eth.Ethereum) {
}() }()
} }
func InitOlympic() {
params.DurationLimit = big.NewInt(8)
params.GenesisGasLimit = big.NewInt(3141592)
params.MinGasLimit = big.NewInt(125000)
params.MaximumExtraDataSize = big.NewInt(1024)
NetworkIdFlag.Value = 0
core.BlockReward = big.NewInt(1.5e+18)
}
func FormatTransactionData(data string) []byte { func FormatTransactionData(data string) []byte {
d := common.StringToByteFunc(data, func(s string) (ret []byte) { d := common.StringToByteFunc(data, func(s string) (ret []byte) {
slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000) slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000)
@ -203,6 +214,11 @@ func ImportChain(chain *core.ChainManager, fn string) error {
} else if err != nil { } else if err != nil {
return fmt.Errorf("at block %d: %v", n, err) return fmt.Errorf("at block %d: %v", n, err)
} }
// don't import first block
if b.NumberU64() == 0 {
i--
continue
}
blocks[i] = &b blocks[i] = &b
n++ n++
} }
@ -218,6 +234,7 @@ func ImportChain(chain *core.ChainManager, fn string) error {
batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4]) batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4])
continue continue
} }
if _, err := chain.InsertChain(blocks[:i]); err != nil { if _, err := chain.InsertChain(blocks[:i]); err != nil {
return fmt.Errorf("invalid block %d: %v", n, err) return fmt.Errorf("invalid block %d: %v", n, err)
} }

View file

@ -126,6 +126,15 @@ var (
Name: "natspec", Name: "natspec",
Usage: "Enable NatSpec confirmation notice", Usage: "Enable NatSpec confirmation notice",
} }
CacheFlag = cli.IntFlag{
Name: "cache",
Usage: "Megabytes of memory allocated to internal caching",
Value: 0,
}
OlympicFlag = cli.BoolFlag{
Name: "olympic",
Usage: "Use olympic style protocol",
}
// miner settings // miner settings
MinerThreadsFlag = cli.IntFlag{ MinerThreadsFlag = cli.IntFlag{
@ -149,7 +158,7 @@ var (
GasPriceFlag = cli.StringFlag{ GasPriceFlag = cli.StringFlag{
Name: "gasprice", Name: "gasprice",
Usage: "Sets the minimal gasprice when mining transactions", Usage: "Sets the minimal gasprice when mining transactions",
Value: new(big.Int).Mul(big.NewInt(500), common.Shannon).String(), Value: new(big.Int).Mul(big.NewInt(50), common.Shannon).String(),
} }
UnlockedAccountFlag = cli.StringFlag{ UnlockedAccountFlag = cli.StringFlag{
@ -309,12 +318,12 @@ var (
GpoMinGasPriceFlag = cli.StringFlag{ GpoMinGasPriceFlag = cli.StringFlag{
Name: "gpomin", Name: "gpomin",
Usage: "Minimum suggested gas price", Usage: "Minimum suggested gas price",
Value: new(big.Int).Mul(big.NewInt(1), common.Szabo).String(), Value: new(big.Int).Mul(big.NewInt(50), common.Shannon).String(),
} }
GpoMaxGasPriceFlag = cli.StringFlag{ GpoMaxGasPriceFlag = cli.StringFlag{
Name: "gpomax", Name: "gpomax",
Usage: "Maximum suggested gas price", Usage: "Maximum suggested gas price",
Value: new(big.Int).Mul(big.NewInt(100), common.Szabo).String(), Value: new(big.Int).Mul(big.NewInt(500), common.Shannon).String(),
} }
GpoFullBlockRatioFlag = cli.IntFlag{ GpoFullBlockRatioFlag = cli.IntFlag{
Name: "gpofull", Name: "gpofull",
@ -384,6 +393,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name), GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name),
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name), GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
SkipBcVersionCheck: false, SkipBcVersionCheck: false,
NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), NetworkId: ctx.GlobalInt(NetworkIdFlag.Name),
LogFile: ctx.GlobalString(LogFileFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name),
@ -396,6 +406,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
Port: ctx.GlobalString(ListenPortFlag.Name), Port: ctx.GlobalString(ListenPortFlag.Name),
Olympic: ctx.GlobalBool(OlympicFlag.Name),
NAT: MakeNAT(ctx), NAT: MakeNAT(ctx),
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name), Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
@ -425,17 +436,26 @@ func SetupLogger(ctx *cli.Context) {
// MakeChain creates a chain manager from set command line flags. // MakeChain creates a chain manager from set command line flags.
func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, extraDB common.Database) { func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, extraDB common.Database) {
dd := ctx.GlobalString(DataDirFlag.Name) datadir := ctx.GlobalString(DataDirFlag.Name)
cache := ctx.GlobalInt(CacheFlag.Name)
var err error var err error
if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "blockchain")); err != nil { if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "blockchain"), cache); err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
} }
if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "state")); err != nil { if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "state"), cache); err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
} }
if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "extra")); err != nil { if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "extra"), cache); err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
} }
if ctx.GlobalBool(OlympicFlag.Name) {
InitOlympic()
_, err := core.WriteTestNetGenesisBlock(stateDB, blockDB, 42)
if err != nil {
glog.Fatalln(err)
}
}
eventMux := new(event.TypeMux) eventMux := new(event.TypeMux)
pow := ethash.New() pow := ethash.New()

View file

@ -153,7 +153,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
b.Fatalf("cannot create temporary directory: %v", err) b.Fatalf("cannot create temporary directory: %v", err)
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
db, err = ethdb.NewLDBDatabase(dir) db, err = ethdb.NewLDBDatabase(dir, 0)
if err != nil { if err != nil {
b.Fatalf("cannot create temporary database: %v", err) b.Fatalf("cannot create temporary database: %v", err)
} }

View file

@ -386,7 +386,7 @@ func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, check
return BlockEqualTSErr return BlockEqualTSErr
} }
expd := CalcDifficulty(block.Time, parent.Time(), parent.Difficulty()) expd := CalcDifficulty(block.Time, parent.Time(), parent.Number(), parent.Difficulty())
if expd.Cmp(block.Difficulty) != 0 { if expd.Cmp(block.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
} }

View file

@ -171,7 +171,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
Root: state.Root(), Root: state.Root(),
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(), Coinbase: parent.Coinbase(),
Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()), Difficulty: CalcDifficulty(time, parent.Time(), parent.Number(), parent.Difficulty()),
GasLimit: CalcGasLimit(parent), GasLimit: CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Number: new(big.Int).Add(parent.Number(), common.Big1), Number: new(big.Int).Add(parent.Number(), common.Big1),

View file

@ -73,13 +73,11 @@ type ChainManager struct {
lastBlockHash common.Hash lastBlockHash common.Hash
currentGasLimit *big.Int currentGasLimit *big.Int
transState *state.StateDB
txState *state.ManagedState
cache *lru.Cache // cache is the LRU caching cache *lru.Cache // cache is the LRU caching
futureBlocks *lru.Cache // future blocks are blocks added for later processing futureBlocks *lru.Cache // future blocks are blocks added for later processing
quit chan struct{} quit chan struct{}
running int32 // running must be called automically
// procInterrupt must be atomically called // procInterrupt must be atomically called
procInterrupt int32 // interrupt signaler for block processing procInterrupt int32 // interrupt signaler for block processing
wg sync.WaitGroup wg sync.WaitGroup
@ -101,7 +99,19 @@ func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux
bc.genesisBlock = bc.GetBlockByNumber(0) bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil { if bc.genesisBlock == nil {
<<<<<<< HEAD
return nil, ErrNoGenesis return nil, ErrNoGenesis
=======
reader, err := NewDefaultGenesisReader()
if err != nil {
return nil, err
}
bc.genesisBlock, err = WriteGenesisBlock(stateDb, blockDb, reader)
if err != nil {
return nil, err
}
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
>>>>>>> 9a02f537260f64cc91a66074d920ae20b99b0a40
} }
if err := bc.setLastState(); err != nil { if err := bc.setLastState(); err != nil {
@ -122,9 +132,7 @@ func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux
} }
} }
bc.transState = bc.State().Copy()
// Take ownership of this particular state // Take ownership of this particular state
bc.txState = state.ManageState(bc.State().Copy())
bc.futureBlocks, _ = lru.New(maxFutureBlocks) bc.futureBlocks, _ = lru.New(maxFutureBlocks)
bc.makeCache() bc.makeCache()
@ -146,9 +154,6 @@ func (bc *ChainManager) SetHead(head *types.Block) {
bc.currentBlock = head bc.currentBlock = head
bc.makeCache() bc.makeCache()
statedb := state.New(head.Root(), bc.stateDb)
bc.txState = state.ManageState(statedb)
bc.transState = statedb.Copy()
bc.setTotalDifficulty(head.Td) bc.setTotalDifficulty(head.Td)
bc.insert(head) bc.insert(head)
bc.setLastState() bc.setLastState()
@ -197,17 +202,6 @@ func (self *ChainManager) State() *state.StateDB {
return state.New(self.CurrentBlock().Root(), self.stateDb) return state.New(self.CurrentBlock().Root(), self.stateDb)
} }
func (self *ChainManager) TransState() *state.StateDB {
self.tsmu.RLock()
defer self.tsmu.RUnlock()
return self.transState
}
func (self *ChainManager) setTransState(statedb *state.StateDB) {
self.transState = statedb
}
func (bc *ChainManager) recover() bool { func (bc *ChainManager) recover() bool {
data, _ := bc.blockDb.Get([]byte("checkpoint")) data, _ := bc.blockDb.Get([]byte("checkpoint"))
if len(data) != 0 { if len(data) != 0 {
@ -462,6 +456,9 @@ func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
} }
func (bc *ChainManager) Stop() { func (bc *ChainManager) Stop() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return
}
close(bc.quit) close(bc.quit)
atomic.StoreInt32(&bc.procInterrupt, 1) atomic.StoreInt32(&bc.procInterrupt, 1)
@ -523,9 +520,6 @@ func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status wr
self.insert(block) self.insert(block)
self.mu.Unlock() self.mu.Unlock()
self.setTransState(state.New(block.Root(), self.stateDb))
self.txState.SetState(state.New(block.Root(), self.stateDb))
status = CanonStatTy status = CanonStatTy
} else { } else {
status = SideStatTy status = SideStatTy

View file

@ -392,7 +392,6 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
bc.futureBlocks, _ = lru.New(100) bc.futureBlocks, _ = lru.New(100)
bc.processor = bproc{} bc.processor = bproc{}
bc.ResetWithGenesisBlock(genesis) bc.ResetWithGenesisBlock(genesis)
bc.txState = state.ManageState(bc.State())
return bc return bc
} }

View file

@ -32,12 +32,13 @@ import (
var ( var (
blockHashPre = []byte("block-hash-") blockHashPre = []byte("block-hash-")
blockNumPre = []byte("block-num-") blockNumPre = []byte("block-num-")
expDiffPeriod = big.NewInt(100000)
) )
// CalcDifficulty is the difficulty adjustment algorithm. It returns // CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block b should have when created at time // the difficulty that a new block b should have when created at time
// given the parent block's time and difficulty. // given the parent block's time and difficulty.
func CalcDifficulty(time, parentTime uint64, parentDiff *big.Int) *big.Int { func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
diff := new(big.Int) diff := new(big.Int)
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
bigTime := new(big.Int) bigTime := new(big.Int)
@ -52,8 +53,19 @@ func CalcDifficulty(time, parentTime uint64, parentDiff *big.Int) *big.Int {
diff.Sub(parentDiff, adjust) diff.Sub(parentDiff, adjust)
} }
if diff.Cmp(params.MinimumDifficulty) < 0 { if diff.Cmp(params.MinimumDifficulty) < 0 {
return params.MinimumDifficulty diff = params.MinimumDifficulty
} }
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, expDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
// diff = diff + 2^(periodCount - 2)
expDiff := periodCount.Sub(periodCount, common.Big2)
expDiff.Exp(common.Big2, expDiff, nil)
diff.Add(diff, expDiff)
diff = common.BigMax(diff, params.MinimumDifficulty)
}
return diff return diff
} }
@ -69,17 +81,30 @@ func CalcTD(block, parent *types.Block) *big.Int {
// CalcGasLimit computes the gas limit of the next block after parent. // CalcGasLimit computes the gas limit of the next block after parent.
// The result may be modified by the caller. // The result may be modified by the caller.
// This is miner strategy, not consensus protocol.
func CalcGasLimit(parent *types.Block) *big.Int { func CalcGasLimit(parent *types.Block) *big.Int {
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) // contrib = (parentGasUsed * 3 / 2) / 1024
contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3)) contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
contrib = contrib.Div(contrib, big.NewInt(2)) contrib = contrib.Div(contrib, big.NewInt(2))
contrib = contrib.Div(contrib, params.GasLimitBoundDivisor) contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
// decay = parentGasLimit / 1024 -1
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
decay.Sub(decay, big.NewInt(1))
/*
strategy: gasLimit of block-to-mine is set based on parent's
gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
increase it, otherwise lower it (or leave it unchanged if it's right
at that usage) the amount increased/decreased depends on how far away
from parentGasLimit * (2/3) parentGasUsed is.
*/
gl := new(big.Int).Sub(parent.GasLimit(), decay) gl := new(big.Int).Sub(parent.GasLimit(), decay)
gl = gl.Add(gl, contrib) gl = gl.Add(gl, contrib)
gl = gl.Add(gl, big.NewInt(1))
gl.Set(common.BigMax(gl, params.MinGasLimit)) gl.Set(common.BigMax(gl, params.MinGasLimit))
// however, if we're now below the target (GenesisGasLimit) we increase the
// limit as much as we can (parentGasLimit / 1024 -1)
if gl.Cmp(params.GenesisGasLimit) < 0 { if gl.Cmp(params.GenesisGasLimit) < 0 {
gl.Add(parent.GasLimit(), decay) gl.Add(parent.GasLimit(), decay)
gl.Set(common.BigMin(gl, params.GenesisGasLimit)) gl.Set(common.BigMin(gl, params.GenesisGasLimit))

77
core/chain_util_test.go Normal file
View file

@ -0,0 +1,77 @@
// 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 core
import (
"encoding/json"
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
)
type diffTest struct {
ParentTimestamp uint64
ParentDifficulty *big.Int
CurrentTimestamp uint64
CurrentBlocknumber *big.Int
CurrentDifficulty *big.Int
}
func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
var ext struct {
ParentTimestamp string
ParentDifficulty string
CurrentTimestamp string
CurrentBlocknumber string
CurrentDifficulty string
}
if err := json.Unmarshal(b, &ext); err != nil {
return err
}
d.ParentTimestamp = common.String2Big(ext.ParentTimestamp).Uint64()
d.ParentDifficulty = common.String2Big(ext.ParentDifficulty)
d.CurrentTimestamp = common.String2Big(ext.CurrentTimestamp).Uint64()
d.CurrentBlocknumber = common.String2Big(ext.CurrentBlocknumber)
d.CurrentDifficulty = common.String2Big(ext.CurrentDifficulty)
return nil
}
func TestDifficulty(t *testing.T) {
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
if err != nil {
t.Fatal(err)
}
defer file.Close()
tests := make(map[string]diffTest)
err = json.NewDecoder(file).Decode(&tests)
if err != nil {
t.Fatal(err)
}
for name, test := range tests {
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
if diff.Cmp(test.CurrentDifficulty) != 0 {
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
}
}
}

30
core/default_genesis.go Normal file

File diff suppressed because one or more lines are too long

View file

@ -44,6 +44,7 @@ type StateDB struct {
thash, bhash common.Hash thash, bhash common.Hash
txIndex int txIndex int
logs map[common.Hash]Logs logs map[common.Hash]Logs
logSize uint
} }
// Create a new state from a given trie // Create a new state from a given trie
@ -66,7 +67,9 @@ func (self *StateDB) AddLog(log *Log) {
log.TxHash = self.thash log.TxHash = self.thash
log.BlockHash = self.bhash log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex) log.TxIndex = uint(self.txIndex)
log.Index = self.logSize
self.logs[self.thash] = append(self.logs[self.thash], log) self.logs[self.thash] = append(self.logs[self.thash], log)
self.logSize++
} }
func (self *StateDB) GetLogs(hash common.Hash) Logs { func (self *StateDB) GetLogs(hash common.Hash) Logs {
@ -288,6 +291,7 @@ func (self *StateDB) Copy() *StateDB {
state.logs[hash] = make(Logs, len(logs)) state.logs[hash] = make(Logs, len(logs))
copy(state.logs[hash], logs) copy(state.logs[hash], logs)
} }
state.logSize = self.logSize
return state return state
} }
@ -298,6 +302,7 @@ func (self *StateDB) Set(state *StateDB) {
self.refund = state.refund self.refund = state.refund
self.logs = state.logs self.logs = state.logs
self.logSize = state.logSize
} }
func (s *StateDB) Root() common.Hash { func (s *StateDB) Root() common.Hash {

View file

@ -135,7 +135,7 @@ func (pool *TxPool) resetState() {
func (pool *TxPool) Stop() { func (pool *TxPool) Stop() {
close(pool.quit) close(pool.quit)
pool.events.Unsubscribe() pool.events.Unsubscribe()
glog.V(logger.Info).Infoln("TX Pool stopped") glog.V(logger.Info).Infoln("Transaction pool stopped")
} }
func (pool *TxPool) State() *state.ManagedState { func (pool *TxPool) State() *state.ManagedState {
@ -356,11 +356,12 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
for _, tx := range txs { for _, tx := range txs {
self.removeTx(tx.Hash()) self.RemoveTx(tx.Hash())
} }
} }
func (pool *TxPool) removeTx(hash common.Hash) { // RemoveTx removes the transaction with the given hash from the pool.
func (pool *TxPool) RemoveTx(hash common.Hash) {
// delete from pending pool // delete from pending pool
delete(pool.pending, hash) delete(pool.pending, hash)
// delete from queue // delete from queue

View file

@ -130,7 +130,7 @@ func TestRemoveTx(t *testing.T) {
t.Error("expected txs to be 1, got", len(pool.pending)) t.Error("expected txs to be 1, got", len(pool.pending))
} }
pool.removeTx(tx.Hash()) pool.RemoveTx(tx.Hash())
if len(pool.queue) > 0 { if len(pool.queue) > 0 {
t.Error("expected queue to be 0, got", len(pool.queue)) t.Error("expected queue to be 0, got", len(pool.queue))

View file

@ -19,9 +19,11 @@ package core
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb"
) )
var ( var (
@ -31,13 +33,21 @@ var (
// PutTransactions stores the transactions in the given database // PutTransactions stores the transactions in the given database
func PutTransactions(db common.Database, block *types.Block, txs types.Transactions) { func PutTransactions(db common.Database, block *types.Block, txs types.Transactions) {
batch := new(leveldb.Batch)
_, batchWrite := db.(*ethdb.LDBDatabase)
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
rlpEnc, err := rlp.EncodeToBytes(tx) rlpEnc, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
glog.V(logger.Debug).Infoln("Failed encoding tx", err) glog.V(logger.Debug).Infoln("Failed encoding tx", err)
return return
} }
if batchWrite {
batch.Put(tx.Hash().Bytes(), rlpEnc)
} else {
db.Put(tx.Hash().Bytes(), rlpEnc) db.Put(tx.Hash().Bytes(), rlpEnc)
}
var txExtra struct { var txExtra struct {
BlockHash common.Hash BlockHash common.Hash
@ -52,23 +62,47 @@ func PutTransactions(db common.Database, block *types.Block, txs types.Transacti
glog.V(logger.Debug).Infoln("Failed encoding tx meta data", err) glog.V(logger.Debug).Infoln("Failed encoding tx meta data", err)
return return
} }
if batchWrite {
batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
} else {
db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta) db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
} }
}
if db, ok := db.(*ethdb.LDBDatabase); ok {
if err := db.LDB().Write(batch, nil); err != nil {
glog.V(logger.Error).Infoln("db write err:", err)
}
}
} }
// PutReceipts stores the receipts in the current database // PutReceipts stores the receipts in the current database
func PutReceipts(db common.Database, receipts types.Receipts) error { func PutReceipts(db common.Database, receipts types.Receipts) error {
batch := new(leveldb.Batch)
_, batchWrite := db.(*ethdb.LDBDatabase)
for _, receipt := range receipts { for _, receipt := range receipts {
storageReceipt := (*types.ReceiptForStorage)(receipt) storageReceipt := (*types.ReceiptForStorage)(receipt)
bytes, err := rlp.EncodeToBytes(storageReceipt) bytes, err := rlp.EncodeToBytes(storageReceipt)
if err != nil { if err != nil {
return err return err
} }
if batchWrite {
batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
} else {
err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes) err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
if err != nil { if err != nil {
return err return err
} }
} }
}
if db, ok := db.(*ethdb.LDBDatabase); ok {
if err := db.LDB().Write(batch, nil); err != nil {
return err
}
}
return nil return nil
} }

View file

@ -257,7 +257,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
return nil return nil
} }
func (b Block) EncodeRLP(w io.Writer) error { func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{ return rlp.Encode(w, extblock{
Header: b.header, Header: b.header,
Txs: b.transactions, Txs: b.transactions,
@ -274,7 +274,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
return nil return nil
} }
func (b StorageBlock) EncodeRLP(w io.Writer) error { func (b *StorageBlock) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, storageblock{ return rlp.Encode(w, storageblock{
Header: b.header, Header: b.header,
Txs: b.transactions, Txs: b.transactions,

View file

@ -97,15 +97,6 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
return &Transaction{data: d} return &Transaction{data: d}
} }
func NewTransactionFromBytes(data []byte) *Transaction {
// TODO: remove this function if possible. callers would
// much better off decoding into transaction directly.
// it's not that hard.
tx := new(Transaction)
rlp.DecodeBytes(data, tx)
return tx
}
func (tx *Transaction) EncodeRLP(w io.Writer) error { func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data) return rlp.Encode(w, &tx.data)
} }
@ -298,3 +289,22 @@ type TxByNonce struct{ Transactions }
func (s TxByNonce) Less(i, j int) bool { func (s TxByNonce) Less(i, j int) bool {
return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce
} }
type TxByPrice struct{ Transactions }
func (s TxByPrice) Less(i, j int) bool {
return s.Transactions[i].data.Price.Cmp(s.Transactions[j].data.Price) > 0
}
type TxByPriceAndNonce struct{ Transactions }
func (s TxByPriceAndNonce) Less(i, j int) bool {
// we can ignore the error here. Sorting shouldn't care about validness
ifrom, _ := s.Transactions[i].From()
jfrom, _ := s.Transactions[j].From()
// favour nonce if they are from the same recipient
if ifrom == jfrom {
return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce
}
return s.Transactions[i].data.Price.Cmp(s.Transactions[j].data.Price) > 0
}

View file

@ -81,11 +81,9 @@ func doScheme(base, v []int) asn1.ObjectIdentifier {
type secgNamedCurve asn1.ObjectIdentifier type secgNamedCurve asn1.ObjectIdentifier
var ( var (
secgNamedCurveP224 = secgNamedCurve{1, 3, 132, 0, 33}
secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7} secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7}
secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34} secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34}
secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35} secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35}
rawCurveP224 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 3}
rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7} rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7}
rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4} rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4}
rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5} rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5}
@ -93,8 +91,6 @@ var (
func rawCurve(curve elliptic.Curve) []byte { func rawCurve(curve elliptic.Curve) []byte {
switch curve { switch curve {
case elliptic.P224():
return rawCurveP224
case elliptic.P256(): case elliptic.P256():
return rawCurveP256 return rawCurveP256
case elliptic.P384(): case elliptic.P384():
@ -120,8 +116,6 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
switch { switch {
case curve.Equal(secgNamedCurveP224):
return elliptic.P224()
case curve.Equal(secgNamedCurveP256): case curve.Equal(secgNamedCurveP256):
return elliptic.P256() return elliptic.P256()
case curve.Equal(secgNamedCurveP384): case curve.Equal(secgNamedCurveP384):
@ -134,8 +128,6 @@ func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) { func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) {
switch curve { switch curve {
case elliptic.P224():
return secgNamedCurveP224, true
case elliptic.P256(): case elliptic.P256():
return secgNamedCurveP256, true return secgNamedCurveP256, true
case elliptic.P384(): case elliptic.P384():
@ -248,7 +240,7 @@ var idEcPublicKeySupplemented = doScheme(idPublicKeyType, []int{0})
func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) { func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) {
switch curve { switch curve {
case elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(): case elliptic.P256(), elliptic.P384(), elliptic.P521():
raw := rawCurve(curve) raw := rawCurve(curve)
return asn1.RawValue{ return asn1.RawValue{
Tag: 30, Tag: 30,

View file

@ -407,11 +407,6 @@ type testCase struct {
} }
var testCases = []testCase{ var testCases = []testCase{
testCase{
Curve: elliptic.P224(),
Name: "P224",
Expected: false,
},
testCase{ testCase{
Curve: elliptic.P256(), Curve: elliptic.P256(),
Name: "P256", Name: "P256",

View file

@ -147,7 +147,6 @@ func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err
} }
func decryptKeyFromFile(keysDirPath string, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) { func decryptKeyFromFile(keysDirPath string, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
fmt.Printf("%v\n", keyAddr.Hex())
m := make(map[string]interface{}) m := make(map[string]interface{})
err = getKey(keysDirPath, keyAddr, &m) err = getKey(keysDirPath, keyAddr, &m)
if err != nil { if err != nil {

View file

@ -21,9 +21,11 @@ package secp256k1
/* /*
#cgo CFLAGS: -I./secp256k1 #cgo CFLAGS: -I./secp256k1
#cgo darwin CFLAGS: -I/usr/local/include #cgo darwin CFLAGS: -I/usr/local/include
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include #cgo linux,arm CFLAGS: -I/usr/local/arm/include
#cgo LDFLAGS: -lgmp #cgo LDFLAGS: -lgmp
#cgo darwin LDFLAGS: -L/usr/local/lib #cgo darwin LDFLAGS: -L/usr/local/lib
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo linux,arm LDFLAGS: -L/usr/local/arm/lib #cgo linux,arm LDFLAGS: -L/usr/local/arm/lib
#define USE_NUM_GMP #define USE_NUM_GMP
#define USE_FIELD_10X26 #define USE_FIELD_10X26

View file

@ -78,9 +78,14 @@ type Config struct {
GenesisNonce int GenesisNonce int
GenesisFile string GenesisFile string
GenesisBlock *types.Block // used by block tests GenesisBlock *types.Block // used by block tests
<<<<<<< HEAD
=======
Olympic bool
>>>>>>> 9a02f537260f64cc91a66074d920ae20b99b0a40
BlockChainVersion int BlockChainVersion int
SkipBcVersionCheck bool // e.g. blockchain export SkipBcVersionCheck bool // e.g. blockchain export
DatabaseCache int
DataDir string DataDir string
LogFile string LogFile string
@ -262,7 +267,7 @@ func New(config *Config) (*Ethereum, error) {
newdb := config.NewDB newdb := config.NewDB
if newdb == nil { if newdb == nil {
newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path) } newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
} }
blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain")) blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain"))
if err != nil { if err != nil {
@ -301,6 +306,17 @@ func New(config *Config) (*Ethereum, error) {
glog.V(logger.Info).Infof("Successfully wrote genesis block. New genesis hash = %x\n", block.Hash()) glog.V(logger.Info).Infof("Successfully wrote genesis block. New genesis hash = %x\n", block.Hash())
} }
<<<<<<< HEAD
=======
if config.Olympic {
_, err := core.WriteTestNetGenesisBlock(stateDb, blockDb, 42)
if err != nil {
return nil, err
}
glog.V(logger.Error).Infoln("Starting Olympic network")
}
>>>>>>> 9a02f537260f64cc91a66074d920ae20b99b0a40
// This is for testing only. // This is for testing only.
if config.GenesisBlock != nil { if config.GenesisBlock != nil {
core.WriteBlock(blockDb, config.GenesisBlock) core.WriteBlock(blockDb, config.GenesisBlock)

View file

@ -28,12 +28,13 @@ import (
"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/params"
) )
var ( var (
testdb, _ = ethdb.NewMemDatabase() testdb, _ = ethdb.NewMemDatabase()
genesis = core.GenesisBlockForTesting(testdb, common.Address{}, big.NewInt(0)) genesis = core.GenesisBlockForTesting(testdb, common.Address{}, big.NewInt(0))
unknownBlock = types.NewBlock(&types.Header{}, nil, nil, nil) unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil)
) )
// makeChain creates a chain of n blocks starting at and including parent. // makeChain creates a chain of n blocks starting at and including parent.

View file

@ -17,10 +17,16 @@
package ethdb package ethdb
import ( import (
<<<<<<< HEAD
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
=======
"path/filepath"
"strconv"
"strings"
>>>>>>> 9a02f537260f64cc91a66074d920ae20b99b0a40
"sync" "sync"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -37,6 +43,14 @@ import (
var OpenFileLimit = common.MaxOpenFileLimit() var OpenFileLimit = common.MaxOpenFileLimit()
// cacheRatio specifies how the total alloted cache is distributed between the
// various system databases.
var cacheRatio = map[string]float64{
"blockchain": 1.0 / 13.0,
"extra": 2.0 / 13.0,
"state": 10.0 / 13.0,
}
type LDBDatabase struct { type LDBDatabase struct {
fn string // filename for reporting fn string // filename for reporting
db *leveldb.DB // LevelDB instance db *leveldb.DB // LevelDB instance
@ -58,14 +72,24 @@ type LDBDatabase struct {
// NewLDBDatabase returns a LevelDB wrapped object. LDBDatabase does not persist data by // NewLDBDatabase returns a LevelDB wrapped object. LDBDatabase does not persist data by
// it self but requires a background poller which syncs every X. `Flush` should be called // it self but requires a background poller which syncs every X. `Flush` should be called
// when data needs to be stored and written to disk. // when data needs to be stored and written to disk.
func NewLDBDatabase(file string) (*LDBDatabase, error) { func NewLDBDatabase(file string, cache int) (*LDBDatabase, error) {
// Open the db // Calculate the cache allowance for this particular database
db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: OpenFileLimit}) cache = int(float64(cache) * cacheRatio[filepath.Base(file)])
// check for corruption and attempt to recover if cache < 16 {
if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { cache = 16
}
glog.V(logger.Info).Infof("Alloted %dMB cache to %s", cache, file)
// Open the db and recover any potential corruptions
db, err := leveldb.OpenFile(file, &opt.Options{
OpenFilesCacheCapacity: OpenFileLimit,
BlockCacheCapacity: cache / 2 * opt.MiB,
WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
})
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
db, err = leveldb.RecoverFile(file, nil) db, err = leveldb.RecoverFile(file, nil)
} }
// (re) check for errors and abort if opening of the db failed // (Re)check for errors and abort if opening of the db failed
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -28,8 +28,7 @@ func newDb() *LDBDatabase {
if common.FileExist(file) { if common.FileExist(file) {
os.RemoveAll(file) os.RemoveAll(file)
} }
db, _ := NewLDBDatabase(file, 0)
db, _ := NewLDBDatabase(file)
return db return db
} }

112
fdtrack/fdtrack.go Normal file
View file

@ -0,0 +1,112 @@
// 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 fdtrack logs statistics about open file descriptors.
package fdtrack
import (
"fmt"
"net"
"sort"
"sync"
"time"
"github.com/ethereum/go-ethereum/logger/glog"
)
var (
mutex sync.Mutex
all = make(map[string]int)
)
func Open(desc string) {
mutex.Lock()
all[desc] += 1
mutex.Unlock()
}
func Close(desc string) {
mutex.Lock()
defer mutex.Unlock()
if c, ok := all[desc]; ok {
if c == 1 {
delete(all, desc)
} else {
all[desc]--
}
}
}
func WrapListener(desc string, l net.Listener) net.Listener {
Open(desc)
return &wrappedListener{l, desc}
}
type wrappedListener struct {
net.Listener
desc string
}
func (w *wrappedListener) Accept() (net.Conn, error) {
c, err := w.Listener.Accept()
if err == nil {
c = WrapConn(w.desc, c)
}
return c, err
}
func (w *wrappedListener) Close() error {
err := w.Listener.Close()
if err == nil {
Close(w.desc)
}
return err
}
func WrapConn(desc string, conn net.Conn) net.Conn {
Open(desc)
return &wrappedConn{conn, desc}
}
type wrappedConn struct {
net.Conn
desc string
}
func (w *wrappedConn) Close() error {
err := w.Conn.Close()
if err == nil {
Close(w.desc)
}
return err
}
func Start() {
go func() {
for range time.Tick(15 * time.Second) {
mutex.Lock()
var sum, tracked = 0, []string{}
for what, n := range all {
sum += n
tracked = append(tracked, fmt.Sprintf("%s:%d", what, n))
}
mutex.Unlock()
used, _ := fdusage()
sort.Strings(tracked)
glog.Infof("fd usage %d/%d, tracked %d %v", used, fdlimit(), sum, tracked)
}
}()
}

29
fdtrack/fdusage.go Normal file
View file

@ -0,0 +1,29 @@
// 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/>.
// +build !linux,!darwin
package fdtrack
import "errors"
func fdlimit() int {
return 0
}
func fdusage() (int, error) {
return 0, errors.New("not implemented")
}

72
fdtrack/fdusage_darwin.go Normal file
View file

@ -0,0 +1,72 @@
// 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/>.
// +build darwin
package fdtrack
import (
"os"
"syscall"
"unsafe"
)
// #cgo CFLAGS: -lproc
// #include <libproc.h>
// #include <stdlib.h>
import "C"
func fdlimit() int {
var nofile syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
return 0
}
return int(nofile.Cur)
}
func fdusage() (int, error) {
pid := C.int(os.Getpid())
// Query for a rough estimate on the amout of data that
// proc_pidinfo will return.
rlen, err := C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, nil, 0)
if rlen <= 0 {
return 0, err
}
// Load the list of file descriptors. We don't actually care about
// the content, only about the size. Since the number of fds can
// change while we're reading them, the loop enlarges the buffer
// until proc_pidinfo says the result fitted.
var buf unsafe.Pointer
defer func() {
if buf != nil {
C.free(buf)
}
}()
for buflen := rlen; ; buflen *= 2 {
buf, err = C.reallocf(buf, C.size_t(buflen))
if buf == nil {
return 0, err
}
rlen, err = C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, buf, buflen)
if rlen <= 0 {
return 0, err
} else if rlen == buflen {
continue
}
return int(rlen / C.PROC_PIDLISTFD_SIZE), nil
}
panic("unreachable")
}

53
fdtrack/fdusage_linux.go Normal file
View file

@ -0,0 +1,53 @@
// 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/>.
// +build linux
package fdtrack
import (
"io"
"os"
"syscall"
)
func fdlimit() int {
var nofile syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
return 0
}
return int(nofile.Cur)
}
func fdusage() (int, error) {
f, err := os.Open("/proc/self/fd")
if err != nil {
return 0, err
}
defer f.Close()
const batchSize = 100
n := 0
for {
list, err := f.Readdirnames(batchSize)
n += len(list)
if err == io.EOF {
break
} else if err != nil {
return 0, err
}
}
return n, nil
}

View file

@ -34,6 +34,7 @@ func ReadDiskStats(stats *DiskStats) error {
if err != nil { if err != nil {
return err return err
} }
defer inf.Close()
in := bufio.NewReader(inf) in := bufio.NewReader(inf)
// Iterate over the IO counter, and extract what we need // Iterate over the IO counter, and extract what we need

View file

@ -20,7 +20,6 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"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"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
@ -29,10 +28,10 @@ import (
type CpuAgent struct { type CpuAgent struct {
mu sync.Mutex mu sync.Mutex
workCh chan *types.Block workCh chan *Work
quit chan struct{} quit chan struct{}
quitCurrentOp chan struct{} quitCurrentOp chan struct{}
returnCh chan<- *types.Block returnCh chan<- *Result
index int index int
pow pow.PoW pow pow.PoW
@ -47,9 +46,9 @@ func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
return miner return miner
} }
func (self *CpuAgent) Work() chan<- *types.Block { return self.workCh } func (self *CpuAgent) Work() chan<- *Work { return self.workCh }
func (self *CpuAgent) Pow() pow.PoW { return self.pow } func (self *CpuAgent) Pow() pow.PoW { return self.pow }
func (self *CpuAgent) SetReturnCh(ch chan<- *types.Block) { self.returnCh = ch } func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }
func (self *CpuAgent) Stop() { func (self *CpuAgent) Stop() {
self.mu.Lock() self.mu.Lock()
@ -65,7 +64,7 @@ func (self *CpuAgent) Start() {
self.quit = make(chan struct{}) self.quit = make(chan struct{})
// creating current op ch makes sure we're not closing a nil ch // creating current op ch makes sure we're not closing a nil ch
// later on // later on
self.workCh = make(chan *types.Block, 1) self.workCh = make(chan *Work, 1)
go self.update() go self.update()
} }
@ -74,13 +73,13 @@ func (self *CpuAgent) update() {
out: out:
for { for {
select { select {
case block := <-self.workCh: case work := <-self.workCh:
self.mu.Lock() self.mu.Lock()
if self.quitCurrentOp != nil { if self.quitCurrentOp != nil {
close(self.quitCurrentOp) close(self.quitCurrentOp)
} }
self.quitCurrentOp = make(chan struct{}) self.quitCurrentOp = make(chan struct{})
go self.mine(block, self.quitCurrentOp) go self.mine(work, self.quitCurrentOp)
self.mu.Unlock() self.mu.Unlock()
case <-self.quit: case <-self.quit:
self.mu.Lock() self.mu.Lock()
@ -106,13 +105,14 @@ done:
} }
} }
func (self *CpuAgent) mine(block *types.Block, stop <-chan struct{}) { func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index) glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)
// Mine // Mine
nonce, mixDigest := self.pow.Search(block, stop) nonce, mixDigest := self.pow.Search(work.Block, stop)
if nonce != 0 { if nonce != 0 {
self.returnCh <- block.WithMiningResult(nonce, common.BytesToHash(mixDigest)) block := work.Block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
self.returnCh <- &Result{work, block}
} else { } else {
self.returnCh <- nil self.returnCh <- nil
} }

View file

@ -18,39 +18,44 @@ package miner
import ( import (
"math/big" "math/big"
"sync"
"time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
) )
type RemoteAgent struct { type RemoteAgent struct {
work *types.Block mu sync.Mutex
currentWork *types.Block
quit chan struct{} quit chan struct{}
workCh chan *types.Block workCh chan *Work
returnCh chan<- *types.Block returnCh chan<- *Result
currentWork *Work
work map[common.Hash]*Work
} }
func NewRemoteAgent() *RemoteAgent { func NewRemoteAgent() *RemoteAgent {
agent := &RemoteAgent{} agent := &RemoteAgent{work: make(map[common.Hash]*Work)}
return agent return agent
} }
func (a *RemoteAgent) Work() chan<- *types.Block { func (a *RemoteAgent) Work() chan<- *Work {
return a.workCh return a.workCh
} }
func (a *RemoteAgent) SetReturnCh(returnCh chan<- *types.Block) { func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) {
a.returnCh = returnCh a.returnCh = returnCh
} }
func (a *RemoteAgent) Start() { func (a *RemoteAgent) Start() {
a.quit = make(chan struct{}) a.quit = make(chan struct{})
a.workCh = make(chan *types.Block, 1) a.workCh = make(chan *Work, 1)
go a.run() go a.maintainLoop()
} }
func (a *RemoteAgent) Stop() { func (a *RemoteAgent) Stop() {
@ -60,47 +65,72 @@ func (a *RemoteAgent) Stop() {
func (a *RemoteAgent) GetHashRate() int64 { return 0 } func (a *RemoteAgent) GetHashRate() int64 { return 0 }
func (a *RemoteAgent) run() { func (a *RemoteAgent) GetWork() [3]string {
a.mu.Lock()
defer a.mu.Unlock()
var res [3]string
if a.currentWork != nil {
block := a.currentWork.Block
res[0] = block.HashNoNonce().Hex()
seedHash, _ := ethash.GetSeedHash(block.NumberU64())
res[1] = common.BytesToHash(seedHash).Hex()
// Calculate the "target" to be returned to the external miner
n := big.NewInt(1)
n.Lsh(n, 255)
n.Div(n, block.Difficulty())
n.Lsh(n, 1)
res[2] = common.BytesToHash(n.Bytes()).Hex()
a.work[block.HashNoNonce()] = a.currentWork
}
return res
}
// Returns true or false, but does not indicate if the PoW was correct
func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, hash common.Hash) bool {
a.mu.Lock()
defer a.mu.Unlock()
// Make sure the work submitted is present
if a.work[hash] != nil {
block := a.work[hash].Block.WithMiningResult(nonce, mixDigest)
a.returnCh <- &Result{a.work[hash], block}
delete(a.work, hash)
return true
} else {
glog.V(logger.Info).Infof("Work was submitted for %x but no pending work found\n", hash)
}
return false
}
func (a *RemoteAgent) maintainLoop() {
ticker := time.Tick(5 * time.Second)
out: out:
for { for {
select { select {
case <-a.quit: case <-a.quit:
break out break out
case work := <-a.workCh: case work := <-a.workCh:
a.work = work a.mu.Lock()
a.currentWork = work
a.mu.Unlock()
case <-ticker:
// cleanup
a.mu.Lock()
for hash, work := range a.work {
if time.Since(work.createdAt) > 7*(12*time.Second) {
delete(a.work, hash)
}
}
a.mu.Unlock()
} }
} }
} }
func (a *RemoteAgent) GetWork() [3]string {
var res [3]string
if a.work != nil {
a.currentWork = a.work
res[0] = a.work.HashNoNonce().Hex()
seedHash, _ := ethash.GetSeedHash(a.currentWork.NumberU64())
res[1] = common.BytesToHash(seedHash).Hex()
// Calculate the "target" to be returned to the external miner
n := big.NewInt(1)
n.Lsh(n, 255)
n.Div(n, a.work.Difficulty())
n.Lsh(n, 1)
res[2] = common.BytesToHash(n.Bytes()).Hex()
}
return res
}
func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, seedHash common.Hash) bool {
// Return true or false, but does not indicate if the PoW was correct
// Make sure the external miner was working on the right hash
if a.currentWork != nil && a.work != nil {
a.returnCh <- a.currentWork.WithMiningResult(nonce, mixDigest)
//a.returnCh <- Work{a.currentWork.Number().Uint64(), nonce, mixDigest.Bytes(), seedHash.Bytes()}
return true
}
return false
}

View file

@ -38,25 +38,20 @@ import (
var jsonlogger = logger.NewJsonLogger() var jsonlogger = logger.NewJsonLogger()
// Work holds the current work const (
type Work struct { resultQueueSize = 10
Number uint64 miningLogAtDepth = 5
Nonce uint64 )
MixDigest []byte
SeedHash []byte
}
// Agent can register themself with the worker // Agent can register themself with the worker
type Agent interface { type Agent interface {
Work() chan<- *types.Block Work() chan<- *Work
SetReturnCh(chan<- *types.Block) SetReturnCh(chan<- *Result)
Stop() Stop()
Start() Start()
GetHashRate() int64 GetHashRate() int64
} }
const miningLogAtDepth = 5
type uint64RingBuffer struct { type uint64RingBuffer struct {
ints []uint64 //array of all integers in buffer ints []uint64 //array of all integers in buffer
next int //where is the next insertion? assert 0 <= next < len(ints) next int //where is the next insertion? assert 0 <= next < len(ints)
@ -64,7 +59,7 @@ type uint64RingBuffer struct {
// environment is the workers current environment and holds // environment is the workers current environment and holds
// all of the current state information // all of the current state information
type environment struct { type Work struct {
state *state.StateDB // apply state changes here state *state.StateDB // apply state changes here
coinbase *state.StateObject // the miner's account coinbase *state.StateObject // the miner's account
ancestors *set.Set // ancestor set (used for checking uncle parent validity) ancestors *set.Set // ancestor set (used for checking uncle parent validity)
@ -78,11 +73,18 @@ type environment struct {
lowGasTxs types.Transactions lowGasTxs types.Transactions
localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
block *types.Block // the new block Block *types.Block // the new block
header *types.Header header *types.Header
txs []*types.Transaction txs []*types.Transaction
receipts []*types.Receipt receipts []*types.Receipt
createdAt time.Time
}
type Result struct {
Work *Work
Block *types.Block
} }
// worker is the main object which takes care of applying messages to the new state // worker is the main object which takes care of applying messages to the new state
@ -90,7 +92,7 @@ type worker struct {
mu sync.Mutex mu sync.Mutex
agents []Agent agents []Agent
recv chan *types.Block recv chan *Result
mux *event.TypeMux mux *event.TypeMux
quit chan struct{} quit chan struct{}
pow pow.PoW pow pow.PoW
@ -105,7 +107,7 @@ type worker struct {
extra []byte extra []byte
currentMu sync.Mutex currentMu sync.Mutex
current *environment current *Work
uncleMu sync.Mutex uncleMu sync.Mutex
possibleUncles map[common.Hash]*types.Block possibleUncles map[common.Hash]*types.Block
@ -116,6 +118,8 @@ type worker struct {
// atomic status counters // atomic status counters
mining int32 mining int32
atWork int32 atWork int32
fullValidation bool
} }
func newWorker(coinbase common.Address, eth core.Backend) *worker { func newWorker(coinbase common.Address, eth core.Backend) *worker {
@ -123,7 +127,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
eth: eth, eth: eth,
mux: eth.EventMux(), mux: eth.EventMux(),
extraDb: eth.ExtraDb(), extraDb: eth.ExtraDb(),
recv: make(chan *types.Block), recv: make(chan *Result, resultQueueSize),
gasPrice: new(big.Int), gasPrice: new(big.Int),
chain: eth.ChainManager(), chain: eth.ChainManager(),
proc: eth.BlockProcessor(), proc: eth.BlockProcessor(),
@ -131,6 +135,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
coinbase: coinbase, coinbase: coinbase,
txQueue: make(map[common.Hash]*types.Transaction), txQueue: make(map[common.Hash]*types.Transaction),
quit: make(chan struct{}), quit: make(chan struct{}),
fullValidation: false,
} }
go worker.update() go worker.update()
go worker.wait() go worker.wait()
@ -164,7 +169,7 @@ func (self *worker) pendingBlock() *types.Block {
self.current.receipts, self.current.receipts,
) )
} }
return self.current.block return self.current.Block
} }
func (self *worker) start() { func (self *worker) start() {
@ -251,13 +256,23 @@ func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (
func (self *worker) wait() { func (self *worker) wait() {
for { for {
for block := range self.recv { for result := range self.recv {
atomic.AddInt32(&self.atWork, -1) atomic.AddInt32(&self.atWork, -1)
if block == nil { if result == nil {
continue continue
} }
block := result.Block
work := result.Work
work.state.Sync()
if self.fullValidation {
if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil {
glog.V(logger.Error).Infoln("mining err", err)
continue
}
go self.mux.Post(core.NewMinedBlockEvent{block})
} else {
parent := self.chain.GetBlock(block.ParentHash()) parent := self.chain.GetBlock(block.ParentHash())
if parent == nil { if parent == nil {
glog.V(logger.Error).Infoln("Invalid block found during mining") glog.V(logger.Error).Infoln("Invalid block found during mining")
@ -278,21 +293,9 @@ func (self *worker) wait() {
// This puts transactions in a extra db for rpc // This puts transactions in a extra db for rpc
core.PutTransactions(self.extraDb, block, block.Transactions()) core.PutTransactions(self.extraDb, block, block.Transactions())
// store the receipts // store the receipts
core.PutReceipts(self.extraDb, self.current.receipts) core.PutReceipts(self.extraDb, work.receipts)
} }
// check staleness and display confirmation
var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale "
} else {
confirm = "Wait 5 blocks for confirmation"
self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks)
}
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
// broadcast before waiting for validation // broadcast before waiting for validation
go func(block *types.Block, logs state.Logs) { go func(block *types.Block, logs state.Logs) {
self.mux.Post(core.NewMinedBlockEvent{block}) self.mux.Post(core.NewMinedBlockEvent{block})
@ -301,16 +304,28 @@ func (self *worker) wait() {
self.mux.Post(core.ChainHeadEvent{block}) self.mux.Post(core.ChainHeadEvent{block})
self.mux.Post(logs) self.mux.Post(logs)
} }
}(block, self.current.state.Logs()) }(block, work.state.Logs())
}
// check staleness and display confirmation
var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale "
} else {
confirm = "Wait 5 blocks for confirmation"
work.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), work.localMinedBlocks)
}
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
self.commitNewWork() self.commitNewWork()
} }
} }
} }
func (self *worker) push() { func (self *worker) push(work *Work) {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
if core.Canary(self.current.state) { if core.Canary(work.state) {
glog.Infoln("Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--") glog.Infoln("Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--")
glog.Infoln("You turn back and abort mining") glog.Infoln("You turn back and abort mining")
return return
@ -321,7 +336,7 @@ func (self *worker) push() {
atomic.AddInt32(&self.atWork, 1) atomic.AddInt32(&self.atWork, 1)
if agent.Work() != nil { if agent.Work() != nil {
agent.Work() <- self.current.block agent.Work() <- work
} }
} }
} }
@ -330,35 +345,36 @@ func (self *worker) push() {
// makeCurrent creates a new environment for the current cycle. // makeCurrent creates a new environment for the current cycle.
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) { func (self *worker) makeCurrent(parent *types.Block, header *types.Header) {
state := state.New(parent.Root(), self.eth.StateDb()) state := state.New(parent.Root(), self.eth.StateDb())
current := &environment{ work := &Work{
state: state, state: state,
ancestors: set.New(), ancestors: set.New(),
family: set.New(), family: set.New(),
uncles: set.New(), uncles: set.New(),
header: header, header: header,
coinbase: state.GetOrNewStateObject(self.coinbase), coinbase: state.GetOrNewStateObject(self.coinbase),
createdAt: time.Now(),
} }
// when 08 is processed ancestors contain 07 (quick block) // when 08 is processed ancestors contain 07 (quick block)
for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) {
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {
current.family.Add(uncle.Hash()) work.family.Add(uncle.Hash())
} }
current.family.Add(ancestor.Hash()) work.family.Add(ancestor.Hash())
current.ancestors.Add(ancestor.Hash()) work.ancestors.Add(ancestor.Hash())
} }
accounts, _ := self.eth.AccountManager().Accounts() accounts, _ := self.eth.AccountManager().Accounts()
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
current.remove = set.New() work.remove = set.New()
current.tcount = 0 work.tcount = 0
current.ignoredTransactors = set.New() work.ignoredTransactors = set.New()
current.lowGasTransactors = set.New() work.lowGasTransactors = set.New()
current.ownedAccounts = accountAddressesSet(accounts) work.ownedAccounts = accountAddressesSet(accounts)
if self.current != nil { if self.current != nil {
current.localMinedBlocks = self.current.localMinedBlocks work.localMinedBlocks = self.current.localMinedBlocks
} }
self.current = current self.current = work
} }
func (w *worker) setGasPrice(p *big.Int) { func (w *worker) setGasPrice(p *big.Int) {
@ -372,13 +388,13 @@ func (w *worker) setGasPrice(p *big.Int) {
w.mux.Post(core.GasPriceChanged{w.gasPrice}) w.mux.Post(core.GasPriceChanged{w.gasPrice})
} }
func (self *worker) isBlockLocallyMined(deepBlockNum uint64) bool { func (self *worker) isBlockLocallyMined(current *Work, deepBlockNum uint64) bool {
//Did this instance mine a block at {deepBlockNum} ? //Did this instance mine a block at {deepBlockNum} ?
var isLocal = false var isLocal = false
for idx, blockNum := range self.current.localMinedBlocks.ints { for idx, blockNum := range current.localMinedBlocks.ints {
if deepBlockNum == blockNum { if deepBlockNum == blockNum {
isLocal = true isLocal = true
self.current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs
break break
} }
} }
@ -392,12 +408,12 @@ func (self *worker) isBlockLocallyMined(deepBlockNum uint64) bool {
return block != nil && block.Coinbase() == self.coinbase return block != nil && block.Coinbase() == self.coinbase
} }
func (self *worker) logLocalMinedBlocks(previous *environment) { func (self *worker) logLocalMinedBlocks(current, previous *Work) {
if previous != nil && self.current.localMinedBlocks != nil { if previous != nil && current.localMinedBlocks != nil {
nextBlockNum := self.current.block.NumberU64() nextBlockNum := current.Block.NumberU64()
for checkBlockNum := previous.block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ { for checkBlockNum := previous.Block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ {
inspectBlockNum := checkBlockNum - miningLogAtDepth inspectBlockNum := checkBlockNum - miningLogAtDepth
if self.isBlockLocallyMined(inspectBlockNum) { if self.isBlockLocallyMined(current, inspectBlockNum) {
glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum) glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum)
} }
} }
@ -429,7 +445,7 @@ func (self *worker) commitNewWork() {
header := &types.Header{ header := &types.Header{
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1), Number: num.Add(num, common.Big1),
Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time(), parent.Difficulty()), Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time(), parent.Number(), parent.Difficulty()),
GasLimit: core.CalcGasLimit(parent), GasLimit: core.CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Coinbase: self.coinbase, Coinbase: self.coinbase,
@ -439,14 +455,47 @@ func (self *worker) commitNewWork() {
previous := self.current previous := self.current
self.makeCurrent(parent, header) self.makeCurrent(parent, header)
current := self.current work := self.current
// commit transactions for this run. /* //approach 1
transactions := self.eth.TxPool().GetTransactions() transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByNonce{transactions}) sort.Sort(types.TxByNonce{transactions})
current.coinbase.SetGasLimit(header.GasLimit) */
current.commitTransactions(transactions, self.gasPrice, self.proc)
self.eth.TxPool().RemoveTransactions(current.lowGasTxs) //approach 2
transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByPriceAndNonce{transactions})
/* // approach 3
// commit transactions for this run.
txPerOwner := make(map[common.Address]types.Transactions)
// Sort transactions by owner
for _, tx := range self.eth.TxPool().GetTransactions() {
from, _ := tx.From() // we can ignore the sender error
txPerOwner[from] = append(txPerOwner[from], tx)
}
var (
singleTxOwner types.Transactions
multiTxOwner types.Transactions
)
// Categorise transactions by
// 1. 1 owner tx per block
// 2. multi txs owner per block
for _, txs := range txPerOwner {
if len(txs) == 1 {
singleTxOwner = append(singleTxOwner, txs[0])
} else {
multiTxOwner = append(multiTxOwner, txs...)
}
}
sort.Sort(types.TxByPrice{singleTxOwner})
sort.Sort(types.TxByNonce{multiTxOwner})
transactions := append(singleTxOwner, multiTxOwner...)
*/
work.coinbase.SetGasLimit(header.GasLimit)
work.commitTransactions(transactions, self.gasPrice, self.proc)
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
// compute uncles for the new block. // compute uncles for the new block.
var ( var (
@ -457,7 +506,7 @@ func (self *worker) commitNewWork() {
if len(uncles) == 2 { if len(uncles) == 2 {
break break
} }
if err := self.commitUncle(uncle.Header()); err != nil { if err := self.commitUncle(work, uncle.Header()); err != nil {
if glog.V(logger.Ridiculousness) { if glog.V(logger.Ridiculousness) {
glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4])
glog.V(logger.Detail).Infoln(uncle) glog.V(logger.Detail).Infoln(uncle)
@ -474,41 +523,40 @@ func (self *worker) commitNewWork() {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
// commit state root after all state transitions. // commit state root after all state transitions.
core.AccumulateRewards(self.current.state, header, uncles) core.AccumulateRewards(work.state, header, uncles)
current.state.SyncObjects() work.state.SyncObjects()
self.current.state.Sync() header.Root = work.state.Root()
header.Root = current.state.Root()
} }
// create the new block whose nonce will be mined. // create the new block whose nonce will be mined.
current.block = types.NewBlock(header, current.txs, uncles, current.receipts) work.Block = types.NewBlock(header, work.txs, uncles, work.receipts)
self.current.block.Td = new(big.Int).Set(core.CalcTD(self.current.block, self.chain.GetBlock(self.current.block.ParentHash()))) work.Block.Td = new(big.Int).Set(core.CalcTD(work.Block, self.chain.GetBlock(work.Block.ParentHash())))
// 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. Took %v\n", current.block.Number(), current.tcount, len(uncles), time.Since(tstart)) glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart))
self.logLocalMinedBlocks(previous) self.logLocalMinedBlocks(work, previous)
} }
self.push() self.push(work)
} }
func (self *worker) commitUncle(uncle *types.Header) error { func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
hash := uncle.Hash() hash := uncle.Hash()
if self.current.uncles.Has(hash) { if work.uncles.Has(hash) {
return core.UncleError("Uncle not unique") return core.UncleError("Uncle not unique")
} }
if !self.current.ancestors.Has(uncle.ParentHash) { if !work.ancestors.Has(uncle.ParentHash) {
return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
} }
if self.current.family.Has(hash) { if work.family.Has(hash) {
return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash))
} }
self.current.uncles.Add(uncle.Hash()) work.uncles.Add(uncle.Hash())
return nil return nil
} }
func (env *environment) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) { func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) {
for _, tx := range transactions { for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool // We can skip err. It has already been validated in the tx pool
from, _ := tx.From() from, _ := tx.From()
@ -566,7 +614,7 @@ func (env *environment) commitTransactions(transactions types.Transactions, gasP
} }
} }
func (env *environment) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error { func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error {
snap := env.state.Copy() snap := env.state.Copy()
receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true) receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true)
if err != nil { if err != nil {

View file

@ -23,6 +23,7 @@ import (
"net" "net"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
"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"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
@ -212,6 +213,7 @@ func (t *dialTask) Do(srv *Server) {
glog.V(logger.Detail).Infof("dial error: %v", err) glog.V(logger.Detail).Infof("dial error: %v", err)
return return
} }
fd = fdtrack.WrapConn("p2p", fd)
mfd := newMeteredConn(fd, false) mfd := newMeteredConn(fd, false)
srv.setupConn(mfd, t.flags, t.dest) srv.setupConn(mfd, t.flags, t.dest)

View file

@ -25,6 +25,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/fdtrack"
"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"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
@ -197,6 +198,7 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
if err != nil { if err != nil {
return nil, err return nil, err
} }
fdtrack.Open("p2p")
conn, err := net.ListenUDP("udp", addr) conn, err := net.ListenUDP("udp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -234,6 +236,7 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
func (t *udp) close() { func (t *udp) close() {
close(t.closing) close(t.closing)
fdtrack.Close("p2p")
t.conn.Close() t.conn.Close()
// TODO: wait for the loops to end. // TODO: wait for the loops to end.
} }

View file

@ -34,7 +34,7 @@ var (
// meteredConn is a wrapper around a network TCP connection that meters both the // meteredConn is a wrapper around a network TCP connection that meters both the
// inbound and outbound network traffic. // inbound and outbound network traffic.
type meteredConn struct { type meteredConn struct {
*net.TCPConn // Network connection to wrap with metering net.Conn
} }
// newMeteredConn creates a new metered connection, also bumping the ingress or // newMeteredConn creates a new metered connection, also bumping the ingress or
@ -45,13 +45,13 @@ func newMeteredConn(conn net.Conn, ingress bool) net.Conn {
} else { } else {
egressConnectMeter.Mark(1) egressConnectMeter.Mark(1)
} }
return &meteredConn{conn.(*net.TCPConn)} return &meteredConn{conn}
} }
// Read delegates a network read to the underlying connection, bumping the ingress // Read delegates a network read to the underlying connection, bumping the ingress
// traffic meter along the way. // traffic meter along the way.
func (c *meteredConn) Read(b []byte) (n int, err error) { func (c *meteredConn) Read(b []byte) (n int, err error) {
n, err = c.TCPConn.Read(b) n, err = c.Conn.Read(b)
ingressTrafficMeter.Mark(int64(n)) ingressTrafficMeter.Mark(int64(n))
return return
} }
@ -59,7 +59,7 @@ func (c *meteredConn) Read(b []byte) (n int, err error) {
// Write delegates a network write to the underlying connection, bumping the // Write delegates a network write to the underlying connection, bumping the
// egress traffic meter along the way. // egress traffic meter along the way.
func (c *meteredConn) Write(b []byte) (n int, err error) { func (c *meteredConn) Write(b []byte) (n int, err error) {
n, err = c.TCPConn.Write(b) n, err = c.Conn.Write(b)
egressTrafficMeter.Mark(int64(n)) egressTrafficMeter.Mark(int64(n))
return return
} }

View file

@ -25,6 +25,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
"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"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
@ -372,7 +373,7 @@ func (srv *Server) startListening() error {
} }
laddr := listener.Addr().(*net.TCPAddr) laddr := listener.Addr().(*net.TCPAddr)
srv.ListenAddr = laddr.String() srv.ListenAddr = laddr.String()
srv.listener = listener srv.listener = fdtrack.WrapListener("p2p", listener)
srv.loopWG.Add(1) srv.loopWG.Add(1)
go srv.listenLoop() go srv.listenLoop()
// Map the TCP listening port if NAT is configured. // Map the TCP listening port if NAT is configured.

View file

@ -39,8 +39,13 @@ var (
EcrecoverGas = big.NewInt(3000) // EcrecoverGas = big.NewInt(3000) //
Sha256WordGas = big.NewInt(12) // Sha256WordGas = big.NewInt(12) //
<<<<<<< HEAD
MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
GenesisGasLimit = big.NewInt(5000) // Gas limit of the Genesis block. GenesisGasLimit = big.NewInt(5000) // Gas limit of the Genesis block.
=======
MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
>>>>>>> 9a02f537260f64cc91a66074d920ae20b99b0a40
Sha3Gas = big.NewInt(30) // Once per SHA3 operation. Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
Sha256Gas = big.NewInt(60) // Sha256Gas = big.NewInt(60) //

View file

@ -935,9 +935,9 @@ func TestCallArgsNotStrings(t *testing.T) {
func TestCallArgsToEmpty(t *testing.T) { func TestCallArgsToEmpty(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]`
args := new(CallArgs) args := new(CallArgs)
str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) err := json.Unmarshal([]byte(input), &args)
if len(str) > 0 { if err != nil {
t.Error(str) t.Error("Did not expect error. Got", err)
} }
} }

View file

@ -21,8 +21,9 @@ import (
"encoding/json" "encoding/json"
"math/big" "math/big"
"fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
@ -322,7 +323,7 @@ func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
if len(gas) == 0 { if len(gas) == 0 {
return newHexNum(0), nil return newHexNum(0), nil
} else { } else {
return newHexNum(gas), nil return newHexNum(common.String2Big(gas)), err
} }
} }
@ -578,14 +579,17 @@ func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
ret, err := self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data) from := common.HexToAddress(args.Tx.From)
if err != nil {
return nil, err pending := self.ethereum.TxPool().GetTransactions()
for _, p := range pending {
if pFrom, err := p.From(); err == nil && pFrom == from && p.SigHash() == args.Tx.tx.SigHash() {
self.ethereum.TxPool().RemoveTx(common.HexToHash(args.Tx.Hash))
return self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data)
}
} }
self.ethereum.TxPool().RemoveTransactions(types.Transactions{args.Tx.tx}) return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash)
return ret, nil
} }
func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) { func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) {

View file

@ -469,10 +469,6 @@ func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
} }
args.From = ext.From args.From = ext.From
if len(ext.To) == 0 {
return shared.NewValidationError("to", "is required")
}
args.To = ext.To args.To = ext.To
var num *big.Int var num *big.Int
@ -888,6 +884,7 @@ type tx struct {
Data string Data string
GasLimit string GasLimit string
GasPrice string GasPrice string
Hash string
} }
func newTx(t *types.Transaction) *tx { func newTx(t *types.Transaction) *tx {
@ -906,6 +903,7 @@ func newTx(t *types.Transaction) *tx {
Data: "0x" + common.Bytes2Hex(t.Data()), Data: "0x" + common.Bytes2Hex(t.Data()),
GasLimit: t.Gas().String(), GasLimit: t.Gas().String(),
GasPrice: t.GasPrice().String(), GasPrice: t.GasPrice().String(),
Hash: t.Hash().Hex(),
} }
} }
@ -931,6 +929,12 @@ func (tx *tx) UnmarshalJSON(b []byte) (err error) {
contractCreation = true contractCreation = true
) )
if val, found := fields["Hash"]; found {
if hashVal, ok := val.(string); ok {
tx.Hash = hashVal
}
}
if val, found := fields["To"]; found { if val, found := fields["To"]; found {
if strVal, ok := val.(string); ok && len(strVal) > 0 { if strVal, ok := val.(string); ok && len(strVal) > 0 {
tx.To = strVal tx.To = strVal

View file

@ -32,16 +32,22 @@ var (
AutoCompletion = map[string][]string{ AutoCompletion = map[string][]string{
"admin": []string{ "admin": []string{
"addPeer", "addPeer",
"peers",
"nodeInfo",
"exportChain",
"importChain",
"verbosity",
"chainSyncStatus", "chainSyncStatus",
"setSolc",
"datadir", "datadir",
"exportChain",
"getContractInfo",
"importChain",
"nodeInfo",
"peers",
"register",
"registerUrl",
"setSolc",
"sleepBlocks",
"startNatSpec",
"startRPC", "startRPC",
"stopNatSpec",
"stopRPC", "stopRPC",
"verbosity",
}, },
"db": []string{ "db": []string{
"getString", "getString",
@ -97,6 +103,7 @@ var (
"miner": []string{ "miner": []string{
"hashrate", "hashrate",
"makeDAG", "makeDAG",
"setEtherbase",
"setExtra", "setExtra",
"setGasPrice", "setGasPrice",
"startAutoDAG", "startAutoDAG",

View file

@ -17,13 +17,19 @@
package comms package comms
import ( import (
"encoding/json"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strings" "strings"
"sync"
"time"
"bytes" "bytes"
"io"
"io/ioutil" "io/ioutil"
"github.com/ethereum/go-ethereum/fdtrack"
"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"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -31,10 +37,15 @@ import (
"github.com/rs/cors" "github.com/rs/cors"
) )
const (
serverIdleTimeout = 10 * time.Second // idle keep-alive connections
serverReadTimeout = 15 * time.Second // per-request read timeout
serverWriteTimeout = 15 * time.Second // per-request read timeout
)
var ( var (
// main HTTP rpc listener httpServerMu sync.Mutex
httpListener *stoppableTCPListener httpServer *stopServer
listenerStoppedError = fmt.Errorf("Listener has stopped")
) )
type HttpConfig struct { type HttpConfig struct {
@ -43,42 +54,172 @@ type HttpConfig struct {
CorsDomain string CorsDomain string
} }
// stopServer augments http.Server with idle connection tracking.
// Idle keep-alive connections are shut down when Close is called.
type stopServer struct {
*http.Server
l net.Listener
// connection tracking state
mu sync.Mutex
shutdown bool // true when Stop has returned
idle map[net.Conn]struct{}
}
type handler struct {
codec codec.Codec
api shared.EthereumApi
}
// StartHTTP starts listening for RPC requests sent via HTTP.
func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error { func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error {
if httpListener != nil { httpServerMu.Lock()
if fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) != httpListener.Addr().String() { defer httpServerMu.Unlock()
return fmt.Errorf("RPC service already running on %s ", httpListener.Addr().String())
addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort)
if httpServer != nil {
if addr != httpServer.Addr {
return fmt.Errorf("RPC service already running on %s ", httpServer.Addr)
} }
return nil // RPC service already running on given host/port return nil // RPC service already running on given host/port
} }
// Set up the request handler, wrapping it with CORS headers if configured.
l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort)) handler := http.Handler(&handler{codec, api})
if len(cfg.CorsDomain) > 0 {
opts := cors.Options{
AllowedMethods: []string{"POST"},
AllowedOrigins: strings.Split(cfg.CorsDomain, " "),
}
handler = cors.New(opts).Handler(handler)
}
// Start the server.
s, err := listenHTTP(addr, handler)
if err != nil { if err != nil {
glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err) glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err)
return err return err
} }
httpListener = l httpServer = s
var handler http.Handler
if len(cfg.CorsDomain) > 0 {
var opts cors.Options
opts.AllowedMethods = []string{"POST"}
opts.AllowedOrigins = strings.Split(cfg.CorsDomain, " ")
c := cors.New(opts)
handler = newStoppableHandler(c.Handler(gethHttpHandler(codec, api)), l.stop)
} else {
handler = newStoppableHandler(gethHttpHandler(codec, api), l.stop)
}
go http.Serve(l, handler)
return nil return nil
} }
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
// Limit request size to resist DoS
if req.ContentLength > maxHttpSizeReqLength {
err := fmt.Errorf("Request too large")
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
sendJSON(w, &response)
return
}
defer req.Body.Close()
payload, err := ioutil.ReadAll(req.Body)
if err != nil {
err := fmt.Errorf("Could not read request body")
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
sendJSON(w, &response)
return
}
c := h.codec.New(nil)
var rpcReq shared.Request
if err = c.Decode(payload, &rpcReq); err == nil {
reply, err := h.api.Execute(&rpcReq)
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
sendJSON(w, &res)
return
}
var reqBatch []shared.Request
if err = c.Decode(payload, &reqBatch); err == nil {
resBatch := make([]*interface{}, len(reqBatch))
resCount := 0
for i, rpcReq := range reqBatch {
reply, err := h.api.Execute(&rpcReq)
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
resCount += 1
}
}
// make response omitting nil entries
sendJSON(w, resBatch[:resCount])
return
}
// invalid request
err = fmt.Errorf("Could not decode request")
res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err)
sendJSON(w, res)
}
func sendJSON(w io.Writer, v interface{}) {
if glog.V(logger.Detail) {
if payload, err := json.MarshalIndent(v, "", "\t"); err == nil {
glog.Infof("Sending payload: %s", payload)
}
}
if err := json.NewEncoder(w).Encode(v); err != nil {
glog.V(logger.Error).Infoln("Error sending JSON:", err)
}
}
// Stop closes all active HTTP connections and shuts down the server.
func StopHttp() { func StopHttp() {
if httpListener != nil { httpServerMu.Lock()
httpListener.Stop() defer httpServerMu.Unlock()
httpListener = nil if httpServer != nil {
httpServer.Close()
httpServer = nil
}
}
func listenHTTP(addr string, h http.Handler) (*stopServer, error) {
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
l = fdtrack.WrapListener("rpc", l)
s := &stopServer{l: l, idle: make(map[net.Conn]struct{})}
s.Server = &http.Server{
Addr: addr,
Handler: h,
ReadTimeout: serverReadTimeout,
WriteTimeout: serverWriteTimeout,
ConnState: s.connState,
}
go s.Serve(l)
return s, nil
}
func (s *stopServer) connState(c net.Conn, state http.ConnState) {
s.mu.Lock()
defer s.mu.Unlock()
// Close c immediately if we're past shutdown.
if s.shutdown {
if state != http.StateClosed {
c.Close()
}
return
}
if state == http.StateIdle {
s.idle[c] = struct{}{}
} else {
delete(s.idle, c)
}
}
func (s *stopServer) Close() {
s.mu.Lock()
defer s.mu.Unlock()
// Shut down the acceptor. No new connections can be created.
s.l.Close()
// Drop all idle connections. Non-idle connections will be
// closed by connState as soon as they become idle.
s.shutdown = true
for c := range s.idle {
glog.V(logger.Detail).Infof("closing idle connection %v", c.RemoteAddr())
c.Close()
delete(s.idle, c)
} }
} }

View file

@ -22,6 +22,7 @@ import (
"net" "net"
"os" "os"
"github.com/ethereum/go-ethereum/fdtrack"
"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"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -50,15 +51,16 @@ func (self *ipcClient) reconnect() error {
func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error { func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error {
os.Remove(cfg.Endpoint) // in case it still exists from a previous run os.Remove(cfg.Endpoint) // in case it still exists from a previous run
l, err := net.ListenUnix("unix", &net.UnixAddr{Name: cfg.Endpoint, Net: "unix"}) l, err := net.Listen("unix", cfg.Endpoint)
if err != nil { if err != nil {
return err return err
} }
l = fdtrack.WrapListener("ipc", l)
os.Chmod(cfg.Endpoint, 0600) os.Chmod(cfg.Endpoint, 0600)
go func() { go func() {
for { for {
conn, err := l.AcceptUnix() conn, err := l.Accept()
if err != nil { if err != nil {
glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err)
continue continue

View file

@ -0,0 +1,100 @@
{
"preExpDiffIncrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "42",
"currentDifficulty" : "1000488"
},
"preExpDiffDecrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "42",
"currentDifficulty" : "999512"
},
"ExpDiffAtBlock200000Increase" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "200000",
"currentDifficulty" : "1000489"
},
"ExpDiffAtBlock200000Decrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "200000",
"currentDifficulty" : "999513"
},
"ExpDiffPostBlock200000Increase" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "200001",
"currentDifficulty" : "1000489"
},
"ExpDiffPostBlock200000Decrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "200001",
"currentDifficulty" : "999513"
},
"ExpDiffPreBlock300000Increase" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "299999",
"currentDifficulty" : "1000489"
},
"ExpDiffPreBlock300000Decrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "299999",
"currentDifficulty" : "999513"
},
"ExpDiffAtBlock300000Increase" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "300000",
"currentDifficulty" : "1000490"
},
"ExpDiffAtBlock300000Decrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "300000",
"currentDifficulty" : "999514"
},
"ExpDiffPostBlock300000Increase" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "300001",
"currentDifficulty" : "1000490"
},
"ExpDiffPostBlock300000Decrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "300001",
"currentDifficulty" : "999514"
},
"ExpDiffInAYearIncrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "43",
"currentBlockNumber" : "2302400",
"currentDifficulty" : "3097640"
},
"ExpDiffInAYearDecrease" : {
"parentTimestamp" : "42",
"parentDifficulty" : "1000000",
"currentTimestamp" : "60",
"currentBlockNumber" : "2302400",
"currentDifficulty" : "3096664"
}
}

View file

@ -123,7 +123,7 @@ func New(ethereum *eth.Ethereum, frontend Frontend) *XEth {
if frontend == nil { if frontend == nil {
xeth.frontend = dummyFrontend{} xeth.frontend = dummyFrontend{}
} }
xeth.state = NewState(xeth, xeth.backend.ChainManager().TransState()) xeth.state = NewState(xeth, xeth.backend.ChainManager().State())
go xeth.start() go xeth.start()
go xeth.filterManager.Start() go xeth.filterManager.Start()
@ -310,7 +310,12 @@ func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blha
// some chain, this probably needs to be refactored for more expressiveness // some chain, this probably needs to be refactored for more expressiveness
data, _ := self.backend.ExtraDb().Get(common.FromHex(hash)) data, _ := self.backend.ExtraDb().Get(common.FromHex(hash))
if len(data) != 0 { if len(data) != 0 {
tx = types.NewTransactionFromBytes(data) dtx := new(types.Transaction)
if err := rlp.DecodeBytes(data, dtx); err != nil {
glog.V(logger.Error).Infoln(err)
return
}
tx = dtx
} else { // check pending transactions } else { // check pending transactions
tx = self.backend.TxPool().GetTransaction(common.HexToHash(hash)) tx = self.backend.TxPool().GetTransaction(common.HexToHash(hash))
} }
@ -773,8 +778,14 @@ func (self *XEth) FromNumber(str string) string {
} }
func (self *XEth) PushTx(encodedTx string) (string, error) { func (self *XEth) PushTx(encodedTx string) (string, error) {
tx := types.NewTransactionFromBytes(common.FromHex(encodedTx)) tx := new(types.Transaction)
err := self.backend.TxPool().Add(tx) err := rlp.DecodeBytes(common.FromHex(encodedTx), tx)
if err != nil {
glog.V(logger.Error).Infoln(err)
return "", err
}
err = self.backend.TxPool().Add(tx)
if err != nil { if err != nil {
return "", err return "", err
} }