Introducing ethash

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

1
.gitignore vendored
View file

@ -6,6 +6,7 @@
/tmp /tmp
*/**/*un~ */**/*un~
*/**/*.test
*un~ *un~
.DS_Store .DS_Store
*/**/.DS_Store */**/.DS_Store

View file

@ -5,6 +5,7 @@ before_install:
- sudo add-apt-repository ppa:beineri/opt-qt541 -y - sudo add-apt-repository ppa:beineri/opt-qt541 -y
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine - sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine
- go get github.com/ethereum/ethash/go-ethash
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

View file

@ -7,12 +7,12 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/ethash/go-ethash"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
) )
@ -50,7 +50,7 @@ func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainM
sm := &BlockProcessor{ sm := &BlockProcessor{
db: db, db: db,
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: ezp.New(), Pow: ethash.New(chainManager),
bc: chainManager, bc: chainManager,
eventMux: eventMux, eventMux: eventMux,
txpool: txpool, 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) 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 { if len(block.Uncles()) > 1 {
return ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles())) 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 // So we can generate blocks easily
type FakePow struct{} type FakePow struct{}
func (f FakePow) Search(block pow.Block, stop <-chan struct{}) []byte { return nil } func (f FakePow) Search(block pow.Block, stop <-chan struct{}) ([]byte, []byte, []byte) {
func (f FakePow) Verify(block pow.Block) bool { return true } return nil, nil, nil
func (f FakePow) GetHashrate() int64 { return 0 } }
func (f FakePow) Turbo(bool) {} 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 { func NewBlockFromParent(addr []byte, parent *types.Block) *types.Block {
return newBlockFromParent(addr, parent) return newBlockFromParent(addr, parent)
} }
func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database) *types.Block { func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database, seed int) *types.Block {
return makeBlock(bman, parent, i, db) return makeBlock(bman, parent, i, db, seed)
} }
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 {
return makeChain(bman, parent, max, db) return makeChain(bman, parent, max, db, seed)
} }
func NewChainMan(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager { 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) return newCanonical(n, db)
} }
// block time is fixed at 10 seconds
func newBlockFromParent(addr []byte, parent *types.Block) *types.Block { func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
block := types.NewBlock(parent.Hash(), addr, parent.Root(), ethutil.BigPow(2, 32), nil, "") block := types.NewBlock(parent.Hash(), addr, parent.Root(), ethutil.BigPow(2, 32), nil, "")
block.SetUncles(nil) block.SetUncles(nil)
block.SetTransactions(nil) block.SetTransactions(nil)
block.SetReceipts(nil) block.SetReceipts(nil)
@ -52,6 +62,7 @@ func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
header := block.Header() header := block.Header()
header.Difficulty = CalcDifficulty(block, parent) header.Difficulty = CalcDifficulty(block, parent)
header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1) header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
header.Time = parent.Header().Time + 10
header.GasLimit = CalcGasLimit(parent, block) header.GasLimit = CalcGasLimit(parent, block)
block.Td = parent.Td 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 // 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 := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
addr[0] = byte(seed)
block := newBlockFromParent(addr, parent) block := newBlockFromParent(addr, parent)
state := state.New(block.Root(), db) state := state.New(block.Root(), db)
cbase := state.GetOrNewStateObject(addr) 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 // Make a chain with real blocks
// Runs ProcessWithParent to get proper state roots // 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 bman.bc.currentBlock = parent
blocks := make(types.Blocks, max) blocks := make(types.Blocks, max)
for i := 0; i < max; i++ { 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) td, err := bman.processWithParent(block, parent)
if err != nil { if err != nil {
fmt.Println("process with parent failed", err) 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 block.Td = td
blocks[i] = block blocks[i] = block
parent = block parent = block
fmt.Printf("New Block: %x\n", block.Hash())
} }
fmt.Println("Done making chain")
return blocks return blocks
} }
@ -113,7 +124,7 @@ func newBlockProcessor(db ethutil.Database, txpool *TxPool, cman *ChainManager,
return bman return bman
} }
// Make a new canonical chain by running InsertChain // Make a new, deterministic canonical chain by running InsertChain
// on result of makeChain // on result of makeChain
func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) { func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
eventMux := &event.TypeMux{} eventMux := &event.TypeMux{}
@ -125,7 +136,7 @@ func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
if n == 0 { if n == 0 {
return bman, nil return bman, nil
} }
lchain := makeChain(bman, parent, n, db) lchain := makeChain(bman, parent, n, db, CanonicalSeed)
bman.bc.InsertChain(lchain) err := bman.bc.InsertChain(lchain)
return bman, nil return bman, err
} }

View file

