From ae837c4719855384921fcaadb1a575942dc9833d Mon Sep 17 00:00:00 2001 From: Maran Date: Thu, 20 Mar 2014 11:20:29 +0100 Subject: [PATCH] More mining rework --- ethchain/block.go | 3 + ethchain/block_chain.go | 3 +- ethchain/dagger.go | 16 ++-- ethchain/state_manager.go | 28 ++----- ethchain/transaction_pool.go | 9 +-- ethminer/miner.go | 149 +++++++++++++++++++++++++++++++++++ peer.go | 2 +- 7 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 ethminer/miner.go diff --git a/ethchain/block.go b/ethchain/block.go index 20af73ba2..d42aa7d83 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -304,6 +304,9 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block { func (block *Block) String() string { return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions)) } +func (block *Block) GetRoot() interface{} { + return block.state.trie.Root +} //////////// UNEXPORTED ///////////////// func (block *Block) header() []interface{} { diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 93970a2c5..90ad4516a 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -44,7 +44,6 @@ func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { hash = bc.LastBlockHash lastBlockTime = bc.CurrentBlock.Time } - block := CreateBlock( root, hash, @@ -181,8 +180,8 @@ func (bc *BlockChain) SetTotalDifficulty(td *big.Int) { // Add a block to the chain and record addition information func (bc *BlockChain) Add(block *Block) { bc.writeBlockInfo(block) - // Prepare the genesis block + bc.CurrentBlock = block bc.LastBlockHash = block.Hash() diff --git a/ethchain/dagger.go b/ethchain/dagger.go index 4d2034e20..a80a9d421 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -11,7 +11,7 @@ import ( ) type PoW interface { - Search(block *Block, minerChan chan ethutil.React) []byte + Search(block *Block, reactChan chan ethutil.React) []byte Verify(hash []byte, diff *big.Int, nonce []byte) bool } @@ -19,7 +19,7 @@ type EasyPow struct { hash *big.Int } -func (pow *EasyPow) Search(block *Block, minerChan chan ethutil.React) []byte { +func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte { r := rand.New(rand.NewSource(time.Now().UnixNano())) hash := block.HashNoNonce() diff := block.Difficulty @@ -28,15 +28,9 @@ func (pow *EasyPow) Search(block *Block, minerChan chan ethutil.React) []byte { for { select { - case chanMessage := <-minerChan: - if _, ok := chanMessage.Resource.(*Block); ok { - log.Println("BREAKING OUT: BLOCK") - return nil - } - if _, ok := chanMessage.Resource.(*Transaction); ok { - log.Println("BREAKING OUT: TX") - return nil - } + case <-reactChan: + log.Println("[pow] Received reactor event; breaking out.") + return nil default: i++ if i%1234567 == 0 { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 3be940745..46d8228d9 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -50,9 +50,6 @@ type StateManager struct { // Comparative state it used for comparing and validating end // results compState *State - - // Mining state, solely used for mining - miningState *State } func NewStateManager(ethereum EthManager) *StateManager { @@ -65,7 +62,6 @@ func NewStateManager(ethereum EthManager) *StateManager { bc: ethereum.BlockChain(), } sm.procState = ethereum.BlockChain().CurrentBlock.State() - return sm } @@ -73,10 +69,6 @@ func (sm *StateManager) ProcState() *State { return sm.procState } -func (sm *StateManager) MiningState() *State { - return sm.miningState -} - // Watches any given address and puts it in the address state store func (sm *StateManager) WatchAddr(addr []byte) *AccountState { //XXX account := sm.bc.CurrentBlock.state.GetAccount(addr) @@ -105,8 +97,6 @@ func (sm *StateManager) MakeContract(tx *Transaction) { sm.procState.states[string(tx.Hash()[12:])] = contract.state } } -func (sm *StateManager) ApplyTransaction(block *Block, tx *Transaction) { -} func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract @@ -136,17 +126,13 @@ func (sm *StateManager) Prepare(processer *State, comparative *State) { sm.procState = processer } -func (sm *StateManager) PrepareMiningState() { - sm.miningState = sm.BlockChain().CurrentBlock.State() -} - // Default prepare function func (sm *StateManager) PrepareDefault(block *Block) { sm.Prepare(sm.BlockChain().CurrentBlock.State(), block.State()) } // Block processing and validating with a given (temporarily) state -func (sm *StateManager) ProcessBlock(block *Block) error { +func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() @@ -155,7 +141,6 @@ func (sm *StateManager) ProcessBlock(block *Block) error { // nodes this won't happen because Commit would have been called // before that. defer sm.bc.CurrentBlock.Undo() - hash := block.Hash() if sm.bc.HasBlock(hash) { @@ -207,7 +192,9 @@ func (sm *StateManager) ProcessBlock(block *Block) error { } ethutil.Config.Log.Infof("[STATE] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) - sm.Ethereum.Reactor().Post("newBlock", block) + if dontReact == false { + sm.Ethereum.Reactor().Post("newBlock", block) + } } else { fmt.Println("total diff failed") } @@ -285,15 +272,16 @@ func CalculateUncleReward(block *Block) *big.Int { } func (sm *StateManager) AccumelateRewards(block *Block) error { + // Get the coinbase rlp data //XXX addr := processor.state.GetAccount(block.Coinbase) addr := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) - //XXX processor.state.UpdateAccount(block.Coinbase, addr) - sm.procState.UpdateAccount(block.Coinbase, addr) - + var acc []byte + copy(acc, block.Coinbase) + sm.procState.UpdateAccount(acc, addr) for _, uncle := range block.Uncles { uncleAddr := sm.procState.GetAccount(uncle.Coinbase) uncleAddr.AddFee(CalculateUncleReward(uncle)) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index b0df1b6c0..26827c289 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,7 +91,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { - log.Println("Processing TX") defer func() { if r := recover(); r != nil { log.Println(r) @@ -105,11 +104,11 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error // funds won't invalidate this transaction but simple ignores it. totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) if sender.Amount.Cmp(totAmount) < 0 { - return errors.New("Insufficient amount in sender's account") + return errors.New("[TXPL] Insufficient amount in sender's account") } if sender.Nonce != tx.Nonce { - return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce) + return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transactoin nonce is %d instead", sender.Nonce, tx.Nonce) } // Get the receiver @@ -145,7 +144,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { block := pool.Ethereum.BlockChain().CurrentBlock // Something has gone horribly wrong if this happens if block == nil { - return errors.New("No last block on the block chain") + return errors.New("[TXPL] No last block on the block chain") } // Get the sender @@ -156,7 +155,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. if sender.Amount.Cmp(totAmount) < 0 { - return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender()) + return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } // Increment the nonce making each tx valid only once to prevent replay diff --git a/ethminer/miner.go b/ethminer/miner.go new file mode 100644 index 000000000..f4f697aba --- /dev/null +++ b/ethminer/miner.go @@ -0,0 +1,149 @@ +package ethminer + +import ( + "bytes" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" + "log" +) + +type Miner struct { + pow ethchain.PoW + ethereum ethchain.EthManager + coinbase []byte + reactChan chan ethutil.React + txs []*ethchain.Transaction + uncles []*ethchain.Block + block *ethchain.Block + powChan chan []byte + quitChan chan ethutil.React +} + +func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner { + reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in + powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block + quitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread + + ethereum.Reactor().Subscribe("newBlock", reactChan) + ethereum.Reactor().Subscribe("newTx", reactChan) + + // We need the quit chan to be a Reactor event. + // The POW search method is actually blocking and if we don't + // listen to the reactor events inside of the pow itself + // The miner overseer will never get the reactor events themselves + // Only after the miner will find the sha + ethereum.Reactor().Subscribe("newBlock", quitChan) + ethereum.Reactor().Subscribe("newTx", quitChan) + + miner := Miner{ + pow: ðchain.EasyPow{}, + ethereum: ethereum, + coinbase: coinbase, + reactChan: reactChan, + powChan: powChan, + quitChan: quitChan, + } + + // Insert initial TXs in our little miner 'pool' + miner.txs = ethereum.TxPool().Flush() + miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + + return miner +} +func (miner *Miner) Start() { + // Prepare inital block + miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) + go func() { miner.listener() }() +} +func (miner *Miner) listener() { + for { + select { + case chanMessage := <-miner.reactChan: + if block, ok := chanMessage.Resource.(*ethchain.Block); ok { + log.Println("[miner] Got new block via Reactor") + if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { + // TODO: Perhaps continue mining to get some uncle rewards + log.Println("[miner] New top block found resetting state") + + // Filter out which Transactions we have that were not in this block + var newtxs []*ethchain.Transaction + for _, tx := range miner.txs { + found := false + for _, othertx := range block.Transactions() { + if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 { + found = true + } + } + if found == false { + newtxs = append(newtxs, tx) + } + } + miner.txs = newtxs + + // Setup a fresh state to mine on + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + + } else { + if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { + log.Println("[miner] Adding uncle block") + miner.uncles = append(miner.uncles, block) + miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) + } + } + } + + if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok { + log.Println("[miner] Got new transaction from Reactor", tx) + found := false + for _, ctx := range miner.txs { + if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found { + break + } + + } + if found == false { + log.Println("[miner] We did not know about this transaction, adding") + miner.txs = append(miner.txs, tx) + miner.block.SetTransactions(miner.txs) + } else { + log.Println("[miner] We already had this transaction, ignoring") + } + } + default: + log.Println("[miner] Mining on block. Includes", len(miner.txs), "transactions") + + // Apply uncles + if len(miner.uncles) > 0 { + miner.block.SetUncles(miner.uncles) + } + + // Apply all transactions to the block + miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions()) + miner.ethereum.StateManager().AccumelateRewards(miner.block) + + // Search the nonce + log.Println("[miner] Initialision complete, starting mining") + miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan) + if miner.block.Nonce != nil { + miner.ethereum.StateManager().PrepareDefault(miner.block) + err := miner.ethereum.StateManager().ProcessBlock(miner.block, true) + if err != nil { + log.Println("Error result from process block:", err) + log.Println(miner.block) + } else { + + if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { + log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) + } + miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) + log.Printf("[miner] 🔨 Mined block %x\n", miner.block.Hash()) + log.Println(miner.block) + + miner.txs = []*ethchain.Transaction{} // Move this somewhere neat + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + } + } + } + } +} diff --git a/peer.go b/peer.go index 4e927ada4..6b914710d 100644 --- a/peer.go +++ b/peer.go @@ -295,7 +295,7 @@ func (p *Peer) HandleInbound() { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) p.ethereum.StateManager().PrepareDefault(block) - err = p.ethereum.StateManager().ProcessBlock(block) + err = p.ethereum.StateManager().ProcessBlock(block, true) if err != nil { if ethutil.Config.Debug {