Эх сурвалжийг харах

Reworked chain handling process

* Forks
* Rename
* Moved inserting of blocks & processing
* Added chain testing method for validating pieces of a **a** chain.
obscuren 11 жил өмнө
parent
commit
699dcaf65c

+ 17 - 23
block_pool.go

@@ -313,31 +313,25 @@ out:
 			// If caught up and just a new block has been propagated:
 			// sm.eth.EventMux().Post(NewBlockEvent{block})
 			// otherwise process and don't emit anything
-			var err error
-			for i, block := range blocks {
-				err = self.eth.BlockManager().Process(block)
+			if len(blocks) > 0 {
+				chainManager := self.eth.ChainManager()
+				chain := chain.NewChain(blocks)
+				_, err := chainManager.TestChain(chain)
 				if err != nil {
-					poollogger.Infoln(err)
-					poollogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
-					poollogger.Debugln(block)
-
-					blocks = blocks[i:]
-
-					break
+					self.Reset()
+
+					poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
+					// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
+					self.eth.BlacklistPeer(self.peer)
+					self.peer.StopWithReason(DiscBadPeer)
+					self.td = ethutil.Big0
+					self.peer = nil
+				} else {
+					chainManager.InsertChain(chain)
+					for _, block := range blocks {
+						self.Remove(block.Hash())
+					}
 				}
-
-				self.Remove(block.Hash())
-			}
-
-			if err != nil {
-				self.Reset()
-
-				poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
-				// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
-				self.eth.BlacklistPeer(self.peer)
-				self.peer.StopWithReason(DiscBadPeer)
-				self.td = ethutil.Big0
-				self.peer = nil
 			}
 		}
 	}

+ 27 - 41
chain/block_manager.go

