Compare commits

..

37 Commits

Author SHA1 Message Date
0397c66c4d crypto: fix build with Go 1.5 2015-08-03 13:08:10 +02:00
0cdc7647aa tests: updated 2015-07-29 15:59:33 +02:00
c5d7faefe9 eth, eth/downloader: don't report stall if fetcher filled the block 2015-07-29 15:39:01 +02:00
d7211dec79 etherbase defaults to first account even if created during the session 2015-07-28 14:12:49 +02:00
cea2c0eedf all: fix license headers one more time
I forgot to update one instance of "go-ethereum" in commit 3f047be5a.

Conflicts:
	common/config.go
2015-07-28 14:02:23 +02:00
b738172dcc Godeps: use BSD-licensed version of gopkg.in/karalabe/cookiejar.v2 2015-07-28 13:33:02 +02:00
abdc3d3c77 crypto/sha3: add full license headers 2015-07-28 13:32:52 +02:00
80901e8288 common: remove config.go
The code in config.go is unused. The main reason for removing it is to
get rid github.com/rakyll/goini in Godeps (it has no license).

Conflicts:
	common/config.go
2015-07-28 13:32:39 +02:00
465796c3a8 web3: fixed toHex 2015-07-27 12:21:30 +02:00
9c05284bd1 core: genesis extra data field fix 2015-07-26 14:46:07 +02:00
56edaa1653 xeth: fix #1485, data race in fiilter creation and event firing 2015-07-26 13:11:51 +02:00
8865fda872 params: reduce extra data to 32 bytes 2015-07-26 13:04:56 +02:00
b0df9b164c core: fixed genesis write out to write only canon number 2015-07-25 21:52:37 +02:00
72188234aa eth: set default miner extra to client name 2015-07-25 17:59:26 +02:00
8c0619d29c core: 5 ether block reward 2015-07-25 17:59:19 +02:00
16a3a4303f cmd/util: lowered default gas price 2015-07-25 17:16:26 +02:00
eaed7584f1 core: check genesis block before writeout 2015-07-25 16:54:51 +02:00
0262ba58cb web3: updated 0.9.1 2015-07-25 12:24:39 +02:00
e088998867 core: 5 ether block reward 2015-07-25 12:06:17 +02:00
db5ec711e8 cmd/geth, core, eth: Version 1.0.0
Genesis release. Closes #1402
2015-07-23 10:46:38 +02:00
9d49c80783 remove LICENSE files 2015-07-23 10:46:38 +02:00
b1fdb9f38e all: update license headers to distiguish GPL/LGPL
All code outside of cmd/ is licensed as LGPL. The headers
now reflect this by calling the whole work "the go-ethereum library".
2015-07-23 10:46:37 +02:00
a606dc274b crypto: fix license of curve.go
crypto/curve.go is not our code and has its own license. This commit
excludes it in update-license.go and removes our GPL header.
2015-07-23 10:46:37 +02:00
c9d6fba07d miner: fix current work data race 2015-07-23 10:41:49 +02:00
228fc5a83a xeth: removed unneeded mutex lock 2015-07-23 10:41:49 +02:00
c28dc03f6d xeth: log signed tx hash 2015-07-23 10:41:49 +02:00
dcb276a0dd Fixed canary to require 2+ nonzero, not sum 2+ 2015-07-22 14:36:33 +02:00
53864a73db Update disclaimer 2015-07-22 13:36:21 +02:00
cf65a127e1 Move text to separate file 2015-07-22 13:35:00 +02:00
fd64dce6a5 Prompt user to accept legalese when datadir doesn't exist 2015-07-22 13:35:00 +02:00
d60b07249c rlp: fix check for canonical byte array size
Decoding did not reject byte arrays of length one with a single element
b where 55 < b < 128. Such byte arrays must be rejected because
they must be encoded as the single byte b instead.
2015-07-22 13:34:16 +02:00
7b99278eb0 rlp: reject trailing data when using DecodeBytes 2015-07-22 13:34:16 +02:00
aaf8ae1d0b tests: document RLP tests 2015-07-22 13:34:16 +02:00
a83fdd0046 cmd/ethtest, tests: add support for RLP JSON tests 2015-07-22 13:33:05 +02:00
b1a219b0ec core: during chain reorg rewrite receipts and transactions
Added PutBlockReceipts; storing receipts by blocks. Eventually this will
require pruning during some cleanup cycle. During forks the receipts by
block are used to get the new canonical receipts and transactions.

This PR fixes #1473 by rewriting transactions and receipts from the point
of where the fork occured.
2015-07-22 13:27:19 +02:00
487b3b0f7b cmd, core, eth, common: genesis preparation
Implemented the --genesis flag thru which we can set a custom genesis
block, including the official Ethereum genesis block.
2015-07-22 13:26:27 +02:00
4ca3d49307 ethdb, trie: removed RLE compression 2015-07-22 13:24:30 +02:00
64 changed files with 716 additions and 2170 deletions

View File