@ -24,12 +24,6 @@ func init() {
// Test fork of length N starting from block i // 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)) { 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 // switch databases to process the new chain
db, err := ethdb.NewMemDatabase() db, err := ethdb.NewMemDatabase()
if err != nil { 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 { if err != nil {
t.Fatal("could not make new canonical in testFork", err) 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) bman2.bc.SetProcessor(bman2)
// extend the fork
parent := bman2.bc.CurrentBlock() parent := bman2.bc.CurrentBlock()
chainB := makeChain(bman2, parent, N, db) chainB := makeChain(bman2, parent, N, db, ForkSeed)
bman2.bc.InsertChain(chainB) err = bman2.bc.InsertChain(chainB)
if err != nil {
t.Fatal("Insert chain error for fork:", err)
}
tdpre := bman.bc.Td() tdpre := bman.bc.Td()
// Test the fork's blocks on the original chain
td, err := testChain(chainB, bman) td, err := testChain(chainB, bman)
if err != nil { if err != nil {
t.Fatal("expected chainB not to give errors:", err) 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) 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) { func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
td := new(big.Int) td := new(big.Int)
for _, block := range chainB { for _, block := range chainB {
@ -102,12 +116,13 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
} }
func TestExtendCanonical(t *testing.T) { func TestExtendCanonical(t *testing.T) {
CanonicalLength := 5
db, err := ethdb.NewMemDatabase() db, err := ethdb.NewMemDatabase()
if err != nil { if err != nil {
t.Fatal("Failed to create db:", err) t.Fatal("Failed to create db:", err)
} }
// make first chain starting from genesis // make first chain starting from genesis
bman, err := newCanonical(5, db) bman, err := newCanonical(CanonicalLength, db)
if err != nil { if err != nil {
t.Fatal("Could not make new canonical chain:", err) 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) t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
} }
} }
// Start fork from current height (5) // Start fork from current height (CanonicalLength)
testFork(t, bman, 5, 1, f) testFork(t, bman, CanonicalLength, 1, f)
testFork(t, bman, 5, 2, f) testFork(t, bman, CanonicalLength, 2, f)
testFork(t, bman, 5, 5, f) testFork(t, bman, CanonicalLength, 5, f)
testFork(t, bman, 5, 10, f) testFork(t, bman, CanonicalLength, 10, f)
} }
func TestShorterFork(t *testing.T) { func TestShorterFork(t *testing.T) {
@ -189,6 +204,7 @@ func TestEqualFork(t *testing.T) {
} }
// Sum of numbers must be equal to 10 // Sum of numbers must be equal to 10
// for this to be an equal fork // for this to be an equal fork
testFork(t, bman, 0, 10, f)
testFork(t, bman, 1, 9, f) testFork(t, bman, 1, 9, f)
testFork(t, bman, 2, 8, f) testFork(t, bman, 2, 8, f)
testFork(t, bman, 5, 5, f) testFork(t, bman, 5, 5, f)
@ -215,7 +231,7 @@ func TestBrokenChain(t *testing.T) {
} }
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock() parent := bman2.bc.CurrentBlock()
chainB := makeChain(bman2, parent, 5, db2) chainB := makeChain(bman2, parent, 5, db2, ForkSeed)
chainB = chainB[1:] chainB = chainB[1:]
_, err = testChain(chainB, bman) _, err = testChain(chainB, bman)
if err == nil { if err == nil {

View file

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

View file

@ -69,8 +69,8 @@ done:
func (self *CpuMiner) mine(block *types.Block) { func (self *CpuMiner) mine(block *types.Block) {
minerlogger.Infof("(re)started agent[%d]. mining...\n", self.index) 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 { if nonce != nil {
self.returnCh <- Work{block.Number().Uint64(), nonce} self.returnCh <- Work{block.Number().Uint64(), nonce, mixDigest, seedHash}
} }
} }

View file

@ -42,8 +42,10 @@ func env(block *types.Block, eth core.Backend) *environment {
} }
type Work struct { type Work struct {
Number uint64 Number uint64
Nonce []byte Nonce []byte
MixDigest []byte
SeedHash []byte
} }
type Agent interface { type Agent interface {
@ -138,9 +140,12 @@ out:
func (self *worker) wait() { func (self *worker) wait() {
for { for {
for work := range self.recv { for work := range self.recv {
// Someone Successfully Mined!
block := self.current.block block := self.current.block
if block.Number().Uint64() == work.Number && block.Nonce() == nil { if block.Number().Uint64() == work.Number && block.Nonce() == nil {
self.current.block.Header().Nonce = work.Nonce 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 { if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil {
self.mux.Post(core.NewMinedBlockEvent{self.current.block}) self.mux.Post(core.NewMinedBlockEvent{self.current.block})

View file

@ -1,12 +1,20 @@
package pow package pow
import "math/big" import (
"github.com/ethereum/go-ethereum/core/types"
"math/big"
)
type Block interface { type Block interface {
Difficulty() *big.Int Difficulty() *big.Int
HashNoNonce() []byte HashNoNonce() []byte
Nonce() []byte Nonce() []byte
Number() *big.Int
MixDigest() []byte MixDigest() []byte
SeedHash() []byte SeedHash() []byte
NumberU64() uint64
}
type ChainManager interface {
GetBlockByNumber(uint64) *types.Block
CurrentBlock() *types.Block
} }

View file

@ -44,7 +44,7 @@ func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
resChan <- 0 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 // TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines := 1 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 { func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {

View file

@ -1,5 +0,0 @@
extern char *Sha3(char *, int);
char *sha3_cgo(char *data, int l)
{
return Sha3(data, l);
}

View file

@ -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)
}

View file

@ -32,7 +32,7 @@ func (pow *EasyPow) Turbo(on bool) {
pow.turbo = on 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())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce() hash := block.HashNoNonce()
diff := block.Difficulty() diff := block.Difficulty()
@ -57,7 +57,7 @@ empty:
for { for {
select { select {
case <-stop: case <-stop:
return nil return nil, nil, nil
default: default:
i++ i++
@ -67,7 +67,7 @@ empty:
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes()) sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
if verify(hash, diff, sha) { if verify(hash, diff, sha) {
return sha return sha, nil, nil
} }
} }
@ -75,8 +75,6 @@ empty:
time.Sleep(20 * time.Microsecond) time.Sleep(20 * time.Microsecond)
} }
} }
return nil
} }
func (pow *EasyPow) Verify(block pow.Block) bool { func (pow *EasyPow) Verify(block pow.Block) bool {

View file

@ -1,7 +1,7 @@
package pow package pow
type PoW interface { type PoW interface {
Search(block Block, stop <-chan struct{}) []byte Search(block Block, stop <-chan struct{}) ([]byte, []byte, []byte)
Verify(block Block) bool Verify(block Block) bool
GetHashrate() int64 GetHashrate() int64
Turbo(bool) Turbo(bool)