Introducing ethash

This commit is contained in:
Matthew Wampler-Doty
2015-02-28 14:58:37 -05:00
parent 080823bdee
commit de9f79133f
52 changed files with 17425 additions and 70 deletions

View File

@ -7,12 +7,12 @@ import (
"sync"
"time"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil"
"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"
)
@ -50,7 +50,7 @@ func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainM
sm := &BlockProcessor{
db: db,
mem: make(map[string]*big.Int),
Pow: ezp.New(),
Pow: ethash.New(chainManager),
bc: chainManager,
eventMux: eventMux,
txpool: txpool,
@ -255,6 +255,7 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
return fmt.Errorf("GasLimit check failed for block %v, %v", block.Header().GasLimit, expl)
}
// 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()))
}

View File

@ -13,21 +13,31 @@ import (
// So we can generate blocks easily
type FakePow struct{}
func (f FakePow) Search(block pow.Block, stop <-chan struct{}) []byte { return nil }
func (f FakePow) Verify(block pow.Block) bool { return true }
func (f FakePow) GetHashrate() int64 { return 0 }
func (f FakePow) Turbo(bool) {}
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) *types.Block {
return makeBlock(bman, parent, i, db)
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) types.Blocks {
return makeChain(bman, parent, max, db)
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 {
@ -42,9 +52,9 @@ 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)
@ -52,6 +62,7 @@ func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
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
@ -60,8 +71,10 @@ func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
}
// Actually make a block by simulating what miner would do
func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database) *types.Block {
// 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)
@ -74,11 +87,11 @@ func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Data
// 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) types.Blocks {
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)
block := makeBlock(bman, parent, i, db, seed)
td, err := bman.processWithParent(block, parent)
if err != nil {
fmt.Println("process with parent failed", err)
@ -87,9 +100,7 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Da
block.Td = td
blocks[i] = block
parent = block
fmt.Printf("New Block: %x\n", block.Hash())
}
fmt.Println("Done making chain")
return blocks
}
@ -113,7 +124,7 @@ func newBlockProcessor(db ethutil.Database, txpool *TxPool, cman *ChainManager,
return bman
}
// Make a new canonical chain by running InsertChain
// 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{}
@ -125,7 +136,7 @@ func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
if n == 0 {
return bman, nil
}
lchain := makeChain(bman, parent, n, db)
bman.bc.InsertChain(lchain)
return bman, nil
lchain := makeChain(bman, parent, n, db, CanonicalSeed)
err := bman.bc.InsertChain(lchain)
return bman, err
}

View File

@ -24,12 +24,6 @@ func init() {
// 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)) {
fmt.Println("Testing Fork!")
var b *types.Block = nil
if i > 0 {
b = bman.bc.GetBlockByNumber(uint64(i))
}
_ = b
// switch databases to process the new chain
db, err := ethdb.NewMemDatabase()
if err != nil {
@ -40,13 +34,25 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
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)
bman2.bc.InsertChain(chainB)
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)
@ -55,6 +61,14 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
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 {
@ -102,12 +116,13 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
}
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(5, db)
bman, err := newCanonical(CanonicalLength, db)
if err != nil {
t.Fatal("Could not make new canonical chain:", err)
}
@ -116,11 +131,11 @@ func TestExtendCanonical(t *testing.T) {
t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
}
}
// Start fork from current height (5)
testFork(t, bman, 5, 1, f)
testFork(t, bman, 5, 2, f)
testFork(t, bman, 5, 5, f)
testFork(t, bman, 5, 10, f)
// 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) {
@ -189,6 +204,7 @@ func TestEqualFork(t *testing.T) {
}
// 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)
@ -215,7 +231,7 @@ func TestBrokenChain(t *testing.T) {
}
bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock()
chainB := makeChain(bman2, parent, 5, db2)
chainB := makeChain(bman2, parent, 5, db2, ForkSeed)
chainB = chainB[1:]
_, err = testChain(chainB, bman)
if err == nil {

View File

@ -121,7 +121,7 @@ func (self *TxPool) add(tx *types.Transaction) error {
if len(tx.From()) > 0 {
from = ethutil.Bytes2Hex(tx.From()[:4])
} else {
from = "INVALID"
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())

View File

@ -48,7 +48,20 @@ type Header struct {
}
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, self.MixDigest, self.SeedHash)
}