@ -1,6 +1,9 @@
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
@ -19,11 +22,7 @@ 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,8 +9,6 @@ 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
@ -27,7 +25,6 @@ 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
} }
@ -36,7 +33,6 @@ 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,12 +7,9 @@ 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 (
@ -29,17 +26,6 @@ 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,8 +5,6 @@ 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
@ -104,8 +102,6 @@ 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,7 +18,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/fdtrack"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
) )
@ -370,8 +369,6 @@ 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
} }
@ -403,7 +400,6 @@ 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
@ -422,7 +418,6 @@ 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,17 +87,14 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error)
return signature, err return signature, err
} }
// Unlock unlocks the given account indefinitely. // unlock 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)
} }
// TimedUnlock unlocks the account with the given address. The account // Unlock unlocks the account with the given address. The account
// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account // stays unlocked for the duration of timeout
// until the program exits. // it timeout is 0 the account is unlocked for the entire session
//
// 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,10 +23,9 @@ 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)
@ -34,24 +33,26 @@ 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, testSigData) _, err = am.Sign(a1, toSign)
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.NewKeyStorePlain) dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase)
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, testSigData) _, err = am.Sign(a1, toSign)
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)
} }
@ -62,26 +63,28 @@ 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, testSigData) _, err = am.Sign(a1, toSign)
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, testSigData) _, err = am.Sign(a1, toSign)
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.NewKeyStorePlain) dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase)
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 {
@ -89,7 +92,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, testSigData) _, err = am.Sign(a1, toSign)
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)
} }
@ -100,46 +103,20 @@ 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, testSigData) _, err = am.Sign(a1, toSign)
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, testSigData) _, err = am.Sign(a1, toSign)
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,7 +36,6 @@ 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{
@ -136,13 +135,8 @@ 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 files, err = getFiles(filepath.Join(file, curTest))
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))
}
} else { } else {
files, err = getFiles(file) files, err = getFiles(file)
} }

View File

@ -18,129 +18,79 @@
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 (
app *cli.App code = flag.String("code", "", "evm code")
DebugFlag = cli.BoolFlag{ loglevel = flag.Int("log", 4, "log level")
Name: "debug", gas = flag.String("gas", "1000000000", "gas amount")
Usage: "output full trace logs", price = flag.String("price", "0", "gas price")
} value = flag.String("value", "0", "tx value")
CodeFlag = cli.StringFlag{ dump = flag.Bool("dump", false, "dump state after run")
Name: "code", data = flag.String("data", "", "data")
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 init() { func perr(v ...interface{}) {
app = utils.NewApp("0.2", "the evm command line interface") fmt.Println(v...)
app.Flags = []cli.Flag{ //os.Exit(1)
DebugFlag,
SysStatFlag,
CodeFlag,
GasFlag,
PriceFlag,
ValueFlag,
DumpFlag,
InputFlag,
}
app.Action = run
} }
func run(ctx *cli.Context) { func main() {
vm.Debug = ctx.GlobalBool(DebugFlag.Name) flag.Parse()
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(ctx.GlobalString(CodeFlag.Name))) receiver.SetCode(common.Hex2Bytes(*code))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name))) vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value))
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 {
fmt.Println(e) perr(e)
os.Exit(1)
} }
if ctx.GlobalBool(DumpFlag.Name) { if *dump {
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
heap alloc: %d 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("OUT: 0x%x\n", ret) fmt.Printf("%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,7 +23,6 @@ import (
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"sort" "sort"
@ -45,10 +44,6 @@ 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)
@ -418,10 +413,8 @@ func (self *jsre) interactive() {
str += input + "\n" str += input + "\n"
self.setIndent() self.setIndent()
if indentCount <= 0 { if indentCount <= 0 {
hist := hidepassword(str[:len(str)-1]) hist := str[:len(str)-1]
if len(hist) > 0 { self.AppendHistory(hist)
self.AppendHistory(hist)
}
self.parseInput(str) self.parseInput(str)
str = "" str = ""
} }
@ -429,14 +422,6 @@ 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,7 +38,6 @@ 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"
@ -50,11 +49,11 @@ import (
const ( const (
ClientIdentifier = "Geth" ClientIdentifier = "Geth"
Version = "1.0.2" Version = "1.0.0"
) )
var ( var (
gitCommit string // set via linker flagg gitCommit string // set via linker flag
nodeNameVersion string nodeNameVersion string
app *cli.App app *cli.App
) )
@ -281,8 +280,6 @@ 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,
@ -348,9 +345,6 @@ 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)
@ -506,7 +500,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"), cfg.DatabaseCache) blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"))
if err != nil { if err != nil {
glog.Fatalln("could not open db:", err) glog.Fatalln("could not open db:", err)
} }
@ -533,9 +527,6 @@ 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,7 +21,6 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"io" "io"
"math/big"
"os" "os"
"os/signal" "os/signal"
"regexp" "regexp"
@ -33,7 +32,6 @@ 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"
) )
@ -145,15 +143,6 @@ 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)
@ -214,11 +203,6 @@ 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++
} }
@ -234,7 +218,6 @@ 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,15 +126,6 @@ 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{
@ -158,7 +149,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(50), common.Shannon).String(), Value: new(big.Int).Mul(big.NewInt(500), common.Shannon).String(),
} }
UnlockedAccountFlag = cli.StringFlag{ UnlockedAccountFlag = cli.StringFlag{
@ -318,12 +309,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(50), common.Shannon).String(), Value: new(big.Int).Mul(big.NewInt(1), common.Szabo).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(500), common.Shannon).String(), Value: new(big.Int).Mul(big.NewInt(100), common.Szabo).String(),
} }
GpoFullBlockRatioFlag = cli.IntFlag{ GpoFullBlockRatioFlag = cli.IntFlag{
Name: "gpofull", Name: "gpofull",
@ -393,7 +384,6 @@ 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),
@ -406,7 +396,6 @@ 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),
@ -436,26 +425,17 @@ 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) {
datadir := ctx.GlobalString(DataDirFlag.Name) dd := ctx.GlobalString(DataDirFlag.Name)
cache := ctx.GlobalInt(CacheFlag.Name)
var err error var err error
if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "blockchain"), cache); err != nil { if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "blockchain")); err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
} }
if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "state"), cache); err != nil { if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "state")); err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
} }
if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "extra"), cache); err != nil { if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "extra")); 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, 0) db, err = ethdb.NewLDBDatabase(dir)
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.Number(), parent.Difficulty()) expd := CalcDifficulty(block.Time, parent.Time(), 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

@ -20,5 +20,8 @@ import "github.com/ethereum/go-ethereum/common"
// Set of manually tracked bad hashes (usually hard forks) // Set of manually tracked bad hashes (usually hard forks)
var BadHashes = map[common.Hash]bool{ var BadHashes = map[common.Hash]bool{
common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true, common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"): true,
common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"): true,
common.HexToHash("7064455b364775a16afbdecd75370e912c6e2879f202eda85b9beae547fff3ac"): true,
common.HexToHash("5b7c80070a6eff35f3eb3181edb023465c776d40af2885571e1bc4689f3a44d8"): true,
} }

View File

@ -24,10 +24,10 @@ import (
) )
var ( var (
jeff = common.HexToAddress("959c33de5961820567930eccce51ea715c496f85") jeff = common.HexToAddress("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c")
vitalik = common.HexToAddress("c8158da0b567a8cc898991c2c2a073af67dc03a9") vitalik = common.HexToAddress("1baf27b88c48dd02b744999cf3522766929d2b2a")
christoph = common.HexToAddress("7a19a893f91d5b6e2cdf941b6acbba2cbcf431ee") christoph = common.HexToAddress("60d11b58744784dc97f878f7e3749c0f1381a004")
gav = common.HexToAddress("539dd9aaf45c3feb03f9c004f4098bd3268fef6b") gav = common.HexToAddress("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a")
) )
// Canary will check the 0'd address of the 4 contracts above. // Canary will check the 0'd address of the 4 contracts above.

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.Number(), parent.Difficulty()), Difficulty: CalcDifficulty(time, parent.Time(), 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,11 +73,13 @@ 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
@ -99,15 +101,7 @@ 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 {
reader, err := NewDefaultGenesisReader() return nil, ErrNoGenesis
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")
} }
if err := bc.setLastState(); err != nil { if err := bc.setLastState(); err != nil {
@ -128,7 +122,9 @@ 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()
@ -150,6 +146,9 @@ 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()
@ -198,6 +197,17 @@ 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 {
@ -452,9 +462,6 @@ 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)
@ -516,6 +523,9 @@ 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,6 +392,7 @@ 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

@ -30,15 +30,14 @@ 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, parentNumber, parentDiff *big.Int) *big.Int { func CalcDifficulty(time, parentTime uint64, 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)
@ -53,19 +52,8 @@ func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int)
diff.Sub(parentDiff, adjust) diff.Sub(parentDiff, adjust)
} }
if diff.Cmp(params.MinimumDifficulty) < 0 { if diff.Cmp(params.MinimumDifficulty) < 0 {
diff = params.MinimumDifficulty return 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
} }
@ -81,30 +69,17 @@ 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 {
// contrib = (parentGasUsed * 3 / 2) / 1024 decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
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))

View File

@ -1,77 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package 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)
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -82,9 +82,8 @@ type StateObject struct {
// Mark for deletion // Mark for deletion
// When an object is marked for deletion it will be delete from the trie // When an object is marked for deletion it will be delete from the trie
// during the "update" phase of the state transition // during the "update" phase of the state transition
remove bool remove bool
deleted bool dirty bool
dirty bool
} }
func (self *StateObject) Reset() { func (self *StateObject) Reset() {

View File

@ -44,7 +44,6 @@ 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
@ -67,9 +66,7 @@ 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 {
@ -203,20 +200,18 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
// Delete the given state object and delete it from the state trie // Delete the given state object and delete it from the state trie
func (self *StateDB) DeleteStateObject(stateObject *StateObject) { func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
stateObject.deleted = true
addr := stateObject.Address() addr := stateObject.Address()
self.trie.Delete(addr[:]) self.trie.Delete(addr[:])
//delete(self.stateObjects, addr.Str())
} }
// Retrieve a state object given my the address. Nil if not found // Retrieve a state object given my the address. Nil if not found
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) { func (self *StateDB) GetStateObject(addr common.Address) *StateObject {
stateObject = self.stateObjects[addr.Str()] //addr = common.Address(addr)
if stateObject != nil {
if stateObject.deleted {
stateObject = nil
}
stateObject := self.stateObjects[addr.Str()]
if stateObject != nil {
return stateObject return stateObject
} }
@ -238,7 +233,7 @@ func (self *StateDB) SetStateObject(object *StateObject) {
// Retrieve a state object or create a new state object if nil // Retrieve a state object or create a new state object if nil
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject == nil || stateObject.deleted { if stateObject == nil {
stateObject = self.CreateAccount(addr) stateObject = self.CreateAccount(addr)
} }
@ -293,7 +288,6 @@ 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
} }
@ -304,7 +298,6 @@ 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("Transaction pool stopped") glog.V(logger.Info).Infoln("TX Pool stopped")
} }
func (pool *TxPool) State() *state.ManagedState { func (pool *TxPool) State() *state.ManagedState {
@ -356,12 +356,11 @@ 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())
} }
} }
// RemoveTx removes the transaction with the given hash from the pool. func (pool *TxPool) removeTx(hash common.Hash) {
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,11 +19,9 @@ 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 (
@ -33,21 +31,13 @@ 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
} }
db.Put(tx.Hash().Bytes(), rlpEnc)
if batchWrite {
batch.Put(tx.Hash().Bytes(), rlpEnc)
} else {
db.Put(tx.Hash().Bytes(), rlpEnc)
}
var txExtra struct { var txExtra struct {
BlockHash common.Hash BlockHash common.Hash
@ -62,44 +52,20 @@ 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
} }
db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
if batchWrite {
batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
} else {
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
} }
err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
if batchWrite { if err != nil {
batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
} else {
err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
if err != nil {
return err
}
}
}
if db, ok := db.(*ethdb.LDBDatabase); ok {
if err := db.LDB().Write(batch, nil); err != nil {
return err return err
} }
} }

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,6 +97,15 @@ 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)
} }
@ -289,22 +298,3 @@ 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,9 +81,11 @@ 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}
@ -91,6 +93,8 @@ 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():
@ -116,6 +120,8 @@ 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):
@ -128,6 +134,8 @@ 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():
@ -240,7 +248,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.P256(), elliptic.P384(), elliptic.P521(): case elliptic.P224(), 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,6 +407,11 @@ 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,6 +147,7 @@ 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,11 +21,9 @@ 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,11 +78,9 @@ 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
Olympic bool
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
@ -264,7 +262,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, config.DatabaseCache) } newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path) }
} }
blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain")) blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain"))
if err != nil { if err != nil {
@ -303,14 +301,6 @@ 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())
} }
if config.Olympic {
_, err := core.WriteTestNetGenesisBlock(stateDb, blockDb, 42)
if err != nil {
return nil, err
}
glog.V(logger.Error).Infoln("Starting Olympic network")
}
// 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,13 +28,12 @@ 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{GasLimit: params.GenesisGasLimit}, nil, nil, nil) unknownBlock = types.NewBlock(&types.Header{}, 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,7 +17,6 @@
package ethdb package ethdb
import ( import (
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -36,14 +35,6 @@ import (
var OpenFileLimit = 64 var OpenFileLimit = 64
// 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
@ -65,24 +56,14 @@ 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, cache int) (*LDBDatabase, error) { func NewLDBDatabase(file string) (*LDBDatabase, error) {
// Calculate the cache allowance for this particular database // Open the db
cache = int(float64(cache) * cacheRatio[filepath.Base(file)]) db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: OpenFileLimit})
if cache < 16 { // check for corruption and attempt to recover
cache = 16 if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
}
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,7 +28,8 @@ 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
} }

