From ef0ebde4f524987e35fd5f76411539152c66d09d Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Sat, 28 Feb 2015 14:58:37 -0500 Subject: [PATCH] Introducing ethash --- .gitignore | 1 + .travis.yml | 1 + core/block_processor.go | 5 ++-- core/chain_makers.go | 47 +++++++++++++++++++++++--------------- core/chain_manager_test.go | 46 +++++++++++++++++++++++++------------ core/transaction_pool.go | 2 +- core/types/block.go | 15 +++++++++++- miner/agent.go | 4 ++-- miner/worker.go | 9 ++++++-- pow/block.go | 12 ++++++++-- pow/dagger/dagger.go | 4 ++-- pow/dash/crypto.c | 5 ---- pow/dash/crypto.go | 14 ------------ pow/ezp/pow.go | 8 +++---- pow/pow.go | 2 +- 15 files changed, 105 insertions(+), 70 deletions(-) delete mode 100644 pow/dash/crypto.c delete mode 100644 pow/dash/crypto.go diff --git a/.gitignore b/.gitignore index e2aa4de9569c..0cd8a23c45a4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /tmp */**/*un~ +*/**/*.test *un~ .DS_Store */**/.DS_Store diff --git a/.travis.yml b/.travis.yml index c4e39b05eaba..5901c9d93eee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ before_install: - sudo add-apt-repository ppa:beineri/opt-qt541 -y - sudo apt-get update -qq - sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine + - go get github.com/ethereum/ethash/go-ethash install: # - go get code.google.com/p/go.tools/cmd/goimports # - go get github.com/golang/lint/golint diff --git a/core/block_processor.go b/core/block_processor.go index aef53c940417..e91c9c9f7d9a 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -7,12 +7,12 @@ import ( "sync" "time" + "github.com/ethereum/ethash/go-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())) } diff --git a/core/chain_makers.go b/core/chain_makers.go index eb43f8aa080e..2c36b892e544 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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 } diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index e6614212f2c7..b562b677d280 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -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 { diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 860f57dc3096..bd377f67900f 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -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()) diff --git a/core/types/block.go b/core/types/block.go index f637b5c3b8e4..a37038f73931 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -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) } diff --git a/miner/agent.go b/miner/agent.go index 9046f5d5ab1c..5b2bfdb06ca9 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -69,8 +69,8 @@ done: func (self *CpuMiner) mine(block *types.Block) { minerlogger.Infof("(re)started agent[%d]. mining...\n", self.index) - nonce := self.pow.Search(block, self.quitCurrentOp) + nonce, mixDigest, seedHash := self.pow.Search(block, self.quitCurrentOp) if nonce != nil { - self.returnCh <- Work{block.Number().Uint64(), nonce} + self.returnCh <- Work{block.Number().Uint64(), nonce, mixDigest, seedHash} } } diff --git a/miner/worker.go b/miner/worker.go index 4f0909302c55..201367fdf9fb 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -42,8 +42,10 @@ func env(block *types.Block, eth core.Backend) *environment { } type Work struct { - Number uint64 - Nonce []byte + Number uint64 + Nonce []byte + MixDigest []byte + SeedHash []byte } type Agent interface { @@ -138,9 +140,12 @@ out: func (self *worker) wait() { for { for work := range self.recv { + // Someone Successfully Mined! block := self.current.block if block.Number().Uint64() == work.Number && block.Nonce() == nil { self.current.block.Header().Nonce = work.Nonce + self.current.block.Header().MixDigest = work.MixDigest + self.current.block.Header().SeedHash = work.SeedHash if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil { self.mux.Post(core.NewMinedBlockEvent{self.current.block}) diff --git a/pow/block.go b/pow/block.go index 129f96fd3218..eb07bc86e7b7 100644 --- a/pow/block.go +++ b/pow/block.go @@ -1,12 +1,20 @@ package pow -import "math/big" +import ( + "github.com/ethereum/go-ethereum/core/types" + "math/big" +) type Block interface { Difficulty() *big.Int HashNoNonce() []byte Nonce() []byte - Number() *big.Int MixDigest() []byte SeedHash() []byte + NumberU64() uint64 +} + +type ChainManager interface { + GetBlockByNumber(uint64) *types.Block + CurrentBlock() *types.Block } diff --git a/pow/dagger/dagger.go b/pow/dagger/dagger.go index 310f8abdddfc..3da7683d564b 100644 --- a/pow/dagger/dagger.go +++ b/pow/dagger/dagger.go @@ -44,7 +44,7 @@ func (dag *Dagger) Find(obj *big.Int, resChan chan int64) { resChan <- 0 } -func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { +func (dag *Dagger) Search(hash, diff *big.Int) ([]byte, []byte, []byte) { // TODO fix multi threading. Somehow it results in the wrong nonce amountOfRoutines := 1 @@ -69,7 +69,7 @@ func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { } } - return big.NewInt(res) + return big.NewInt(res).Bytes(), nil, nil } func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool { diff --git a/pow/dash/crypto.c b/pow/dash/crypto.c deleted file mode 100644 index 9c5a62d16aaf..000000000000 --- a/pow/dash/crypto.c +++ /dev/null @@ -1,5 +0,0 @@ -extern char *Sha3(char *, int); -char *sha3_cgo(char *data, int l) -{ - return Sha3(data, l); -} diff --git a/pow/dash/crypto.go b/pow/dash/crypto.go deleted file mode 100644 index 0644a54ae5e6..000000000000 --- a/pow/dash/crypto.go +++ /dev/null @@ -1,14 +0,0 @@ -package dash - -/* -char *sha3_cgo(char *, int); // Forward declaration -*/ -import "C" -import ( - "github.com/ethereum/go-ethereum/crypto" -) - -//export Sha3 -func Sha3(data []byte, l int) []byte { - return crypto.Sha3(data) -} diff --git a/pow/ezp/pow.go b/pow/ezp/pow.go index 8808d7ce0902..49854c3d0276 100644 --- a/pow/ezp/pow.go +++ b/pow/ezp/pow.go @@ -32,7 +32,7 @@ func (pow *EasyPow) Turbo(on bool) { pow.turbo = on } -func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte { +func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) ([]byte, []byte, []byte) { r := rand.New(rand.NewSource(time.Now().UnixNano())) hash := block.HashNoNonce() diff := block.Difficulty() @@ -57,7 +57,7 @@ empty: for { select { case <-stop: - return nil + return nil, nil, nil default: i++ @@ -67,7 +67,7 @@ empty: sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes()) if verify(hash, diff, sha) { - return sha + return sha, nil, nil } } @@ -75,8 +75,6 @@ empty: time.Sleep(20 * time.Microsecond) } } - - return nil } func (pow *EasyPow) Verify(block pow.Block) bool { diff --git a/pow/pow.go b/pow/pow.go index c94ee40ba454..11aecbd6bc9d 100644 --- a/pow/pow.go +++ b/pow/pow.go @@ -1,7 +1,7 @@ package pow type PoW interface { - Search(block Block, stop <-chan struct{}) []byte + Search(block Block, stop <-chan struct{}) ([]byte, []byte, []byte) Verify(block Block) bool GetHashrate() int64 Turbo(bool)