aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/block_processor.go13
-rw-r--r--core/chain_makers.go142
-rw-r--r--core/chain_manager_test.go194
-rw-r--r--core/transaction_pool.go9
-rw-r--r--core/types/block.go29
5 files changed, 378 insertions, 9 deletions
diff --git a/core/block_processor.go b/core/block_processor.go
index 1a4501740..8974cd94b 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -12,7 +12,6 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/pow"
- "github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/state"
"gopkg.in/fatih/set.v0"
)
@@ -46,11 +45,11 @@ type BlockProcessor struct {
eventMux *event.TypeMux
}
-func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
+func NewBlockProcessor(db ethutil.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
sm := &BlockProcessor{
db: db,
mem: make(map[string]*big.Int),
- Pow: ezp.New(),
+ Pow: pow,
bc: chainManager,
eventMux: eventMux,
txpool: txpool,
@@ -105,6 +104,9 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
return receipt, txGas, err
}
+func (self *BlockProcessor) ChainManager() *ChainManager {
+ return self.bc
+}
func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
var (
@@ -256,6 +258,11 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.Header().GasLimit, a, b)
}
+ // There can be at most one uncle
+ if len(block.Uncles()) > 1 {
+ return ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles()))
+ }
+
if block.Time() < parent.Time() {
return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time)
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
new file mode 100644
index 000000000..7afdfde0d
--- /dev/null
+++ b/core/chain_makers.go
@@ -0,0 +1,142 @@
+package core
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/pow"
+ "github.com/ethereum/go-ethereum/state"
+)
+
+// So we can generate blocks easily
+type FakePow struct{}
+
+func (f FakePow) Search(block pow.Block, stop <-chan struct{}) ([]byte, []byte, []byte) {
+ return nil, nil, nil
+}
+func (f FakePow) Verify(block pow.Block) bool { return true }
+func (f FakePow) GetHashrate() int64 { return 0 }
+func (f FakePow) Turbo(bool) {}
+
+// So we can deterministically seed different blockchains
+var (
+ CanonicalSeed = 1
+ ForkSeed = 2
+)
+
+// Utility functions for making chains on the fly
+// Exposed for sake of testing from other packages (eg. go-ethash)
+func NewBlockFromParent(addr []byte, parent *types.Block) *types.Block {
+ return newBlockFromParent(addr, parent)
+}
+
+func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database, seed int) *types.Block {
+ return makeBlock(bman, parent, i, db, seed)
+}
+
+func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Database, seed int) types.Blocks {
+ return makeChain(bman, parent, max, db, seed)
+}
+
+func NewChainMan(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager {
+ return newChainManager(block, eventMux, db)
+}
+
+func NewBlockProc(db ethutil.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
+ return newBlockProcessor(db, txpool, cman, eventMux)
+}
+
+func NewCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
+ return newCanonical(n, db)
+}
+
+// block time is fixed at 10 seconds
+func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
+ block := types.NewBlock(parent.Hash(), addr, parent.Root(), ethutil.BigPow(2, 32), nil, "")
+ block.SetUncles(nil)
+ block.SetTransactions(nil)
+ block.SetReceipts(nil)
+
+ header := block.Header()
+ header.Difficulty = CalcDifficulty(block, parent)
+ header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
+ header.Time = parent.Header().Time + 10
+ header.GasLimit = CalcGasLimit(parent, block)
+
+ block.Td = parent.Td
+
+ return block
+}
+
+// Actually make a block by simulating what miner would do
+// we seed chains by the first byte of the coinbase
+func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database, seed int) *types.Block {
+ addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
+ addr[0] = byte(seed)
+ block := newBlockFromParent(addr, parent)
+ state := state.New(block.Root(), db)
+ cbase := state.GetOrNewStateObject(addr)
+ cbase.SetGasPool(CalcGasLimit(parent, block))
+ cbase.AddBalance(BlockReward)
+ state.Update(ethutil.Big0)
+ block.SetRoot(state.Root())
+ return block
+}
+
+// Make a chain with real blocks
+// Runs ProcessWithParent to get proper state roots
+func makeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Database, seed int) types.Blocks {
+ bman.bc.currentBlock = parent
+ blocks := make(types.Blocks, max)
+ for i := 0; i < max; i++ {
+ block := makeBlock(bman, parent, i, db, seed)
+ td, err := bman.processWithParent(block, parent)
+ if err != nil {
+ fmt.Println("process with parent failed", err)
+ panic(err)
+ }
+ block.Td = td
+ blocks[i] = block
+ parent = block
+ }
+ return blocks
+}
+
+// Create a new chain manager starting from given block
+// Effectively a fork factory
+func newChainManager(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager {
+ bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: eventMux}
+ if block == nil {
+ bc.Reset()
+ } else {
+ bc.currentBlock = block
+ bc.td = block.Td
+ }
+ return bc
+}
+
+// block processor with fake pow
+func newBlockProcessor(db ethutil.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
+ bman := NewBlockProcessor(db, FakePow{}, txpool, newChainManager(nil, eventMux, db), eventMux)
+ return bman
+}
+
+// Make a new, deterministic canonical chain by running InsertChain
+// on result of makeChain
+func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
+ eventMux := &event.TypeMux{}
+ txpool := NewTxPool(eventMux)
+
+ bman := newBlockProcessor(db, txpool, newChainManager(nil, eventMux, db), eventMux)
+ bman.bc.SetProcessor(bman)
+ parent := bman.bc.CurrentBlock()
+ if n == 0 {
+ return bman, nil
+ }
+ lchain := makeChain(bman, parent, n, db, CanonicalSeed)
+ err := bman.bc.InsertChain(lchain)
+ return bman, err
+}
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index bc3a264d1..b562b677d 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -3,6 +3,7 @@ package core
import (
"bytes"
"fmt"
+ "math/big"
"os"
"path"
"runtime"
@@ -21,6 +22,75 @@ func init() {
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
}
+// Test fork of length N starting from block i
+func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) {
+ // switch databases to process the new chain
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // copy old chain up to i into new db with deterministic canonical
+ bman2, err := newCanonical(i, db)
+ if err != nil {
+ t.Fatal("could not make new canonical in testFork", err)
+ }
+ // asert the bmans have the same block at i
+ bi1 := bman.bc.GetBlockByNumber(uint64(i)).Hash()
+ bi2 := bman2.bc.GetBlockByNumber(uint64(i)).Hash()
+ if bytes.Compare(bi1, bi2) != 0 {
+ t.Fatal("chains do not have the same hash at height", i)
+ }
+
+ bman2.bc.SetProcessor(bman2)
+
+ // extend the fork
+ parent := bman2.bc.CurrentBlock()
+ chainB := makeChain(bman2, parent, N, db, ForkSeed)
+ err = bman2.bc.InsertChain(chainB)
+ if err != nil {
+ t.Fatal("Insert chain error for fork:", err)
+ }
+
+ tdpre := bman.bc.Td()
+ // Test the fork's blocks on the original chain
+ td, err := testChain(chainB, bman)
+ if err != nil {
+ t.Fatal("expected chainB not to give errors:", err)
+ }
+ // Compare difficulties
+ f(tdpre, td)
+}
+
+func printChain(bc *ChainManager) {
+ for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- {
+ b := bc.GetBlockByNumber(uint64(i))
+ fmt.Printf("\t%x\n", b.Hash())
+ }
+}
+
+// process blocks against a chain
+func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
+ td := new(big.Int)
+ for _, block := range chainB {
+ td2, err := bman.bc.processor.Process(block)
+ if err != nil {
+ if IsKnownBlockErr(err) {
+ continue
+ }
+ return nil, err
+ }
+ block.Td = td2
+ td = td2
+
+ bman.bc.mu.Lock()
+ {
+ bman.bc.write(block)
+ }
+ bman.bc.mu.Unlock()
+ }
+ return td, nil
+}
+
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil {
@@ -45,6 +115,130 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
done <- true
}
+func TestExtendCanonical(t *testing.T) {
+ CanonicalLength := 5
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(CanonicalLength, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) <= 0 {
+ t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
+ }
+ }
+ // Start fork from current height (CanonicalLength)
+ testFork(t, bman, CanonicalLength, 1, f)
+ testFork(t, bman, CanonicalLength, 2, f)
+ testFork(t, bman, CanonicalLength, 5, f)
+ testFork(t, bman, CanonicalLength, 10, f)
+}
+
+func TestShorterFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) >= 0 {
+ t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1)
+ }
+ }
+ // Sum of numbers must be less than 10
+ // for this to be a shorter fork
+ testFork(t, bman, 0, 3, f)
+ testFork(t, bman, 0, 7, f)
+ testFork(t, bman, 1, 1, f)
+ testFork(t, bman, 1, 7, f)
+ testFork(t, bman, 5, 3, f)
+ testFork(t, bman, 5, 4, f)
+}
+
+func TestLongerFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) <= 0 {
+ t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
+ }
+ }
+ // Sum of numbers must be greater than 10
+ // for this to be a longer fork
+ testFork(t, bman, 0, 11, f)
+ testFork(t, bman, 0, 15, f)
+ testFork(t, bman, 1, 10, f)
+ testFork(t, bman, 1, 12, f)
+ testFork(t, bman, 5, 6, f)
+ testFork(t, bman, 5, 8, f)
+}
+
+func TestEqualFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) != 0 {
+ t.Error("expected chainB to have equal difficulty. Got", td2, "expected ", td1)
+ }
+ }
+ // Sum of numbers must be equal to 10
+ // for this to be an equal fork
+ testFork(t, bman, 0, 10, f)
+ testFork(t, bman, 1, 9, f)
+ testFork(t, bman, 2, 8, f)
+ testFork(t, bman, 5, 5, f)
+ testFork(t, bman, 6, 4, f)
+ testFork(t, bman, 9, 1, f)
+}
+
+func TestBrokenChain(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ db2, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman2, err := newCanonical(10, db2)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ bman2.bc.SetProcessor(bman2)
+ parent := bman2.bc.CurrentBlock()
+ chainB := makeChain(bman2, parent, 5, db2, ForkSeed)
+ chainB = chainB[1:]
+ _, err = testChain(chainB, bman)
+ if err == nil {
+ t.Error("expected broken chain to return error")
+ }
+}
+
func TestChainInsertions(t *testing.T) {
t.Skip() // travil fails.
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 050cff3d8..bd377f679 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -117,8 +117,13 @@ func (self *TxPool) add(tx *types.Transaction) error {
} else {
to = "[NEW_CONTRACT]"
}
-
- txplogger.Debugf("(t) %x => %s (%v) %x\n", tx.From()[:4], to, tx.Value, tx.Hash())
+ var from string
+ if len(tx.From()) > 0 {
+ from = ethutil.Bytes2Hex(tx.From()[:4])
+ } else {
+ return errors.New(fmt.Sprintf("FROM ADDRESS MUST BE POSITIVE (was %v)", tx.From()))
+ }
+ txplogger.Debugf("(t) %x => %s (%v) %x\n", from, to, tx.Value, tx.Hash())
// Notify the subscribers
go self.eventMux.Post(TxPreEvent{tx})
diff --git a/core/types/block.go b/core/types/block.go
index d57de1311..673c72003 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -41,12 +41,29 @@ type Header struct {
Extra string
// Block Nonce for verification
Nonce ethutil.Bytes
+ // Mix digest for quick checking to prevent DOS
+ MixDigest ethutil.Bytes
+ // SeedHash used for light client verification
+ SeedHash ethutil.Bytes
}
func (self *Header) rlpData(withNonce bool) []interface{} {
- fields := []interface{}{self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra}
+ fields := []interface{}{
+ self.ParentHash,
+ self.UncleHash,
+ self.Coinbase,
+ self.Root,
+ self.TxHash,
+ self.ReceiptHash,
+ self.Bloom,
+ self.Difficulty,
+ self.Number,
+ self.GasLimit,
+ self.GasUsed,
+ self.Time,
+ self.Extra}
if withNonce {
- fields = append(fields, self.Nonce)
+ fields = append(fields, self.Nonce, self.MixDigest, self.SeedHash)
}
return fields
@@ -176,6 +193,8 @@ func (self *Block) RlpDataForStorage() interface{} {
// Header accessors (add as you need them)
func (self *Block) Number() *big.Int { return self.header.Number }
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
+func (self *Block) MixDigest() []byte { return self.header.MixDigest }
+func (self *Block) SeedHash() []byte { return self.header.SeedHash }
func (self *Block) Nonce() []byte { return self.header.Nonce }
func (self *Block) Bloom() []byte { return self.header.Bloom }
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
@@ -200,7 +219,6 @@ func (self *Block) GetUncle(i int) *Header {
// Implement pow.Block
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
-func (self *Block) N() []byte { return self.header.Nonce }
func (self *Block) HashNoNonce() []byte { return self.header.HashNoNonce() }
func (self *Block) Hash() []byte {
@@ -250,7 +268,10 @@ func (self *Header) String() string {
Time: %v
Extra: %v
Nonce: %x
-`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce)
+ MixDigest: %x
+ SeedHash: %x
+
+`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce, self.MixDigest, self.SeedHash)
}
type Blocks []*Block