View File

@ -1,112 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package 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)
}
}()
}

View File

@ -1,29 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !linux,!darwin
package fdtrack
import "errors"
func fdlimit() int {
return 0
}
func fdusage() (int, error) {
return 0, errors.New("not implemented")
}

View File

@ -1,72 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +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")
}

View File

@ -1,53 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +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

@ -1137,10 +1137,10 @@ var toHex = function (val) {
if (isString(val)) { if (isString(val)) {
if (val.indexOf('-0x') === 0) if (val.indexOf('-0x') === 0)
return fromDecimal(val); return fromDecimal(val);
else if (!isFinite(val))
return fromAscii(val);
else if(val.indexOf('0x') === 0) else if(val.indexOf('0x') === 0)
return val; return val;
else if (!isFinite(val))
return fromAscii(val);
} }
return fromDecimal(val); return fromDecimal(val);

View File

@ -34,7 +34,6 @@ 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,6 +20,7 @@ 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"
@ -28,10 +29,10 @@ import (
type CpuAgent struct { type CpuAgent struct {
mu sync.Mutex mu sync.Mutex
workCh chan *Work workCh chan *types.Block
quit chan struct{} quit chan struct{}
quitCurrentOp chan struct{} quitCurrentOp chan struct{}
returnCh chan<- *Result returnCh chan<- *types.Block
index int index int
pow pow.PoW pow pow.PoW
@ -46,9 +47,9 @@ func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
return miner return miner
} }
func (self *CpuAgent) Work() chan<- *Work { return self.workCh } func (self *CpuAgent) Work() chan<- *types.Block { 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<- *Result) { self.returnCh = ch } func (self *CpuAgent) SetReturnCh(ch chan<- *types.Block) { self.returnCh = ch }
func (self *CpuAgent) Stop() { func (self *CpuAgent) Stop() {
self.mu.Lock() self.mu.Lock()
@ -64,7 +65,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 *Work, 1) self.workCh = make(chan *types.Block, 1)
go self.update() go self.update()
} }
@ -73,13 +74,13 @@ func (self *CpuAgent) update() {
out: out:
for { for {
select { select {
case work := <-self.workCh: case block := <-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(work, self.quitCurrentOp) go self.mine(block, self.quitCurrentOp)
self.mu.Unlock() self.mu.Unlock()
case <-self.quit: case <-self.quit:
self.mu.Lock() self.mu.Lock()
@ -105,14 +106,13 @@ done:
} }
} }
func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) { func (self *CpuAgent) mine(block *types.Block, 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(work.Block, stop) nonce, mixDigest := self.pow.Search(block, stop)
if nonce != 0 { if nonce != 0 {
block := work.Block.WithMiningResult(nonce, common.BytesToHash(mixDigest)) self.returnCh <- block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
self.returnCh <- &Result{work, block}
} else { } else {
self.returnCh <- nil self.returnCh <- nil
} }

View File

@ -18,44 +18,39 @@ 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/logger" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/logger/glog"
) )
type RemoteAgent struct { type RemoteAgent struct {
mu sync.Mutex work *types.Block
currentWork *types.Block
quit chan struct{} quit chan struct{}
workCh chan *Work workCh chan *types.Block
returnCh chan<- *Result returnCh chan<- *types.Block
currentWork *Work
work map[common.Hash]*Work
} }
func NewRemoteAgent() *RemoteAgent { func NewRemoteAgent() *RemoteAgent {
agent := &RemoteAgent{work: make(map[common.Hash]*Work)} agent := &RemoteAgent{}
return agent return agent
} }
func (a *RemoteAgent) Work() chan<- *Work { func (a *RemoteAgent) Work() chan<- *types.Block {
return a.workCh return a.workCh
} }
func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { func (a *RemoteAgent) SetReturnCh(returnCh chan<- *types.Block) {
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 *Work, 1) a.workCh = make(chan *types.Block, 1)
go a.maintainLoop() go a.run()
} }
func (a *RemoteAgent) Stop() { func (a *RemoteAgent) Stop() {
@ -65,72 +60,47 @@ func (a *RemoteAgent) Stop() {
func (a *RemoteAgent) GetHashRate() int64 { return 0 } func (a *RemoteAgent) GetHashRate() int64 { return 0 }
func (a *RemoteAgent) GetWork() [3]string { func (a *RemoteAgent) run() {
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.mu.Lock() a.work = work
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,20 +38,25 @@ import (
var jsonlogger = logger.NewJsonLogger() var jsonlogger = logger.NewJsonLogger()
const ( // Work holds the current work
resultQueueSize = 10 type Work struct {
miningLogAtDepth = 5 Number uint64
) 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<- *Work Work() chan<- *types.Block
SetReturnCh(chan<- *Result) SetReturnCh(chan<- *types.Block)
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)
@ -59,7 +64,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 Work struct { type environment 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)
@ -73,18 +78,11 @@ type Work 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
@ -92,7 +90,7 @@ type worker struct {
mu sync.Mutex mu sync.Mutex
agents []Agent agents []Agent
recv chan *Result recv chan *types.Block
mux *event.TypeMux mux *event.TypeMux
quit chan struct{} quit chan struct{}
pow pow.PoW pow pow.PoW
@ -107,7 +105,7 @@ type worker struct {
extra []byte extra []byte
currentMu sync.Mutex currentMu sync.Mutex
current *Work current *environment
uncleMu sync.Mutex uncleMu sync.Mutex
possibleUncles map[common.Hash]*types.Block possibleUncles map[common.Hash]*types.Block
@ -118,8 +116,6 @@ 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 {
@ -127,7 +123,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 *Result, resultQueueSize), recv: make(chan *types.Block),
gasPrice: new(big.Int), gasPrice: new(big.Int),
chain: eth.ChainManager(), chain: eth.ChainManager(),
proc: eth.BlockProcessor(), proc: eth.BlockProcessor(),
@ -135,7 +131,6 @@ 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()
@ -169,7 +164,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() {
@ -256,55 +251,34 @@ func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (
func (self *worker) wait() { func (self *worker) wait() {
for { for {
for result := range self.recv { for block := range self.recv {
atomic.AddInt32(&self.atWork, -1) atomic.AddInt32(&self.atWork, -1)
if result == nil { if block == nil {
continue continue
} }
block := result.Block
work := result.Work
work.state.Sync() parent := self.chain.GetBlock(block.ParentHash())
if self.fullValidation { if parent == nil {
if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil { glog.V(logger.Error).Infoln("Invalid block found during mining")
glog.V(logger.Error).Infoln("mining err", err) continue
continue }
} if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent, true); err != nil && err != core.BlockFutureErr {
go self.mux.Post(core.NewMinedBlockEvent{block}) glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
} else { continue
parent := self.chain.GetBlock(block.ParentHash()) }
if parent == nil {
glog.V(logger.Error).Infoln("Invalid block found during mining")
continue
}
if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent, true); err != nil && err != core.BlockFutureErr {
glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
continue
}
stat, err := self.chain.WriteBlock(block, false) stat, err := self.chain.WriteBlock(block, false)
if err != nil { if err != nil {
glog.V(logger.Error).Infoln("error writing block to chain", err) glog.V(logger.Error).Infoln("error writing block to chain", err)
continue continue
} }
// check if canon block and write transactions // check if canon block and write transactions
if stat == core.CanonStatTy { if stat == core.CanonStatTy {
// 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, work.receipts) core.PutReceipts(self.extraDb, self.current.receipts)
}
// broadcast before waiting for validation
go func(block *types.Block, logs state.Logs) {
self.mux.Post(core.NewMinedBlockEvent{block})
self.mux.Post(core.ChainEvent{block, block.Hash(), logs})
if stat == core.CanonStatTy {
self.mux.Post(core.ChainHeadEvent{block})
self.mux.Post(logs)
}
}(block, work.state.Logs())
} }
// check staleness and display confirmation // check staleness and display confirmation
@ -314,18 +288,29 @@ func (self *worker) wait() {
stale = "stale " stale = "stale "
} else { } else {
confirm = "Wait 5 blocks for confirmation" confirm = "Wait 5 blocks for confirmation"
work.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), work.localMinedBlocks) 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) glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
// broadcast before waiting for validation
go func(block *types.Block, logs state.Logs) {
self.mux.Post(core.NewMinedBlockEvent{block})
self.mux.Post(core.ChainEvent{block, block.Hash(), logs})
if stat == core.CanonStatTy {
self.mux.Post(core.ChainHeadEvent{block})
self.mux.Post(logs)
}
}(block, self.current.state.Logs())
self.commitNewWork() self.commitNewWork()
} }
} }
} }
func (self *worker) push(work *Work) { func (self *worker) push() {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
if core.Canary(work.state) { if core.Canary(self.current.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
@ -336,7 +321,7 @@ func (self *worker) push(work *Work) {
atomic.AddInt32(&self.atWork, 1) atomic.AddInt32(&self.atWork, 1)
if agent.Work() != nil { if agent.Work() != nil {
agent.Work() <- work agent.Work() <- self.current.block
} }
} }
} }
@ -345,36 +330,35 @@ func (self *worker) push(work *Work) {
// 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())
work := &Work{ current := &environment{
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() {
work.family.Add(uncle.Hash()) current.family.Add(uncle.Hash())
} }
work.family.Add(ancestor.Hash()) current.family.Add(ancestor.Hash())
work.ancestors.Add(ancestor.Hash()) current.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
work.remove = set.New() current.remove = set.New()
work.tcount = 0 current.tcount = 0
work.ignoredTransactors = set.New() current.ignoredTransactors = set.New()
work.lowGasTransactors = set.New() current.lowGasTransactors = set.New()
work.ownedAccounts = accountAddressesSet(accounts) current.ownedAccounts = accountAddressesSet(accounts)
if self.current != nil { if self.current != nil {
work.localMinedBlocks = self.current.localMinedBlocks current.localMinedBlocks = self.current.localMinedBlocks
} }
self.current = work self.current = current
} }
func (w *worker) setGasPrice(p *big.Int) { func (w *worker) setGasPrice(p *big.Int) {
@ -388,13 +372,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(current *Work, deepBlockNum uint64) bool { func (self *worker) isBlockLocallyMined(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 current.localMinedBlocks.ints { for idx, blockNum := range self.current.localMinedBlocks.ints {
if deepBlockNum == blockNum { if deepBlockNum == blockNum {
isLocal = true isLocal = true
current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs self.current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs
break break
} }
} }
@ -408,12 +392,12 @@ func (self *worker) isBlockLocallyMined(current *Work, deepBlockNum uint64) bool
return block != nil && block.Coinbase() == self.coinbase return block != nil && block.Coinbase() == self.coinbase
} }
func (self *worker) logLocalMinedBlocks(current, previous *Work) { func (self *worker) logLocalMinedBlocks(previous *environment) {
if previous != nil && current.localMinedBlocks != nil { if previous != nil && self.current.localMinedBlocks != nil {
nextBlockNum := current.Block.NumberU64() nextBlockNum := self.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(current, inspectBlockNum) { if self.isBlockLocallyMined(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)
} }
} }
@ -445,7 +429,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.Number(), parent.Difficulty()), Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time(), parent.Difficulty()),
GasLimit: core.CalcGasLimit(parent), GasLimit: core.CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Coinbase: self.coinbase, Coinbase: self.coinbase,
@ -455,47 +439,14 @@ func (self *worker) commitNewWork() {
previous := self.current previous := self.current
self.makeCurrent(parent, header) self.makeCurrent(parent, header)
work := self.current current := self.current
/* //approach 1 // commit transactions for this run.
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)
//approach 2 self.eth.TxPool().RemoveTransactions(current.lowGasTxs)
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 (
@ -506,7 +457,7 @@ func (self *worker) commitNewWork() {
if len(uncles) == 2 { if len(uncles) == 2 {
break break
} }
if err := self.commitUncle(work, uncle.Header()); err != nil { if err := self.commitUncle(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)
@ -523,40 +474,41 @@ 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(work.state, header, uncles) core.AccumulateRewards(self.current.state, header, uncles)
work.state.SyncObjects() current.state.SyncObjects()
header.Root = work.state.Root() self.current.state.Sync()
header.Root = current.state.Root()
} }
// create the new block whose nonce will be mined. // create the new block whose nonce will be mined.
work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) current.block = types.NewBlock(header, current.txs, uncles, current.receipts)
work.Block.Td = new(big.Int).Set(core.CalcTD(work.Block, self.chain.GetBlock(work.Block.ParentHash()))) self.current.block.Td = new(big.Int).Set(core.CalcTD(self.current.block, self.chain.GetBlock(self.current.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", work.Block.Number(), work.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", current.block.Number(), current.tcount, len(uncles), time.Since(tstart))
self.logLocalMinedBlocks(work, previous) self.logLocalMinedBlocks(previous)
} }
self.push(work) self.push()
} }
func (self *worker) commitUncle(work *Work, uncle *types.Header) error { func (self *worker) commitUncle(uncle *types.Header) error {
hash := uncle.Hash() hash := uncle.Hash()
if work.uncles.Has(hash) { if self.current.uncles.Has(hash) {
return core.UncleError("Uncle not unique") return core.UncleError("Uncle not unique")
} }
if !work.ancestors.Has(uncle.ParentHash) { if !self.current.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 work.family.Has(hash) { if self.current.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))
} }
work.uncles.Add(uncle.Hash()) self.current.uncles.Add(uncle.Hash())
return nil return nil
} }
func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) { func (env *environment) 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()
@ -614,7 +566,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
} }
} }
func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error { func (env *environment) 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,7 +23,6 @@ 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"
@ -213,7 +212,6 @@ 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,7 +25,6 @@ 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"
@ -198,7 +197,6 @@ 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
@ -236,7 +234,6 @@ 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.Conn *net.TCPConn // Network connection to wrap with metering
} }
// 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} return &meteredConn{conn.(*net.TCPConn)}
} }
// 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.Conn.Read(b) n, err = c.TCPConn.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.Conn.Write(b) n, err = c.TCPConn.Write(b)
egressTrafficMeter.Mark(int64(n)) egressTrafficMeter.Mark(int64(n))
return return
} }

View File

@ -25,7 +25,6 @@ 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"
@ -373,7 +372,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 = fdtrack.WrapListener("p2p", listener) srv.listener = 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,8 @@ var (
EcrecoverGas = big.NewInt(3000) // EcrecoverGas = big.NewInt(3000) //
Sha256WordGas = big.NewInt(12) // Sha256WordGas = big.NewInt(12) //
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(3141592) // Gas limit of the Genesis block. GenesisGasLimit = big.NewInt(5000) // Gas limit of the Genesis block.
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)
err := json.Unmarshal([]byte(input), &args) str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
if err != nil { if len(str) > 0 {
t.Error("Did not expect error. Got", err) t.Error(str)
} }
} }

View File

@ -21,9 +21,8 @@ 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"
@ -323,7 +322,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(common.String2Big(gas)), err return newHexNum(gas), nil
} }
} }
@ -579,17 +578,14 @@ func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
from := common.HexToAddress(args.Tx.From) ret, err := self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data)
if err != nil {
pending := self.ethereum.TxPool().GetTransactions() return nil, err
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)
}
} }
return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash) self.ethereum.TxPool().RemoveTransactions(types.Transactions{args.Tx.tx})
return ret, nil
} }
func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) { func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) {

View File

@ -469,6 +469,10 @@ 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
@ -884,7 +888,6 @@ 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 {
@ -903,7 +906,6 @@ 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(),
} }
} }
@ -929,12 +931,6 @@ 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,22 +32,16 @@ var (
AutoCompletion = map[string][]string{ AutoCompletion = map[string][]string{
"admin": []string{ "admin": []string{
"addPeer", "addPeer",
"chainSyncStatus",
"datadir",
"exportChain",
"getContractInfo",
"importChain",
"nodeInfo",
"peers", "peers",
"register", "nodeInfo",
"registerUrl", "exportChain",
"setSolc", "importChain",
"sleepBlocks",
"startNatSpec",
"startRPC",
"stopNatSpec",
"stopRPC",
"verbosity", "verbosity",
"chainSyncStatus",
"setSolc",
"datadir",
"startRPC",
"stopRPC",
}, },
"db": []string{ "db": []string{
"getString", "getString",
@ -103,7 +97,6 @@ var (
"miner": []string{ "miner": []string{
"hashrate", "hashrate",
"makeDAG", "makeDAG",
"setEtherbase",
"setExtra", "setExtra",
"setGasPrice", "setGasPrice",
"startAutoDAG", "startAutoDAG",

View File

@ -17,19 +17,13 @@
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"
@ -37,15 +31,10 @@ 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 (
httpServerMu sync.Mutex // main HTTP rpc listener
httpServer *stopServer httpListener *stoppableTCPListener
listenerStoppedError = fmt.Errorf("Listener has stopped")
) )
type HttpConfig struct { type HttpConfig struct {
@ -54,172 +43,42 @@ 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 {
httpServerMu.Lock() if httpListener != nil {
defer httpServerMu.Unlock() if fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) != httpListener.Addr().String() {
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.
handler := http.Handler(&handler{codec, api}) l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort))
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
} }
httpServer = s httpListener = l
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() {
httpServerMu.Lock() if httpListener != nil {
defer httpServerMu.Unlock() httpListener.Stop()
if httpServer != nil { httpListener = 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)
} }
} }

182
rpc/comms/http_net.go Normal file
View File

@ -0,0 +1,182 @@
// 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 comms
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"time"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
// When https://github.com/golang/go/issues/4674 is implemented this could be replaced
type stoppableTCPListener struct {
*net.TCPListener
stop chan struct{} // closed when the listener must stop
}
func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) {
wl, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
if tcpl, ok := wl.(*net.TCPListener); ok {
stop := make(chan struct{})
return &stoppableTCPListener{tcpl, stop}, nil
}
return nil, fmt.Errorf("Unable to create TCP listener for RPC service")
}
// Stop the listener and all accepted and still active connections.
func (self *stoppableTCPListener) Stop() {
close(self.stop)
}
func (self *stoppableTCPListener) Accept() (net.Conn, error) {
for {
self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second)))
c, err := self.TCPListener.AcceptTCP()
select {
case <-self.stop:
if c != nil { // accept timeout
c.Close()
}
self.TCPListener.Close()
return nil, listenerStoppedError
default:
}
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() {
continue // regular timeout
}
}
return &closableConnection{c, self.stop}, err
}
}
type closableConnection struct {
*net.TCPConn
closed chan struct{}
}
func (self *closableConnection) Read(b []byte) (n int, err error) {
select {
case <-self.closed:
self.TCPConn.Close()
return 0, io.EOF
default:
return self.TCPConn.Read(b)
}
}
// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an
// error indicating that the service was stopped. This will only happen for connections which are
// kept open (HTTP keep-alive) when the RPC service was shutdown.
func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case <-stop:
w.Header().Set("Content-Type", "application/json")
err := fmt.Errorf("RPC service stopped")
response := shared.NewRpcResponse(-1, shared.JsonRpcVersion, nil, err)
httpSend(w, response)
default:
h.ServeHTTP(w, r)
}
})
}
func httpSend(writer io.Writer, v interface{}) (n int, err error) {
var payload []byte
payload, err = json.MarshalIndent(v, "", "\t")
if err != nil {
glog.V(logger.Error).Infoln("Error marshalling JSON", err)
return 0, err
}
glog.V(logger.Detail).Infof("Sending payload: %s", payload)
return writer.Write(payload)
}
func gethHttpHandler(codec codec.Codec, a shared.EthereumApi) http.Handler {
return http.HandlerFunc(func(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)
httpSend(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)
httpSend(w, &response)
return
}
c := codec.New(nil)
var rpcReq shared.Request
if err = c.Decode(payload, &rpcReq); err == nil {
reply, err := a.Execute(&rpcReq)
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
httpSend(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 := a.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
resBatch = resBatch[:resCount]
httpSend(w, resBatch)
return
}
// invalid request
err = fmt.Errorf("Could not decode request")
res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err)
httpSend(w, res)
})
}

View File

@ -22,7 +22,6 @@ 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"
@ -51,16 +50,15 @@ 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.Listen("unix", cfg.Endpoint) l, err := net.ListenUnix("unix", &net.UnixAddr{Name: cfg.Endpoint, Net: "unix"})
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.Accept() conn, err := l.AcceptUnix()
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

@ -150,7 +150,7 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
// test the block // test the block
if err := runBlockTest(test); err != nil { if err := runBlockTest(test); err != nil {
return fmt.Errorf("%s: %v", name, err) return err
} }
glog.Infoln("Block test passed: ", name) glog.Infoln("Block test passed: ", name)

View File

@ -1,100 +0,0 @@
{
"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"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,10 +20,8 @@ package xeth
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"math/big" "math/big"
"regexp"
"sync" "sync"
"time" "time"
@ -47,7 +45,6 @@ var (
defaultGasPrice = big.NewInt(10000000000000) //150000000000 defaultGasPrice = big.NewInt(10000000000000) //150000000000
defaultGas = big.NewInt(90000) //500000 defaultGas = big.NewInt(90000) //500000
dappStorePre = []byte("dapp-") dappStorePre = []byte("dapp-")
addrReg = regexp.MustCompile(`^(0x)?[a-fA-F0-9]{40}$`)
) )
// byte will be inferred // byte will be inferred
@ -126,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().State()) xeth.state = NewState(xeth, xeth.backend.ChainManager().TransState())
go xeth.start() go xeth.start()
go xeth.filterManager.Start() go xeth.filterManager.Start()
@ -313,12 +310,7 @@ 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 {
dtx := new(types.Transaction) tx = types.NewTransactionFromBytes(data)
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))
} }
@ -781,14 +773,8 @@ func (self *XEth) FromNumber(str string) string {
} }
func (self *XEth) PushTx(encodedTx string) (string, error) { func (self *XEth) PushTx(encodedTx string) (string, error) {
tx := new(types.Transaction) tx := types.NewTransactionFromBytes(common.FromHex(encodedTx))
err := rlp.DecodeBytes(common.FromHex(encodedTx), tx) err := self.backend.TxPool().Add(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
} }
@ -881,10 +867,6 @@ func (self *XEth) Sign(fromStr, hashStr string, didUnlock bool) (string, error)
return common.ToHex(sig), nil return common.ToHex(sig), nil
} }
func isAddress(addr string) bool {
return addrReg.MatchString(addr)
}
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
// this minimalistic recoding is enough (works for natspec.js) // this minimalistic recoding is enough (works for natspec.js)
@ -894,10 +876,6 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
return "", err return "", err
} }
if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) {
return "", errors.New("Invalid address")
}
var ( var (
from = common.HexToAddress(fromStr) from = common.HexToAddress(fromStr)
to = common.HexToAddress(toStr) to = common.HexToAddress(toStr)

View File

@ -1,26 +0,0 @@
package xeth
import "testing"
func TestIsAddress(t *testing.T) {
for _, invalid := range []string{
"0x00",
"0xNN",
"0x00000000000000000000000000000000000000NN",
"0xAAar000000000000000000000000000000000000",
} {
if isAddress(invalid) {
t.Error("Expected", invalid, "to be invalid")
}
}
for _, valid := range []string{
"0x0000000000000000000000000000000000000000",
"0xAABBbbCCccff9900000000000000000000000000",
"AABBbbCCccff9900000000000000000000000000",
} {
if !isAddress(valid) {
t.Error("Expected", valid, "to be valid")
}
}
}