@@ -102,7 +102,7 @@ func (self *BlockManager) Stop() {
 func (self *BlockManager) updateThread() {
 	for ev := range self.events.Chan() {
 		for _, block := range ev.(Blocks) {
-			err := self.Process(block)
+			_, err := self.Process(block)
 			if err != nil {
 				statelogger.Infoln(err)
 				statelogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
@@ -208,25 +208,27 @@ done:
 	return receipts, handled, unhandled, erroneous, err
 }
 
-func (sm *BlockManager) Process(block *Block) (err error) {
+func (sm *BlockManager) Process(block *Block) (td *big.Int, err error) {
 	// Processing a blocks may never happen simultaneously
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
 
 	if sm.bc.HasBlock(block.Hash()) {
-		return nil
+		return nil, nil
 	}
 
 	if !sm.bc.HasBlock(block.PrevHash) {
-		return ParentError(block.PrevHash)
+		return nil, ParentError(block.PrevHash)
 	}
+	parent := sm.bc.GetBlock(block.PrevHash)
+
+	return sm.ProcessWithParent(block, parent)
+}
 
+func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, err error) {
 	sm.lastAttemptedBlock = block
 
-	var (
-		parent = sm.bc.GetBlock(block.PrevHash)
-		state  = parent.State()
-	)
+	state := parent.State()
 
 	// Defer the Undo on the Trie. If the block processing happened
 	// we don't want to undo but since undo only happens on dirty
@@ -240,32 +242,32 @@ func (sm *BlockManager) Process(block *Block) (err error) {
 
 	txSha := DeriveSha(block.transactions)
 	if bytes.Compare(txSha, block.TxSha) != 0 {
-		return fmt.Errorf("Error validating transaction sha. Received %x, got %x", block.TxSha, txSha)
+		return nil, fmt.Errorf("Error validating transaction sha. Received %x, got %x", block.TxSha, txSha)
 	}
 
 	receipts, err := sm.ApplyDiff(state, parent, block)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	receiptSha := DeriveSha(receipts)
 	if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
-		return fmt.Errorf("Error validating receipt sha. Received %x, got %x", block.ReceiptSha, receiptSha)
+		return nil, fmt.Errorf("Error validating receipt sha. Received %x, got %x", block.ReceiptSha, receiptSha)
 	}
 
 	// Block validation
-	if err = sm.ValidateBlock(block); err != nil {
+	if err = sm.ValidateBlock(block, parent); err != nil {
 		statelogger.Errorln("Error validating block:", err)
-		return err
+		return nil, err
 	}
 
 	if err = sm.AccumelateRewards(state, block, parent); err != nil {
 		statelogger.Errorln("Error accumulating reward", err)
-		return err
+		return nil, err
 	}
 
 	if bytes.Compare(CreateBloom(block), block.LogsBloom) != 0 {
-		return errors.New("Unable to replicate block's bloom")
+		return nil, errors.New("Unable to replicate block's bloom")
 	}
 
 	state.Update()
@@ -276,27 +278,22 @@ func (sm *BlockManager) Process(block *Block) (err error) {
 	}
 
 	// Calculate the new total difficulty and sync back to the db
-	if sm.CalculateTD(block) {
+	if td, ok := sm.CalculateTD(block); ok {
 		// Sync the current block's state to the database and cancelling out the deferred Undo
 		state.Sync()
 
-		// Add the block to the chain
-		sm.bc.Add(block)
-
 		// TODO at this point we should also insert LOGS in to a database
 
 		sm.transState = state.Copy()
 
-		statelogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
-
 		state.Manifest().Reset()
 
 		sm.eth.TxPool().RemoveSet(block.Transactions())
+
+		return td, nil
 	} else {
-		statelogger.Errorln("total diff failed")
+		return nil, errors.New("total diff failed")
 	}
-
-	return nil
 }
 
 func (sm *BlockManager) ApplyDiff(state *state.State, parent, block *Block) (receipts Receipts, err error) {
@@ -312,7 +309,7 @@ func (sm *BlockManager) ApplyDiff(state *state.State, parent, block *Block) (rec
 	return receipts, nil
 }
 
-func (sm *BlockManager) CalculateTD(block *Block) bool {
+func (sm *BlockManager) CalculateTD(block *Block) (*big.Int, bool) {
 	uncleDiff := new(big.Int)
 	for _, uncle := range block.Uncles {
 		uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
@@ -326,30 +323,19 @@ func (sm *BlockManager) CalculateTD(block *Block) bool {
 	// The new TD will only be accepted if the new difficulty is
 	// is greater than the previous.
 	if td.Cmp(sm.bc.TD) > 0 {
-		// Set the new total difficulty back to the block chain
-		sm.bc.SetTotalDifficulty(td)
+		return td, true
 
-		return true
+		// Set the new total difficulty back to the block chain
+		//sm.bc.SetTotalDifficulty(td)
 	}
 
-	return false
+	return nil, false
 }
 
 // Validates the current block. Returns an error if the block was invalid,
 // an uncle or anything that isn't on the current block chain.
 // Validation validates easy over difficult (dagger takes longer time = difficult)
-func (sm *BlockManager) ValidateBlock(block *Block) error {
-	// Check each uncle's previous hash. In order for it to be valid
-	// is if it has the same block hash as the current
-	parent := sm.bc.GetBlock(block.PrevHash)
-	/*
-		for _, uncle := range block.Uncles {
-			if bytes.Compare(uncle.PrevHash,parent.PrevHash) != 0 {
-				return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x",parent.PrevHash, uncle.PrevHash)
-			}
-		}
-	*/
-
+func (sm *BlockManager) ValidateBlock(block, parent *Block) error {
 	expd := CalcDifficulty(block, parent)
 	if expd.Cmp(block.Difficulty) < 0 {
 		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)

+ 77 - 3
chain/chain_manager.go

@@ -2,6 +2,7 @@ package chain
 
 import (
 	"bytes"
+	"container/list"
 	"fmt"
 	"math/big"
 
@@ -86,7 +87,7 @@ func (bc *ChainManager) Reset() {
 
 	bc.genesisBlock.state.Trie.Sync()
 	// Prepare the genesis block
-	bc.Add(bc.genesisBlock)
+	bc.add(bc.genesisBlock)
 	bc.CurrentBlock = bc.genesisBlock
 
 	bc.SetTotalDifficulty(ethutil.Big("0"))
@@ -191,9 +192,8 @@ func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
 }
 
 // Add a block to the chain and record addition information
-func (bc *ChainManager) Add(block *Block) {
+func (bc *ChainManager) add(block *Block) {
 	bc.writeBlockInfo(block)
-	// Prepare the genesis block
 
 	bc.CurrentBlock = block
 	bc.LastBlockHash = block.Hash()
@@ -201,6 +201,8 @@ func (bc *ChainManager) Add(block *Block) {
 	encodedBlock := block.RlpEncode()
 	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
 	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
+
+	chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
 }
 
 func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) {
@@ -287,3 +289,75 @@ func (bc *ChainManager) Stop() {
 		chainlogger.Infoln("Stopped")
 	}
 }
+
+type link struct {
+	block *Block
+	td    *big.Int
+}
+
+type BlockChain struct {
+	*list.List
+}
+
+func NewChain(blocks Blocks) *BlockChain {
+	chain := &BlockChain{list.New()}
+
+	for _, block := range blocks {
+		chain.PushBack(&link{block, nil})
+	}
+
+	return chain
+}
+
+// This function assumes you've done your checking. No checking is done at this stage anymore
+func (self *ChainManager) InsertChain(chain *BlockChain) {
+	for e := chain.Front(); e != nil; e = e.Next() {
+		link := e.Value.(*link)
+
+		self.SetTotalDifficulty(link.td)
+		self.add(link.block)
+	}
+}
+
+func (self *ChainManager) TestChain(chain *BlockChain) (i int, err error) {
+	var (
+		td *big.Int
+	)
+	for e := chain.Front(); e != nil; e = e.Next() {
+		var (
+			l      = e.Value.(*link)
+			block  = l.block
+			parent *Block
+			prev   = e.Prev()
+		)
+		if prev == nil {
+			parent = self.GetBlock(block.PrevHash)
+		} else {
+			parent = prev.Value.(*link).block
+		}
+
+		if parent == nil {
+			err = fmt.Errorf("incoming chain broken on hash %x\n", block.PrevHash[0:4])
+			return
+		}
+
+		td, err = self.Ethereum.BlockManager().ProcessWithParent(block, parent)
+		if err != nil {
+			chainlogger.Infoln(err)
+			chainlogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
+			chainlogger.Debugln(block)
+
+			err = fmt.Errorf("incoming chain failed %v\n", err)
+			return
+		}
+		l.td = td
+		i++
+	}
+
+	if td.Cmp(self.TD) <= 0 {
+		err = fmt.Errorf("incoming chain has a lower or equal TD (%v <= %v)", td, self.TD)
+		return
+	}
+
+	return i, nil
+}

+ 5 - 2
miner/miner.go

@@ -162,8 +162,9 @@ func (miner *Miner) stopMining() {
 
 func (self *Miner) mineNewBlock() {
 	blockManager := self.ethereum.BlockManager()
+	chainMan := self.ethereum.ChainManager()
 
-	self.block = self.ethereum.ChainManager().NewBlock(self.coinbase)
+	self.block = chainMan.NewBlock(self.coinbase)
 
 	// Apply uncles
 	if len(self.uncles) > 0 {
@@ -199,10 +200,12 @@ func (self *Miner) mineNewBlock() {
 	nonce := self.pow.Search(self.block, self.powQuitChan)
 	if nonce != nil {
 		self.block.Nonce = nonce
-		err := self.ethereum.BlockManager().Process(self.block)
+		lchain := chain.NewChain(chain.Blocks{self.block})
+		_, err := chainMan.TestChain(lchain)
 		if err != nil {
 			minerlogger.Infoln(err)
 		} else {
+			self.ethereum.ChainManager().InsertChain(lchain)
 			self.ethereum.Broadcast(wire.MsgBlockTy, []interface{}{self.block.Value().Val})
 			minerlogger.Infof("🔨  Mined block %x\n", self.block.Hash())
 			minerlogger.Infoln(self.block)