mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 12:46:44 +00:00
Merge remote-tracking branch 'felix/blocktest-parsing-fix' into HEAD
* felix/blocktest-parsing-fix: tests: hopefully improve test conversion helpers blockpool: add missing shutdown synchronization core, eth: add missing shutdown synchronization to ChainManager cmd/geth, tests: enable running multiple tests from a single file eth: honour config.Shh eth: use NewDB hook also for extra DB Conflicts: eth/backend.go
This commit is contained in:
commit
7c3319f4d7
6 changed files with 146 additions and 57 deletions
|
|
@ -181,9 +181,10 @@ type BlockPool struct {
|
|||
|
||||
// waitgroup is used in tests to wait for result-critical routines
|
||||
// as well as in determining idle / syncing status
|
||||
wg sync.WaitGroup //
|
||||
quit chan bool // chan used for quitting parallel routines
|
||||
running bool //
|
||||
wg sync.WaitGroup //
|
||||
quit chan bool // chan used for quitting parallel routines
|
||||
running bool //
|
||||
evloopdone chan struct{}
|
||||
}
|
||||
|
||||
// public constructor
|
||||
|
|
@ -204,6 +205,7 @@ func New(
|
|||
verifyPoW: verifyPoW,
|
||||
chainEvents: chainEvents,
|
||||
td: td,
|
||||
evloopdone: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,6 +246,7 @@ func (self *BlockPool) Start() {
|
|||
// status update interval
|
||||
timer := time.NewTicker(self.Config.StatusUpdateInterval)
|
||||
go func() {
|
||||
defer close(self.evloopdone)
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
|
|
@ -290,6 +293,7 @@ func (self *BlockPool) Stop() {
|
|||
|
||||
self.tdSub.Unsubscribe()
|
||||
close(self.quit)
|
||||
<-self.evloopdone
|
||||
|
||||
self.lock.Lock()
|
||||
self.peers = nil
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
|
|
@ -12,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
var blocktestCmd = cli.Command{
|
||||
Action: runblocktest,
|
||||
Action: runBlockTest,
|
||||
Name: "blocktest",
|
||||
Usage: `loads a block test file`,
|
||||
Description: `
|
||||
|
|
@ -25,27 +26,78 @@ be able to interact with the chain defined by the test.
|
|||
`,
|
||||
}
|
||||
|
||||
func runblocktest(ctx *cli.Context) {
|
||||
if len(ctx.Args()) != 3 {
|
||||
utils.Fatalf("Usage: ethereum blocktest <path-to-test-file> <test-name> {rpc, norpc}")
|
||||
func runBlockTest(ctx *cli.Context) {
|
||||
var (
|
||||
file, testname string
|
||||
rpc bool
|
||||
)
|
||||
args := ctx.Args()
|
||||
switch {
|
||||
case len(args) == 1:
|
||||
file = args[0]
|
||||
case len(args) == 2:
|
||||
file, testname = args[0], args[1]
|
||||
case len(args) == 3:
|
||||
file, testname = args[0], args[1]
|
||||
rpc = true
|
||||
default:
|
||||
utils.Fatalf(`Usage: ethereum blocktest <path-to-test-file> [ <test-name> [ "rpc" ] ]`)
|
||||
}
|
||||
file, testname, startrpc := ctx.Args()[0], ctx.Args()[1], ctx.Args()[2]
|
||||
|
||||
bt, err := tests.LoadBlockTests(file)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// run all tests if no test name is specified
|
||||
if testname == "" {
|
||||
ecode := 0
|
||||
for name, test := range bt {
|
||||
fmt.Printf("----------------- Running Block Test %q\n", name)
|
||||
ethereum, err := runOneBlockTest(ctx, test)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("FAIL")
|
||||
ecode = 1
|
||||
}
|
||||
if ethereum != nil {
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
}
|
||||
os.Exit(ecode)
|
||||
return
|
||||
}
|
||||
// otherwise, run the given test
|
||||
test, ok := bt[testname]
|
||||
if !ok {
|
||||
utils.Fatalf("Test file does not contain test named %q", testname)
|
||||
}
|
||||
ethereum, err := runOneBlockTest(ctx, test)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
if rpc {
|
||||
fmt.Println("Block Test post state validated, starting RPC interface.")
|
||||
startEth(ctx, ethereum)
|
||||
utils.StartRPC(ethereum, ctx)
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
|
||||
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
|
||||
cfg.MaxPeers = 0 // disable network
|
||||
cfg.Shh = false // disable whisper
|
||||
cfg.NAT = nil // disable port mapping
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := ethereum.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// import the genesis block
|
||||
|
|
@ -54,27 +106,16 @@ func runblocktest(ctx *cli.Context) {
|
|||
// import pre accounts
|
||||
statedb, err := test.InsertPreState(ethereum.StateDb())
|
||||
if err != nil {
|
||||
utils.Fatalf("could not insert genesis accounts: %v", err)
|
||||
return ethereum, fmt.Errorf("InsertPreState: %v", err)
|
||||
}
|
||||
|
||||
// insert the test blocks, which will execute all transactions
|
||||
chain := ethereum.ChainManager()
|
||||
if err := chain.InsertChain(test.Blocks); err != nil {
|
||||
utils.Fatalf("Block Test load error: %v %T", err, err)
|
||||
} else {
|
||||
fmt.Println("Block Test chain loaded")
|
||||
if err := test.InsertBlocks(ethereum.ChainManager()); err != nil {
|
||||
return ethereum, fmt.Errorf("Block Test load error: %v %T", err, err)
|
||||
}
|
||||
|
||||
fmt.Println("chain loaded")
|
||||
if err := test.ValidatePostState(statedb); err != nil {
|
||||
utils.Fatalf("post state validation failed: %v", err)
|
||||
}
|
||||
fmt.Println("Block Test post state validated, starting ethereum.")
|
||||
|
||||
if startrpc == "rpc" {
|
||||
startEth(ctx, ethereum)
|
||||
utils.StartRPC(ethereum, ctx)
|
||||
ethereum.WaitForShutdown()
|
||||
} else {
|
||||
startEth(ctx, ethereum)
|
||||
return ethereum, fmt.Errorf("post state validation failed: %v", err)
|
||||
}
|
||||
return ethereum, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ type ChainManager struct {
|
|||
futureBlocks *BlockCache
|
||||
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *ChainManager {
|
||||
|
|
@ -104,6 +105,7 @@ func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *Chai
|
|||
bc.futureBlocks = NewBlockCache(254)
|
||||
bc.makeCache()
|
||||
|
||||
bc.wg.Add(1)
|
||||
go bc.update()
|
||||
|
||||
return bc
|
||||
|
|
@ -428,6 +430,7 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
|||
|
||||
func (bc *ChainManager) Stop() {
|
||||
close(bc.quit)
|
||||
bc.wg.Wait()
|
||||
}
|
||||
|
||||
type queueEvent struct {
|
||||
|
|
@ -593,6 +596,9 @@ func (self *ChainManager) merge(oldBlock, newBlock *types.Block) {
|
|||
func (self *ChainManager) update() {
|
||||
events := self.eventMux.Subscribe(queueEvent{})
|
||||
futureTimer := time.NewTicker(5 * time.Second)
|
||||
defer self.wg.Done()
|
||||
defer futureTimer.Stop()
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,10 @@ func New(config *Config) (*Ethereum, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extraDb, err := ethdb.NewLDBDatabase(path.Join(config.DataDir, "extra"))
|
||||
extraDb, err := newdb(path.Join(config.DataDir, "extra"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Perform database sanity checks
|
||||
d, _ := blockDb.Get([]byte("ProtocolVersion"))
|
||||
|
|
@ -213,8 +216,6 @@ func New(config *Config) (*Ethereum, error) {
|
|||
eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State)
|
||||
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.chainManager.SetProcessor(eth.blockProcessor)
|
||||
eth.whisper = whisper.New()
|
||||
eth.shhVersionId = int(eth.whisper.Version())
|
||||
eth.miner = miner.New(eth, eth.pow, config.MinerThreads)
|
||||
eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.txPool, eth.chainManager, eth.downloader)
|
||||
|
||||
|
|
@ -419,6 +420,7 @@ func (s *Ethereum) Stop() {
|
|||
s.txSub.Unsubscribe() // quits txBroadcastLoop
|
||||
s.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
||||
|
||||
s.chainManager.Stop()
|
||||
s.txPool.Stop()
|
||||
s.eventMux.Stop()
|
||||
if s.whisper != nil {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -73,8 +74,8 @@ type btBlock struct {
|
|||
|
||||
type BlockTest struct {
|
||||
Genesis *types.Block
|
||||
Blocks []*types.Block
|
||||
|
||||
json *btJSON
|
||||
preAccounts map[string]btAccount
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ func LoadBlockTests(file string) (map[string]*BlockTest, error) {
|
|||
for name, in := range bt {
|
||||
var err error
|
||||
if out[name], err = convertTest(in); err != nil {
|
||||
return nil, fmt.Errorf("bad test %q: %v", err)
|
||||
return out, fmt.Errorf("bad test %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
|
|
@ -124,6 +125,15 @@ func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
|
|||
return statedb, nil
|
||||
}
|
||||
|
||||
// InsertBlocks loads the test's blocks into the given chain.
|
||||
func (t *BlockTest) InsertBlocks(chain *core.ChainManager) error {
|
||||
blocks, err := t.convertBlocks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return chain.InsertChain(blocks)
|
||||
}
|
||||
|
||||
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
||||
for addrString, acct := range t.preAccounts {
|
||||
// XXX: is is worth it checking for errors here?
|
||||
|
|
@ -149,6 +159,21 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockTest) convertBlocks() (blocks []*types.Block, err error) {
|
||||
// the conversion handles errors by catching panics.
|
||||
// you might consider this ugly, but the alternative (passing errors)
|
||||
// would be much harder to read.
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
buf := make([]byte, 64<<10)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||
}
|
||||
}()
|
||||
blocks = mustConvertBlocks(t.json.Blocks)
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func convertTest(in *btJSON) (out *BlockTest, err error) {
|
||||
// the conversion handles errors by catching panics.
|
||||
// you might consider this ugly, but the alternative (passing errors)
|
||||
|
|
@ -160,9 +185,8 @@ func convertTest(in *btJSON) (out *BlockTest, err error) {
|
|||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||
}
|
||||
}()
|
||||
out = &BlockTest{preAccounts: in.Pre}
|
||||
out = &BlockTest{preAccounts: in.Pre, json: in}
|
||||
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
||||
out.Blocks = mustConvertBlocks(in.Blocks)
|
||||
return out, err
|
||||
}
|
||||
|
||||
|
|
@ -187,13 +211,13 @@ func mustConvertHeader(in btHeader) *types.Header {
|
|||
UncleHash: mustConvertHash(in.UncleHash),
|
||||
ParentHash: mustConvertHash(in.ParentHash),
|
||||
Extra: mustConvertBytes(in.ExtraData),
|
||||
GasUsed: mustConvertBigInt(in.GasUsed),
|
||||
GasLimit: mustConvertBigInt(in.GasLimit),
|
||||
Difficulty: mustConvertBigInt(in.Difficulty),
|
||||
Time: mustConvertUint(in.Timestamp),
|
||||
GasUsed: mustConvertBigInt(in.GasUsed, 10),
|
||||
GasLimit: mustConvertBigInt(in.GasLimit, 10),
|
||||
Difficulty: mustConvertBigInt(in.Difficulty, 10),
|
||||
Time: mustConvertUint(in.Timestamp, 10),
|
||||
}
|
||||
// XXX cheats? :-)
|
||||
header.SetNonce(common.BytesToHash(mustConvertBytes(in.Nonce)).Big().Uint64())
|
||||
header.SetNonce(mustConvertUint(in.Nonce, 16))
|
||||
return header
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +227,7 @@ func mustConvertBlocks(testBlocks []btBlock) []*types.Block {
|
|||
var b types.Block
|
||||
r := bytes.NewReader(mustConvertBytes(inb.Rlp))
|
||||
if err := rlp.Decode(r, &b); err != nil {
|
||||
panic(fmt.Errorf("invalid block %d: %q", i, inb.Rlp))
|
||||
panic(fmt.Errorf("invalid block %d: %q\nerror: %v", i, inb.Rlp, err))
|
||||
}
|
||||
out = append(out, &b)
|
||||
}
|
||||
|
|
@ -214,7 +238,7 @@ func mustConvertBytes(in string) []byte {
|
|||
if in == "0x" {
|
||||
return []byte{}
|
||||
}
|
||||
h := strings.TrimPrefix(unfuckCPPHexInts(in), "0x")
|
||||
h := nibbleFix(strings.TrimPrefix(in, "0x"))
|
||||
out, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", h))
|
||||
|
|
@ -231,7 +255,7 @@ func mustConvertHash(in string) common.Hash {
|
|||
}
|
||||
|
||||
func mustConvertAddress(in string) common.Address {
|
||||
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
out, err := hex.DecodeString(nibbleFix(strings.TrimPrefix(in, "0x")))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", in))
|
||||
}
|
||||
|
|
@ -246,16 +270,18 @@ func mustConvertBloom(in string) types.Bloom {
|
|||
return types.BytesToBloom(out)
|
||||
}
|
||||
|
||||
func mustConvertBigInt(in string) *big.Int {
|
||||
out, ok := new(big.Int).SetString(unfuckCPPHexInts(in), 0)
|
||||
func mustConvertBigInt(in string, base int) *big.Int {
|
||||
in = prepInt(base, in)
|
||||
out, ok := new(big.Int).SetString(in, base)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("invalid integer: %q", in))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustConvertUint(in string) uint64 {
|
||||
out, err := strconv.ParseUint(unfuckCPPHexInts(in), 0, 64)
|
||||
func mustConvertUint(in string, base int) uint64 {
|
||||
in = prepInt(base, in)
|
||||
out, err := strconv.ParseUint(in, base, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid integer: %q", in))
|
||||
}
|
||||
|
|
@ -292,12 +318,22 @@ func findLine(data []byte, offset int64) (line int) {
|
|||
return
|
||||
}
|
||||
|
||||
func unfuckCPPHexInts(s string) string {
|
||||
if s == "0x" { // no respect for the empty value :(
|
||||
return "0x00"
|
||||
}
|
||||
if (len(s) % 2) != 0 { // motherfucking nibbles
|
||||
return "0x0" + s[2:]
|
||||
func prepInt(base int, s string) string {
|
||||
if base == 16 {
|
||||
if strings.HasPrefix(s, "0x") {
|
||||
s = s[2:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
s = "00"
|
||||
}
|
||||
s = nibbleFix(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func nibbleFix(s string) string {
|
||||
if len(s)%2 != 0 {
|
||||
s = "0" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,15 +127,15 @@ func convertTestTypes(txTest TransactionTest) (sender, to common.Address,
|
|||
txInputData = mustConvertBytes(txTest.Transaction.Data)
|
||||
rlpBytes = mustConvertBytes(txTest.Rlp)
|
||||
|
||||
gasLimit = mustConvertBigInt(txTest.Transaction.GasLimit)
|
||||
gasPrice = mustConvertBigInt(txTest.Transaction.GasPrice)
|
||||
value = mustConvertBigInt(txTest.Transaction.Value)
|
||||
gasLimit = mustConvertBigInt(txTest.Transaction.GasLimit, 16)
|
||||
gasPrice = mustConvertBigInt(txTest.Transaction.GasPrice, 16)
|
||||
value = mustConvertBigInt(txTest.Transaction.Value, 16)
|
||||
|
||||
r = common.Bytes2Big(mustConvertBytes(txTest.Transaction.R))
|
||||
s = common.Bytes2Big(mustConvertBytes(txTest.Transaction.S))
|
||||
|
||||
nonce = mustConvertUint(txTest.Transaction.Nonce)
|
||||
v = mustConvertUint(txTest.Transaction.V)
|
||||
nonce = mustConvertUint(txTest.Transaction.Nonce, 16)
|
||||
v = mustConvertUint(txTest.Transaction.V, 16)
|
||||
|
||||
return sender, to, txInputData, rlpBytes, gasLimit, gasPrice, value, r, s, nonce, v, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue