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
*/**/*un~
*/**/*.test
*un~
.DS_Store
*/**/.DS_Store

View file

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

View file

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

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

View file

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

View file

@ -44,6 +44,8 @@ func env(block *types.Block, eth core.Backend) *environment {
type Work struct {
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})

View file

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

View file

@ -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 {

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
}
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 {

View file

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