From 1e930fdb95a38e096f3bd0972f0e0b77eee1ed8f Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Tue, 27 Nov 2018 16:02:22 +0700 Subject: [PATCH 01/13] refactor RW & pairRW connect for block , transaction --- core/tx_pool.go | 30 +++++-------- core/types/transaction_test.go | 2 +- eth/fetcher/fetcher.go | 2 +- eth/handler.go | 35 +-------------- eth/helper_test.go | 4 -- eth/peer.go | 78 ++++++++++++++++++++++++---------- eth/protocol.go | 1 - eth/sync.go | 18 ++++---- params/version.go | 6 +-- 9 files changed, 82 insertions(+), 94 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 8cae8434e4..6f42149382 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -188,17 +188,16 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig { // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - config TxPoolConfig - chainconfig *params.ChainConfig - chain blockChain - gasPrice *big.Int - txFeed event.Feed - specialTxFeed event.Feed - scope event.SubscriptionScope - chainHeadCh chan ChainHeadEvent - chainHeadSub event.Subscription - signer types.Signer - mu sync.RWMutex + config TxPoolConfig + chainconfig *params.ChainConfig + chain blockChain + gasPrice *big.Int + txFeed event.Feed + scope event.SubscriptionScope + chainHeadCh chan ChainHeadEvent + chainHeadSub event.Subscription + signer types.Signer + mu sync.RWMutex currentState *state.StateDB // Current state in the blockchain head pendingState *state.ManagedState // Pending state tracking virtual nonces @@ -458,12 +457,6 @@ func (pool *TxPool) SubscribeTxPreEvent(ch chan<- TxPreEvent) event.Subscription return pool.scope.Track(pool.txFeed.Subscribe(ch)) } -// SubscribeSpecialTxPreEvent registers a subscription of TxPreEvent and -// starts sending event to the given channel. -func (pool *TxPool) SubscribeSpecialTxPreEvent(ch chan<- TxPreEvent) event.Subscription { - return pool.scope.Track(pool.specialTxFeed.Subscribe(ch)) -} - // GasPrice returns the current gas price enforced by the transaction pool. func (pool *TxPool) GasPrice() *big.Int { pool.mu.RLock() @@ -830,8 +823,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction) broadcastTxs = append(broadcastTxs, tx) go func() { for _, btx := range broadcastTxs { - pool.specialTxFeed.Send(TxPreEvent{btx}) - log.Trace("Pooled new special transaction", "hash", tx.Hash(), "from", addr, "to", tx.To(), "nonce", tx.Nonce()) + pool.txFeed.Send(TxPreEvent{btx}) } }() return true, nil diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 4e74a0e9b8..6539f69a3f 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -144,7 +144,7 @@ func TestTransactionPriceNonceSort(t *testing.T) { } } // Sort the transactions and cross check the nonce ordering - txset, _ := NewTransactionsByPriceAndNonce(signer, groups,nil) + txset, _ := NewTransactionsByPriceAndNonce(signer, groups, nil) txs := Transactions{} for tx := txset.Peek(); tx != nil; tx = txset.Peek() { diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index dbb7de94d4..36e9568f34 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -704,7 +704,7 @@ func (f *Fetcher) insert(peer string, block *types.Block) { // If import succeeded, broadcast the block propAnnounceOutTimer.UpdateSince(block.ReceivedAt) go f.broadcastBlock(block, true) - go f.broadcastBlock(block, false) + //go f.broadcastBlock(block, false) }() } diff --git a/eth/handler.go b/eth/handler.go index 181ffa704e..9ca0edf261 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -82,8 +82,6 @@ type ProtocolManager struct { eventMux *event.TypeMux txCh chan core.TxPreEvent txSub event.Subscription - specialTxCh chan core.TxPreEvent - specialTxSub event.Subscription minedBlockSub *event.TypeMuxSubscription // channels for fetcher, syncer, txsyncLoop @@ -209,11 +207,6 @@ func (pm *ProtocolManager) Start(maxPeers int) { pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh) go pm.txBroadcastLoop() - // broadcast special transactions - pm.specialTxCh = make(chan core.TxPreEvent, txChanSize) - pm.specialTxSub = pm.txpool.SubscribeSpecialTxPreEvent(pm.specialTxCh) - go pm.specialTxBroadcastLoop() - // broadcast mined blocks pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{}) go pm.minedBroadcastLoop() @@ -227,7 +220,6 @@ func (pm *ProtocolManager) Stop() { log.Info("Stopping Ethereum protocol") pm.txSub.Unsubscribe() // quits txBroadcastLoop - pm.specialTxSub.Unsubscribe() // quits specialTxBroadcastLoop pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop // Quit the sync loop. @@ -735,24 +727,14 @@ func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction) log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers)) } -func (pm *ProtocolManager) BroadcastSpecialTx(hash common.Hash, tx *types.Transaction) { - // Broadcast transaction to a batch of peers not knowing about it - peers := pm.peers.PeersWithoutTx(hash) - //FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))] - for _, peer := range peers { - peer.SendSpecialTransactions(tx) - } - log.Trace("Broadcast special transaction", "hash", hash, "recipients", len(peers)) -} - // Mined broadcast loop func (self *ProtocolManager) minedBroadcastLoop() { // automatically stops if unsubscribe for obj := range self.minedBlockSub.Chan() { switch ev := obj.Data.(type) { case core.NewMinedBlockEvent: - self.BroadcastBlock(ev.Block, true) // First propagate block to peers - self.BroadcastBlock(ev.Block, false) // Only then announce to the rest + self.BroadcastBlock(ev.Block, true) // First propagate block to peers + //self.BroadcastBlock(ev.Block, false) // Only then announce to the rest } } } @@ -770,19 +752,6 @@ func (self *ProtocolManager) txBroadcastLoop() { } } -func (self *ProtocolManager) specialTxBroadcastLoop() { - for { - select { - case event := <-self.specialTxCh: - self.BroadcastSpecialTx(event.Tx.Hash(), event.Tx) - - // Err() channel will be closed when unsubscribing. - case <-self.specialTxSub.Err(): - return - } - } -} - // NodeInfo represents a short summary of the Ethereum sub-protocol metadata // known about the host peer. type NodeInfo struct { diff --git a/eth/helper_test.go b/eth/helper_test.go index ca686df808..2b05cea801 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -128,10 +128,6 @@ func (p *testTxPool) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscr return p.txFeed.Subscribe(ch) } -func (p *testTxPool) SubscribeSpecialTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription { - return p.txFeed.Subscribe(ch) -} - // newTestTransaction create a new dummy transaction. func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction { tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize)) diff --git a/eth/peer.go b/eth/peer.go index 75813fe93a..47a1075486 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "gopkg.in/fatih/set.v0" @@ -141,15 +140,6 @@ func (p *peer) SendTransactions(txs types.Transactions) error { return p2p.Send(p.rw, TxMsg, txs) } -func (p *peer) SendSpecialTransactions(tx *types.Transaction) error { - p.knownTxs.Add(tx.Hash()) - if p.pairRw != nil { - return p2p.Send(p.pairRw, TxMsg, types.Transactions{tx}) - } else { - return p2p.Send(p.rw, TxMsg, types.Transactions{tx}) - } -} - // SendNewBlockHashes announces the availability of a number of blocks through // a hash notification. func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { @@ -168,81 +158,123 @@ func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { p.knownBlocks.Add(block.Hash()) if p.pairRw != nil { - log.Trace("p2p send new block to the pairRw connection", "p", p, "number", block.NumberU64()) return p2p.Send(p.pairRw, NewBlockMsg, []interface{}{block, td}) } else { return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) } - } // SendBlockHeaders sends a batch of block headers to the remote peer. func (p *peer) SendBlockHeaders(headers []*types.Header) error { - return p2p.Send(p.rw, BlockHeadersMsg, headers) + if p.pairRw != nil { + return p2p.Send(p.pairRw, BlockHeadersMsg, headers) + } else { + return p2p.Send(p.rw, BlockHeadersMsg, headers) + } } // SendBlockBodies sends a batch of block contents to the remote peer. func (p *peer) SendBlockBodies(bodies []*blockBody) error { - return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) + if p.pairRw != nil { + return p2p.Send(p.pairRw, BlockBodiesMsg, blockBodiesData(bodies)) + } else { + return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) + } } // SendBlockBodiesRLP sends a batch of block contents to the remote peer from // an already RLP encoded format. func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { - return p2p.Send(p.rw, BlockBodiesMsg, bodies) + if p.pairRw != nil { + return p2p.Send(p.pairRw, BlockBodiesMsg, bodies) + } else { + return p2p.Send(p.rw, BlockBodiesMsg, bodies) + } } // SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the // hashes requested. func (p *peer) SendNodeData(data [][]byte) error { - return p2p.Send(p.rw, NodeDataMsg, data) + if p.pairRw != nil { + return p2p.Send(p.pairRw, NodeDataMsg, data) + } else { + return p2p.Send(p.rw, NodeDataMsg, data) + } } // SendReceiptsRLP sends a batch of transaction receipts, corresponding to the // ones requested from an already RLP encoded format. func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error { - return p2p.Send(p.rw, ReceiptsMsg, receipts) + if p.pairRw != nil { + return p2p.Send(p.pairRw, ReceiptsMsg, receipts) + } else { + return p2p.Send(p.rw, ReceiptsMsg, receipts) + } } // RequestOneHeader is a wrapper around the header query functions to fetch a // single header. It is used solely by the fetcher. func (p *peer) RequestOneHeader(hash common.Hash) error { p.Log().Debug("Fetching single header", "hash", hash) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) + } else { + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) + } } // RequestHeadersByHash fetches a batch of blocks' headers corresponding to the // specified header query, based on the hash of an origin block. func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + } else { + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + } } // RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the // specified header query, based on the number of an origin block. func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + } else { + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) + } } // RequestBodies fetches a batch of blocks' bodies corresponding to the hashes // specified. func (p *peer) RequestBodies(hashes []common.Hash) error { p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) - return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetBlockBodiesMsg, hashes) + } else { + return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) + } } // RequestNodeData fetches a batch of arbitrary data from a node's known state // data, corresponding to the specified hashes. func (p *peer) RequestNodeData(hashes []common.Hash) error { p.Log().Debug("Fetching batch of state data", "count", len(hashes)) - return p2p.Send(p.rw, GetNodeDataMsg, hashes) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetNodeDataMsg, hashes) + } else { + return p2p.Send(p.rw, GetNodeDataMsg, hashes) + } } // RequestReceipts fetches a batch of transaction receipts from a remote node. func (p *peer) RequestReceipts(hashes []common.Hash) error { p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) - return p2p.Send(p.rw, GetReceiptsMsg, hashes) + if p.pairRw != nil { + return p2p.Send(p.pairRw, GetReceiptsMsg, hashes) + } else { + return p2p.Send(p.rw, GetReceiptsMsg, hashes) + } } // Handshake executes the eth protocol handshake, negotiating version number, diff --git a/eth/protocol.go b/eth/protocol.go index f44a01f020..cd7db57f23 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -106,7 +106,6 @@ type txPool interface { // SubscribeTxPreEvent should return an event subscription of // TxPreEvent and send events to the given channel. SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription - SubscribeSpecialTxPreEvent(chan<- core.TxPreEvent) event.Subscription } // statusData is the network packet for the status message. diff --git a/eth/sync.go b/eth/sync.go index 89f1f15c51..5072aad775 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -205,13 +205,13 @@ func (pm *ProtocolManager) synchronise(peer *peer) { atomic.StoreUint32(&pm.fastSync, 0) } atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done - if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { - // We've completed a sync cycle, notify all peers of new state. This path is - // essential in star-topology networks where a gateway node needs to notify - // all its out-of-date peers of the availability of a new block. This failure - // scenario will most often crop up in private and hackathon networks with - // degenerate connectivity, but it should be healthy for the mainnet too to - // more reliably update peers or the local TD state. - go pm.BroadcastBlock(head, false) - } + //if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { + // // We've completed a sync cycle, notify all peers of new state. This path is + // // essential in star-topology networks where a gateway node needs to notify + // // all its out-of-date peers of the availability of a new block. This failure + // // scenario will most often crop up in private and hackathon networks with + // // degenerate connectivity, but it should be healthy for the mainnet too to + // // more reliably update peers or the local TD state. + // go pm.BroadcastBlock(head, false) + //} } diff --git a/params/version.go b/params/version.go index 6e2690c62d..5b74e2a1b2 100644 --- a/params/version.go +++ b/params/version.go @@ -21,9 +21,9 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 0 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionMajor = 1 // Major version component of the current release + VersionMinor = 0 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string ) From a0d36b2fe439a7f3f0444c0d32f56576e84eb7a1 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Tue, 4 Dec 2018 14:10:18 +0700 Subject: [PATCH 02/13] fix err concurrent map read and write with signers --- consensus/posv/posv.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 142aec836f..7a61c3194c 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -806,23 +806,14 @@ func (c *Posv) UpdateMasternodes(chain consensus.ChainReader, header *types.Head if err != nil { return err } - currentSigners := snap.GetSigners() - proposedSigners := make(map[common.Address]struct{}) - // count all addresses in ms to be masternode + newSigners := make(map[common.Address]struct{}) for _, m := range ms { - proposedSigners[m.Address] = struct{}{} - snap.Signers[m.Address] = struct{}{} - } - // deactivate current masternodes which aren't in ms - for _, s := range currentSigners { - if _, ok := proposedSigners[s]; !ok { - delete(snap.Signers, s) - } + newSigners[m.Address] = struct{}{} } + snap.Signers = newSigners nm := []string{} - newSigners := snap.GetSigners() - for _, n := range newSigners { - nm = append(nm, n.String()) + for _, n := range ms { + nm = append(nm, n.Address.String()) } c.recents.Add(snap.Hash, snap) log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm) From c293ca57ecdeae5abc891027a2dd4d5a2eba9774 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Sat, 1 Dec 2018 11:52:21 +0700 Subject: [PATCH 03/13] fix err download block on masternode --- cmd/tomo/main.go | 1 + cmd/utils/flags.go | 8 ++++ eth/backend.go | 2 +- eth/downloader/queue.go | 2 - miner/miner.go | 9 +--- miner/worker.go | 100 +++++++++++++++++++++++++--------------- node/config.go | 2 + 7 files changed, 77 insertions(+), 47 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 48a4bdcc4f..0d7d60991c 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -120,6 +120,7 @@ var ( //utils.GpoPercentileFlag, //utils.ExtraDataFlag, configFileFlag, + utils.CommitTxWhenNotMiningFlag, } rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 27c3077242..082c6a7e54 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -113,6 +113,11 @@ func NewApp(gitCommit, usage string) *cli.App { var ( // General settings + CommitTxWhenNotMiningFlag = DirectoryFlag{ + Name: "committxwhennotmining", + Usage: "Always commit transactions", + Value: DirectoryString{node.DefaultDataDir()}, + } DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", @@ -897,6 +902,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(NoUSBFlag.Name) { cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) } + if ctx.GlobalIsSet(CommitTxWhenNotMiningFlag.Name) { + cfg.CommitTxWhenNotMining = ctx.GlobalBool(CommitTxWhenNotMiningFlag.Name) + } } func setGPO(ctx *cli.Context, cfg *gasprice.Config) { diff --git a/eth/backend.go b/eth/backend.go index f3790b99db..28216abe2c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -173,7 +173,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, ctx.GetConfig().CommitTxWhenNotMining) eth.miner.SetExtra(makeExtraData(config.ExtraData)) eth.ApiBackend = &EthApiBackend{eth, nil} diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 359cce54b5..8e6c91166e 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -146,9 +146,7 @@ func (q *queue) Reset() { // Close marks the end of the sync, unblocking WaitResults. // It may be called even if the queue is already closed. func (q *queue) Close() { - q.lock.Lock() q.closed = true - q.lock.Unlock() q.active.Broadcast() } diff --git a/miner/miner.go b/miner/miner.go index d9256e9787..021099e6a3 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -57,12 +57,12 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, commitTxWhenNotMining bool) *Miner { miner := &Miner{ eth: eth, mux: mux, engine: engine, - worker: newWorker(config, engine, common.Address{}, eth, mux), + worker: newWorker(config, engine, common.Address{}, eth, mux, commitTxWhenNotMining), canStart: 1, } miner.Register(NewCpuAgent(eth.BlockChain(), engine)) @@ -77,7 +77,6 @@ func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine con // and halt your mining operation for as long as the DOS continues. func (self *Miner) update() { events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) -out: for ev := range events.Chan() { switch ev.Data.(type) { case downloader.StartEvent: @@ -95,10 +94,6 @@ out: if shouldStart { self.Start(self.coinbase) } - // unsubscribe. we're only interested in this event once - events.Unsubscribe() - // stop immediately and ignore all further pending events - break out } } } diff --git a/miner/worker.go b/miner/worker.go index a774cd1a06..8081d53741 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -130,30 +130,35 @@ type worker struct { unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations // atomic status counters - mining int32 - atWork int32 + mining int32 + atWork int32 + commitTxWhenNotMining bool + lastParentBlockCommit string } -func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { +func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux, commitTxWhenNotMining bool) *worker { worker := &worker{ - config: config, - engine: engine, - eth: eth, - mux: mux, - txCh: make(chan core.TxPreEvent, txChanSize), - chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), - chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), - chainDb: eth.ChainDb(), - recv: make(chan *Result, resultQueueSize), - chain: eth.BlockChain(), - proc: eth.BlockChain().Validator(), - possibleUncles: make(map[common.Hash]*types.Block), - coinbase: coinbase, - agents: make(map[Agent]struct{}), - unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), + config: config, + engine: engine, + eth: eth, + mux: mux, + txCh: make(chan core.TxPreEvent, txChanSize), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), + chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), + chainDb: eth.ChainDb(), + recv: make(chan *Result, resultQueueSize), + chain: eth.BlockChain(), + proc: eth.BlockChain().Validator(), + possibleUncles: make(map[common.Hash]*types.Block), + coinbase: coinbase, + agents: make(map[Agent]struct{}), + unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), + commitTxWhenNotMining: commitTxWhenNotMining, + } + if worker.commitTxWhenNotMining { + // Subscribe TxPreEvent for tx pool + worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) } - // Subscribe TxPreEvent for tx pool - worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) // Subscribe events for blockchain worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) @@ -248,16 +253,39 @@ func (self *worker) unregister(agent Agent) { } func (self *worker) update() { - defer self.txSub.Unsubscribe() + if self.commitTxWhenNotMining { + defer self.txSub.Unsubscribe() + } defer self.chainHeadSub.Unsubscribe() defer self.chainSideSub.Unsubscribe() - + timeout := time.NewTimer(waitPeriod * time.Second) + c := make(chan struct{}) + finish := make(chan struct{}) + defer close(finish) + defer timeout.Stop() + go func() { + for { + // A real event arrived, process interesting content + select { + case <-timeout.C: + c <- struct{}{} + case <-finish: + return + } + } + }() for { // A real event arrived, process interesting content select { - // Handle ChainHeadEvent + case <-c: + if atomic.LoadInt32(&self.mining) == 1 { + self.commitNewWork() + } + timeout.Reset(waitPeriod * time.Second) + // Handle ChainHeadEvent case <-self.chainHeadCh: self.commitNewWork() + timeout.Reset(waitPeriod * time.Second) // Handle ChainSideEvent case ev := <-self.chainSideCh: @@ -283,8 +311,6 @@ func (self *worker) update() { } } // System stopped - case <-self.txSub.Err(): - return case <-self.chainHeadSub.Err(): return case <-self.chainSideSub.Err(): @@ -466,6 +492,13 @@ func (self *worker) commitNewWork() { tstart := time.Now() parent := self.chain.CurrentBlock() var signers map[common.Address]struct{} + if parent.Hash().Hex() == self.lastParentBlockCommit { + return + } + if !self.commitTxWhenNotMining && atomic.LoadInt32(&self.mining) == 0 { + return + } + // Only try to commit new work if we are mining if atomic.LoadInt32(&self.mining) == 1 { // check if we are right after parent's coinbase in the list @@ -504,19 +537,11 @@ func (self *worker) commitNewWork() { gap += waitPeriodCheckpoint } log.Info("Distance from the parent block", "seconds", gap, "hops", h) - L: - select { - case newBlock := <-self.chainHeadCh: - self.chainHeadCh <- newBlock - if newBlock.Block.NumberU64() > parent.NumberU64() { - log.Info("New block has came already. Skip this turn", "new block", newBlock.Block.NumberU64(), "current block", parent.NumberU64()) - return - } - case <-time.After(time.Duration(gap) * time.Second): - // wait enough. It's my turn - log.Info("Wait enough. It's my turn", "waited seconds", gap) - break L + waitedTime := time.Now().Unix() - parent.Header().Time.Int64() + if gap > waitedTime { + return } + log.Info("Wait enough. It's my turn", "waited seconds", waitedTime) } } } @@ -611,6 +636,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "special txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) self.unconfirmed.Shift(work.Block.NumberU64() - 1) + self.lastParentBlockCommit = parent.Hash().Hex() } self.push(work) } diff --git a/node/config.go b/node/config.go index dda24583ee..e828001406 100644 --- a/node/config.go +++ b/node/config.go @@ -147,6 +147,8 @@ type Config struct { // Logger is a custom logger to use with the p2p.Server. Logger log.Logger `toml:",omitempty"` + + CommitTxWhenNotMining bool `toml:",omitempty"` } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into From e4e70614826da52ae39e158d5e3d26a7fe3acda6 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 7 Dec 2018 15:17:33 +0700 Subject: [PATCH 04/13] filter txs before add to pool --- eth/handler.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/eth/handler.go b/eth/handler.go index 9ca0edf261..29924fa55d 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/hashicorp/golang-lru" "math/big" "sync" "sync/atomic" @@ -92,12 +93,14 @@ type ProtocolManager struct { // wait group is used for graceful shutdowns during downloading // and processing - wg sync.WaitGroup + wg sync.WaitGroup + knownTxs *lru.Cache } // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { + knownTxs, _ := lru.New(maxKnownTxs) // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkId, @@ -110,6 +113,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne noMorePeers: make(chan struct{}), txsyncCh: make(chan *txsync), quitSync: make(chan struct{}), + knownTxs: knownTxs, } // Figure out whether to allow fast sync or not if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 { @@ -668,12 +672,19 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } + var unkownTxs []*types.Transaction for i, tx := range txs { // Validate and mark the remote transaction if tx == nil { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkTransaction(tx.Hash()) + exist, _ := pm.knownTxs.ContainsOrAdd(tx.Hash(), true) + if !exist { + unkownTxs = append(unkownTxs, tx) + } else { + log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce(), "to", tx.To()) + } } pm.txpool.AddRemotes(txs) From 33ddb251ef068267d0170d078751e46b43ef774a Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Tue, 4 Dec 2018 10:20:31 +0700 Subject: [PATCH 05/13] Parallel process block from fetcher --- consensus/consensus.go | 2 +- consensus/posv/posv.go | 43 +++--- core/blockchain.go | 266 ++++++++++++++++++++++++++++++++---- core/error.go | 2 + core/state_processor.go | 43 +++++- core/tx_pool.go | 4 +- core/types.go | 1 + core/types/block.go | 27 ++++ eth/backend.go | 17 ++- eth/fetcher/fetcher.go | 59 +++++--- eth/fetcher/fetcher_test.go | 35 ++++- eth/handler.go | 20 ++- eth/sync.go | 1 - miner/worker.go | 2 - 14 files changed, 429 insertions(+), 93 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index be5e661c12..b02afa63c4 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -58,7 +58,7 @@ type Engine interface { // VerifyHeader checks whether a header conforms to the consensus rules of a // given engine. Verifying the seal may be done optionally here, or explicitly // via the VerifySeal method. - VerifyHeader(chain ChainReader, header *types.Header, seal bool) error + VerifyHeader(chain ChainReader, header *types.Header, fullVerify bool) error // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // concurrently. The method returns a quit channel to abort the operations and diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 7a61c3194c..e51f01d8e6 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -261,20 +261,20 @@ func (c *Posv) Author(header *types.Header) (common.Address, error) { } // VerifyHeader checks whether a header conforms to the consensus rules. -func (c *Posv) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { - return c.verifyHeaderWithCache(chain, header, nil) +func (c *Posv) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error { + return c.verifyHeaderWithCache(chain, header, nil, fullVerify) } // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // method returns a quit channel to abort the operations and a results channel to // retrieve the async verifications (the order is that of the input slice). -func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { +func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) { abort := make(chan struct{}) results := make(chan error, len(headers)) go func() { for i, header := range headers { - err := c.verifyHeaderWithCache(chain, header, headers[:i]) + err := c.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i]) select { case <-abort: @@ -286,12 +286,12 @@ func (c *Posv) VerifyHeaders(chain consensus.ChainReader, headers []*types.Heade return abort, results } -func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { +func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error { _, check := c.verifiedHeaders.Get(header.Hash()) if check { return nil } - err := c.verifyHeader(chain, header, parents) + err := c.verifyHeader(chain, header, parents, fullVerify) if err == nil { c.verifiedHeaders.Add(header.Hash(), true) } @@ -302,15 +302,19 @@ func (c *Posv) verifyHeaderWithCache(chain consensus.ChainReader, header *types. // caller may optionally pass in a batch of parents (ascending order) to avoid // looking those up from the database. This is useful for concurrently verifying // a batch of new headers. -func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { +func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error { if header.Number == nil { return errUnknownBlock } number := header.Number.Uint64() - - // Don't waste time checking blocks from the future - if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { - return consensus.ErrFutureBlock + if fullVerify { + if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 { + return consensus.ErrNoValidatorSignature + } + // Don't waste time checking blocks from the future + if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + return consensus.ErrFutureBlock + } } // Checkpoint blocks need to enforce zero beneficiary checkpoint := (number % c.config.Epoch) == 0 @@ -359,14 +363,14 @@ func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, p return err } // All basic checks passed, verify cascading fields - return c.verifyCascadingFields(chain, header, parents) + return c.verifyCascadingFields(chain, header, parents, fullVerify) } // verifyCascadingFields verifies all the header fields that are not standalone, // rather depend on a batch of previous headers. The caller may optionally pass // in a batch of parents (ascending order) to avoid looking those up from the // database. This is useful for concurrently verifying a batch of new headers. -func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { +func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error { // The genesis block is the always valid dead-end number := header.Number.Uint64() if number == 0 { @@ -385,9 +389,6 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types. if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() { return ErrInvalidTimestamp } - if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 { - return consensus.ErrNoValidatorSignature - } // Retrieve the snapshot needed to verify this header and cache it snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) if err != nil { @@ -429,7 +430,7 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types. } } // All basic checks passed, verify the seal and return - return c.verifySeal(chain, header, parents) + return c.verifySeal(chain, header, parents, fullVerify) } func (c *Posv) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*Snapshot, error) { @@ -533,7 +534,7 @@ func (c *Posv) snapshot(chain consensus.ChainReader, number uint64, hash common. // If we're at block zero, make a snapshot if number == 0 { genesis := chain.GetHeaderByNumber(0) - if err := c.VerifyHeader(chain, genesis, false); err != nil { + if err := c.VerifyHeader(chain, genesis, true); err != nil { return nil, err } signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength) @@ -598,7 +599,7 @@ func (c *Posv) VerifyUncles(chain consensus.ChainReader, block *types.Block) err // VerifySeal implements consensus.Engine, checking whether the signature contained // in the header satisfies the consensus protocol requirements. func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) error { - return c.verifySeal(chain, header, nil) + return c.verifySeal(chain, header, nil, true) } // verifySeal checks whether the signature contained in the header satisfies the @@ -607,7 +608,7 @@ func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) err // from. // verifySeal also checks the pair of creator-validator set in the header satisfies // the double validation. -func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { +func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error { // Verifying the genesis block is not supported number := header.Number.Uint64() if number == 0 { @@ -663,7 +664,7 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par // header must contain validator info following double validation design // start checking from epoch 2nd. - if header.Number.Uint64() > c.config.Epoch { + if header.Number.Uint64() > c.config.Epoch && fullVerify { validator, err := c.RecoverValidator(header) if err != nil { return err diff --git a/core/blockchain.go b/core/blockchain.go index 80b3bc1307..bf6a5d7323 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -75,6 +75,13 @@ type CacheConfig struct { TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk } +type ResultProcessBlock struct { + logs []*types.Log + receipts []*types.Receipt + state *state.StateDB + proctime time.Duration + usedGas uint64 +} // BlockChain represents the canonical chain given a database with a genesis // block. The Blockchain manages chain imports, reverts, chain reorganisations. @@ -115,14 +122,16 @@ type BlockChain struct { currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks - futureBlocks *lru.Cache // future blocks are blocks added for later processing - - quit chan struct{} // blockchain quit channel - running int32 // running must be called atomically + stateCache state.Database // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing + resultProcess *lru.Cache + calculatingBlock *lru.Cache + downloadingBlock *lru.Cache + quit chan struct{} // blockchain quit channel + running int32 // running must be called atomically // procInterrupt must be atomically called procInterrupt int32 // interrupt signaler for block processing wg sync.WaitGroup // chain processing wait group for shutting down @@ -152,21 +161,26 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par blockCache, _ := lru.New(blockCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) badBlocks, _ := lru.New(badBlockLimit) - + resultProcess, _ := lru.New(blockCacheLimit) + preparingBlock, _ := lru.New(blockCacheLimit) + downloadingBlock, _ := lru.New(blockCacheLimit) bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triegc: prque.New(), - stateCache: state.NewDatabase(db), - quit: make(chan struct{}), - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - blockCache: blockCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - badBlocks: badBlocks, + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(), + stateCache: state.NewDatabase(db), + quit: make(chan struct{}), + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + resultProcess: resultProcess, + calculatingBlock: preparingBlock, + downloadingBlock: downloadingBlock, + engine: engine, + vmConfig: vmConfig, + badBlocks: badBlocks, } bc.SetValidator(NewBlockValidator(chainConfig, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) @@ -1049,6 +1063,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty for i, block := range chain { headers[i] = block.Header() seals[i] = true + bc.downloadingBlock.Add(block.Hash(), true) } abort, results := bc.engine.VerifyHeaders(bc, headers, seals) defer close(abort) @@ -1168,7 +1183,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty } switch status { case CanonStatTy: - log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), + log.Debug("Inserted new block from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart))) coalescedLogs = append(coalescedLogs, logs...) @@ -1180,7 +1195,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty bc.gcproc += proctime case SideStatTy: - log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", + log.Debug("Inserted forked block from downloader", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) blockInsertTimer.UpdateSince(bstart) @@ -1189,7 +1204,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty stats.processed++ stats.usedGas += usedGas stats.report(chain, i, bc.stateCache.TrieDB().Size()) - if bc.chainConfig.Posv != nil { + if status == CanonStatTy && bc.chainConfig.Posv != nil { // epoch block if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == 0 { CheckpointCh <- 1 @@ -1206,11 +1221,212 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty } // Append a single chain head event if we've progressed the chain if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { + log.Debug("New ChainHeadEvent ", "number", lastCanon.NumberU64(), "hash", lastCanon.Hash()) events = append(events, ChainHeadEvent{lastCanon}) } return 0, events, coalescedLogs, nil } +func (bc *BlockChain) InsertBlock(block *types.Block) error { + events, logs, err := bc.insertBlock(block) + bc.PostChainEvents(events, logs) + return err +} + +func (bc *BlockChain) PrepareBlock(block *types.Block) (err error) { + defer log.Debug("Done prepare block ", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator, "err", err) + if _, check := bc.resultProcess.Get(block.Hash()); check { + log.Debug("Stop prepare a block because the result cached", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator) + return nil + } + if _, check := bc.calculatingBlock.Get(block.Hash()); check { + log.Debug("Stop prepare a block because inserting", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator) + return nil + } + err = bc.engine.VerifyHeader(bc, block.Header(), false) + if err != nil { + return err + } + result, err := bc.getResultBlock(block, false) + if err == nil { + bc.resultProcess.Add(block.Hash(), result) + return nil + } else if err == ErrKnownBlock { + return nil + } else if err == ErrStopPreparingBlock { + log.Debug("Stop prepare a block because calculating", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator) + return nil + } + return err +} + +func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*ResultProcessBlock, error) { + var calculatedBlock *CalculatedBlock + if verifiedM2 { + if result, check := bc.resultProcess.Get(block.HashNoValidator()); check { + log.Debug("Get result block from cache ", "number", block.NumberU64(), "hash", block.Hash(), "hash no validator", block.HashNoValidator()) + return result.(*ResultProcessBlock), nil + } + log.Debug("Not found cache prepare block ", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.HashNoValidator()) + if calculatedBlock, _ := bc.calculatingBlock.Get(block.HashNoValidator()); calculatedBlock != nil { + calculatedBlock.(*CalculatedBlock).stop = true + } + } + calculatedBlock = &CalculatedBlock{block, false} + bc.calculatingBlock.Add(block.HashNoValidator(), calculatedBlock) + // Start the parallel header verifier + // If the chain is terminating, stop processing blocks + if atomic.LoadInt32(&bc.procInterrupt) == 1 { + log.Debug("Premature abort during blocks processing") + return nil, ErrBlacklistedHash + } + // If the header is a banned one, straight out abort + if BadHashes[block.Hash()] { + bc.reportBlock(block, nil, ErrBlacklistedHash) + return nil, ErrBlacklistedHash + } + // Wait for the block's verification to complete + bstart := time.Now() + err := bc.Validator().ValidateBody(block) + switch { + case err == ErrKnownBlock: + // Block and state both already known. However if the current block is below + // this number we did a rollback and we should reimport it nonetheless. + if bc.CurrentBlock().NumberU64() >= block.NumberU64() { + return nil, ErrKnownBlock + } + case err == consensus.ErrPrunedAncestor: + // Block competing with the canonical chain, store in the db, but don't process + // until the competitor TD goes above the canonical TD + currentBlock := bc.CurrentBlock() + localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) + externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) + if localTd.Cmp(externTd) > 0 { + return nil, err + } + // Competitor chain beat canonical, gather all blocks from the common ancestor + var winner []*types.Block + + parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + for !bc.HasState(parent.Root()) { + winner = append(winner, parent) + parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1) + } + for j := 0; j < len(winner)/2; j++ { + winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j] + } + log.Debug("Number block need calculated again", "number", block.NumberU64(), "hash", block.Hash().Hex(), "winners", len(winner)) + // Import all the pruned blocks to make the state available + _, _, _, err := bc.insertChain(winner) + if err != nil { + return nil, err + } + case err != nil: + bc.reportBlock(block, nil, err) + return nil, err + } + // Create a new statedb using the parent block and report an + // error if it fails. + var parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + state, err := state.New(parent.Root(), bc.stateCache) + if err != nil { + return nil, err + } + // Process block using the parent state as reference point. + receipts, logs, usedGas, err := bc.processor.ProcessBlockNoValidator(calculatedBlock, state, bc.vmConfig) + process := time.Since(bstart) + if err != nil { + if err != ErrStopPreparingBlock { + bc.reportBlock(block, receipts, err) + } + return nil, err + } + // Validate the state using the default validator + err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas) + if err != nil { + bc.reportBlock(block, receipts, err) + return nil, err + } + proctime := time.Since(bstart) + log.Debug("Caculate new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)), "process", process) + return &ResultProcessBlock{receipts: receipts, logs: logs, state: state, proctime: proctime, usedGas: usedGas}, nil +} + +// insertChain will execute the actual chain insertion and event aggregation. The +// only reason this method exists as a separate one is to make locking cleaner +// with deferred statements. +func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.Log, error) { + var ( + stats = insertStats{startTime: mclock.Now()} + events = make([]interface{}, 0, 1) + coalescedLogs []*types.Log + ) + if _, check := bc.downloadingBlock.Get(block.Hash()); check { + log.Debug("Stop fetcher a block because downloading", "number", block.NumberU64(), "hash", block.Hash()) + return events, coalescedLogs, nil + } + result, err := bc.getResultBlock(block, true) + if err != nil { + return events, coalescedLogs, err + } + defer bc.resultProcess.Remove(block.HashNoValidator()) + bc.wg.Add(1) + defer bc.wg.Done() + // Write the block to the chain and get the status. + bc.chainmu.Lock() + defer bc.chainmu.Unlock() + if bc.HasBlockAndState(block.Hash(), block.NumberU64()) { + return events, coalescedLogs, nil + } + status, err := bc.WriteBlockWithState(block, result.receipts, result.state) + + if err != nil { + return events, coalescedLogs, err + } + switch status { + case CanonStatTy: + log.Debug("Inserted new block from fetcher", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(block.ReceivedAt))) + + coalescedLogs = append(coalescedLogs, result.logs...) + events = append(events, ChainEvent{block, block.Hash(), result.logs}) + + // Only count canonical blocks for GC processing time + bc.gcproc += result.proctime + + case SideStatTy: + log.Debug("Inserted forked block from fetcher", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", + common.PrettyDuration(time.Since(block.ReceivedAt)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) + + blockInsertTimer.Update(result.proctime) + events = append(events, ChainSideEvent{block}) + } + stats.processed++ + stats.usedGas += result.usedGas + stats.report(types.Blocks{block}, 0, bc.stateCache.TrieDB().Size()) + if status == CanonStatTy && bc.chainConfig.Posv != nil { + // epoch block + if (block.NumberU64() % bc.chainConfig.Posv.Epoch) == 0 { + CheckpointCh <- 1 + } + // prepare set of masternodes for the next epoch + if (block.NumberU64() % bc.chainConfig.Posv.Epoch) == (bc.chainConfig.Posv.Epoch - bc.chainConfig.Posv.Gap) { + err := bc.UpdateM1() + if err != nil { + log.Error("Error when update masternodes set. Stopping node", "err", err) + os.Exit(1) + } + } + } + // Append a single chain head event if we've progressed the chain + if status == CanonStatTy && bc.CurrentBlock().Hash() == block.Hash() { + events = append(events, ChainHeadEvent{block}) + log.Debug("New ChainHeadEvent from fetcher ", "number", block.NumberU64(), "hash", block.Hash()) + } + return events, coalescedLogs, nil +} + // insertStats tracks and reports on block insertion. type insertStats struct { queued, processed, ignored int diff --git a/core/error.go b/core/error.go index 86b093b151..4af0d2922a 100644 --- a/core/error.go +++ b/core/error.go @@ -36,4 +36,6 @@ var ( ErrNotPoSV = errors.New("Posv not found in config") ErrNotFoundM1 = errors.New("list M1 not found ") + + ErrStopPreparingBlock = errors.New("stop calculate a block not vrified M2") ) diff --git a/core/state_processor.go b/core/state_processor.go index 962d4735ad..52d1fc0f65 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -40,6 +40,10 @@ type StateProcessor struct { bc *BlockChain // Canonical block chain engine consensus.Engine // Consensus engine used for block rewards } +type CalculatedBlock struct { + block *types.Block + stop bool +} // NewStateProcessor initialises a new StateProcessor. func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { @@ -69,9 +73,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } - InitSignerInTransactions(p.config, header, block.Transactions()) - // Iterate over and process the individual transactions for i, tx := range block.Transactions() { statedb.Prepare(tx.Hash(), block.Hash(), i) receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) @@ -83,7 +85,44 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts) + return receipts, allLogs, *usedGas, nil +} +func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { + block := cBlock.block + var ( + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) + ) + // Mutate the the block and state according to any hard-fork specs + if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { + misc.ApplyDAOHardFork(statedb) + } + if cBlock.stop { + return nil, nil, 0, ErrStopPreparingBlock + } + InitSignerInTransactions(p.config, header, block.Transactions()) + if cBlock.stop { + return nil, nil, 0, ErrStopPreparingBlock + } + // Iterate over and process the individual transactions + receipts = make([]*types.Receipt, block.Transactions().Len()) + for i, tx := range block.Transactions() { + statedb.Prepare(tx.Hash(), block.Hash(), i) + receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) + if err != nil { + return nil, nil, 0, err + } + if cBlock.stop { + return nil, nil, 0, ErrStopPreparingBlock + } + receipts[i] = receipt + } + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) + p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts) return receipts, allLogs, *usedGas, nil } diff --git a/core/tx_pool.go b/core/tx_pool.go index 6f42149382..1093dc1837 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -652,6 +652,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { } // If the transaction pool is full, discard underpriced transactions if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue { + log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce()) // If the new transaction is underpriced, don't accept it if pool.priced.Underpriced(tx, pool.locals) { log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) @@ -879,9 +880,6 @@ func (pool *TxPool) addTx(tx *types.Transaction, local bool) error { // addTxs attempts to queue a batch of transactions if they are valid. func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error { - for _, tx := range txs { - types.CacheSigner(pool.signer, tx) - } pool.mu.Lock() defer pool.mu.Unlock() diff --git a/core/types.go b/core/types.go index d0bbaf0aa7..3f691cb420 100644 --- a/core/types.go +++ b/core/types.go @@ -43,4 +43,5 @@ type Validator interface { // failed. type Processor interface { Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) + ProcessBlockNoValidator(block *CalculatedBlock, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) } diff --git a/core/types/block.go b/core/types/block.go index 25865d4b25..87d989b69c 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -125,6 +125,30 @@ func (h *Header) HashNoNonce() common.Hash { }) } +// HashNoNonce returns the hash which is used as input for the proof-of-work search. +func (h *Header) HashNoValidator() common.Hash { + return rlpHash([]interface{}{ + h.ParentHash, + h.UncleHash, + h.Coinbase, + h.Root, + h.TxHash, + h.ReceiptHash, + h.Bloom, + h.Difficulty, + h.Number, + h.GasLimit, + h.GasUsed, + h.Time, + h.Extra, + h.MixDigest, + h.Nonce, + h.Validators, + []byte{}, + h.Penalties, + }) +} + // Size returns the approximate memory used by all internal contents. It is used // to approximate and limit the memory consumption of various caches. func (h *Header) Size() common.StorageSize { @@ -337,6 +361,9 @@ func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } func (b *Block) HashNoNonce() common.Hash { return b.header.HashNoNonce() } +func (b *Block) HashNoValidator() common.Hash { + return b.header.HashNoValidator() +} // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previsouly cached value. diff --git a/eth/backend.go b/eth/backend.go index 28216abe2c..0cb6fe601b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -203,29 +203,28 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { return nil } - appendM2HeaderHook := func(block *types.Block) (*types.Block, error) { + appendM2HeaderHook := func(block *types.Block) (*types.Block, bool, error) { eb, err := eth.Etherbase() if err != nil { log.Error("Cannot get etherbase for append m2 header", "err", err) - return block, fmt.Errorf("etherbase missing: %v", err) + return block, false, fmt.Errorf("etherbase missing: %v", err) } m1, err := c.RecoverSigner(block.Header()) if err != nil { - return block, fmt.Errorf("can't get block creator: %v", err) + return block, false, fmt.Errorf("can't get block creator: %v", err) } m2, err := c.GetValidator(m1, eth.blockchain, block.Header()) if err != nil { - return block, fmt.Errorf("can't get block validator: %v", err) + return block, false, fmt.Errorf("can't get block validator: %v", err) } if m2 == eb { wallet, _ := eth.accountManager.Find(accounts.Account{Address: eb}) header := block.Header() sighash, _ := wallet.SignHash(accounts.Account{Address: eb}, posv.SigHash(header).Bytes()) header.Validator = sighash - block = types.NewBlockWithHeader(header).WithBody(block.Transactions(), block.Uncles()) + return types.NewBlockWithHeader(header).WithBody(block.Transactions(), block.Uncles()), true, nil } - - return block, nil + return block, false, nil } eth.protocolManager.fetcher.SetSignHook(signHook) @@ -301,8 +300,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if foudationWalletAddr == (common.Address{}) { log.Error("Foundation Wallet Address is empty", "error", foudationWalletAddr) } - start := time.Now() if number > 0 && number-rCheckpoint > 0 && foudationWalletAddr != (common.Address{}) { + start := time.Now() // Get signers in blockSigner smartcontract. addr := common.HexToAddress(common.BlockSigners) // Get reward inflation. @@ -334,8 +333,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } } } + log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) } - log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) return nil } diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index 36e9568f34..0b94b50d7a 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -62,8 +62,10 @@ type blockBroadcasterFn func(block *types.Block, propagate bool) // chainHeightFn is a callback type to retrieve the current chain height. type chainHeightFn func() uint64 -// chainInsertFn is a callback type to insert a batch of blocks into the local chain. -type chainInsertFn func(blocks types.Blocks) (int, error) +// blockInsertFn is a callback type to insert a batch of blocks into the local chain. +type blockInsertFn func(block *types.Block) error + +type blockPrepareFn func(block *types.Block) error // peerDropFn is a callback type for dropping a peer detected as malicious. type peerDropFn func(id string) @@ -135,8 +137,9 @@ type Fetcher struct { verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers chainHeight chainHeightFn // Retrieves the current chain's height - insertChain chainInsertFn // Injects a batch of blocks into the chain - dropPeer peerDropFn // Drops a peer for misbehaving + insertBlock blockInsertFn // Injects a batch of blocks into the chain + prepareBlock blockPrepareFn + dropPeer peerDropFn // Drops a peer for misbehaving // Testing hooks announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the announce list @@ -144,11 +147,11 @@ type Fetcher struct { fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62) signHook func(*types.Block) error - appendM2HeaderHook func(*types.Block) (*types.Block, error) + appendM2HeaderHook func(*types.Block) (*types.Block, bool, error) } // New creates a block fetcher to retrieve blocks based on hash announcements. -func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher { +func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher { knownBlocks, _ := lru.NewARC(blockLimit) return &Fetcher{ notify: make(chan *announce), @@ -171,7 +174,8 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc verifyHeader: verifyHeader, broadcastBlock: broadcastBlock, chainHeight: chainHeight, - insertChain: insertChain, + insertBlock: insertBlock, + prepareBlock: prepareBlock, dropPeer: dropPeer, } } @@ -605,7 +609,7 @@ func (f *Fetcher) rescheduleComplete(complete *time.Timer) { func (f *Fetcher) enqueue(peer string, block *types.Block) { hash := block.Hash() if f.knowns.Contains(hash) { - log.Debug("Discarded propagated block, known block", "peer", peer, "number", block.Number(), "hash", hash, "limit", blockLimit) + log.Trace("Discarded propagated block, known block", "peer", peer, "number", block.Number(), "hash", hash, "limit", blockLimit) return } // Ensure the peer isn't DOSing us @@ -657,40 +661,56 @@ func (f *Fetcher) insert(peer string, block *types.Block) { log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash()) return } + fastBroadCast := true again: + err := f.verifyHeader(block.Header()) // Quickly validate the header and propagate the block if it passes - switch err := f.verifyHeader(block.Header()); err { + switch err { case nil: // All ok, quickly propagate to our peers propBroadcastOutTimer.UpdateSince(block.ReceivedAt) - go f.broadcastBlock(block, true) + if fastBroadCast { + go f.broadcastBlock(block, true) + } case consensus.ErrFutureBlock: delay := time.Unix(block.Time().Int64(), 0).Sub(time.Now()) // nolint: gosimple - time.Sleep(delay) log.Info("Receive future block", "number", block.NumberU64(), "hash", block.Hash().Hex(), "delay", delay) + time.Sleep(delay) goto again case consensus.ErrNoValidatorSignature: newBlock := block + var errM2 error + isM2 := false if f.appendM2HeaderHook != nil { - if newBlock, err = f.appendM2HeaderHook(block); err != nil { - log.Error("Append m2 to block header fail", "err", err) + if newBlock, isM2, errM2 = f.appendM2HeaderHook(block); errM2 != nil { + log.Error("Append m2 to block header fail", "err", errM2) return } } - if newBlock.Hash() == block.Hash() { + if !isM2 { go f.broadcastBlock(block, true) + if err := f.prepareBlock(block); err != nil { + log.Debug("Propagated block prepare failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) + return + } + return + } + log.Debug("Append M2 to header block", "numer", block.NumberU64(), "hahs", block.Hash()) + if err := f.prepareBlock(block); err != nil { + log.Debug("Propagated block prepare failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) return } block = newBlock + fastBroadCast = false + goto again default: // Something went very wrong, drop the peer log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) f.dropPeer(peer) return } - // Run the actual import and log any issues - if _, err := f.insertChain(types.Blocks{block}); err != nil { + if err := f.insertBlock(block); err != nil { log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) return } @@ -703,8 +723,9 @@ func (f *Fetcher) insert(peer string, block *types.Block) { } // If import succeeded, broadcast the block propAnnounceOutTimer.UpdateSince(block.ReceivedAt) - go f.broadcastBlock(block, true) - //go f.broadcastBlock(block, false) + if !fastBroadCast { + go f.broadcastBlock(block, true) + } }() } @@ -768,6 +789,6 @@ func (f *Fetcher) SetSignHook(signHook func(*types.Block) error) { } // Bind append m2 to block header hook when imported into chain. -func (f *Fetcher) SetAppendM2HeaderHook(appendM2HeaderHook func(*types.Block) (*types.Block, error)) { +func (f *Fetcher) SetAppendM2HeaderHook(appendM2HeaderHook func(*types.Block) (*types.Block, bool, error)) { f.appendM2HeaderHook = appendM2HeaderHook } diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index af9a5a6b44..b4f4cebf1a 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -92,7 +92,7 @@ func newTester() *fetcherTester { blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, drops: make(map[string]bool), } - tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) + tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertBlock, tester.prepareBlock, tester.dropPeer) tester.fetcher.Start() return tester @@ -123,7 +123,7 @@ func (f *fetcherTester) chainHeight() uint64 { return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() } -// insertChain injects a new blocks into the simulated chain. +// insertBlock injects a new blocks into the simulated chain. func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) { f.lock.Lock() defer f.lock.Unlock() @@ -144,6 +144,31 @@ func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) { return 0, nil } +// insertBlock injects a new blocks into the simulated chain. +func (f *fetcherTester) insertBlock(block *types.Block) error { + f.lock.Lock() + defer f.lock.Unlock() + + // Make sure the parent in known + if _, ok := f.blocks[block.ParentHash()]; !ok { + return errors.New("unknown parent") + } + // Discard any new blocks if the same height already exists + if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() { + return nil + } + // Otherwise build our current chain + f.hashes = append(f.hashes, block.Hash()) + f.blocks[block.Hash()] = block + + return nil +} + +// insertBlock injects a new blocks into the simulated chain. +func (f *fetcherTester) prepareBlock(block *types.Block) error { + return nil +} + // dropPeer is an emulator for the peer removal, simply accumulating the various // peers dropped by the fetcher. func (f *fetcherTester) dropPeer(peer string) { @@ -512,9 +537,9 @@ func testImportDeduplication(t *testing.T, protocol int) { bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) counter := uint32(0) - tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) { - atomic.AddUint32(&counter, uint32(len(blocks))) - return tester.insertChain(blocks) + tester.fetcher.insertBlock = func(block *types.Block) error { + atomic.AddUint32(&counter, uint32(1)) + return tester.insertBlock(block) } // Instrument the fetching and imported events fetching := make(chan []common.Hash) diff --git a/eth/handler.go b/eth/handler.go index 29924fa55d..17ad4df52e 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -170,16 +170,26 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne heighter := func() uint64 { return blockchain.CurrentBlock().NumberU64() } - inserter := func(blocks types.Blocks) (int, error) { + inserter := func(block *types.Block) error { // If fast sync is running, deny importing weird blocks if atomic.LoadUint32(&manager.fastSync) == 1 { - log.Warn("Discarded bad propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) - return 0, nil + log.Warn("Discarded bad propagated block", "number", block.Number(), "hash", block.Hash()) + return nil } atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import - return manager.blockchain.InsertChain(blocks) + return manager.blockchain.InsertBlock(block) } - manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) + + prepare := func(block *types.Block) error { + // If fast sync is running, deny importing weird blocks + if atomic.LoadUint32(&manager.fastSync) == 1 { + log.Warn("Discarded bad propagated block", "number", block.Number(), "hash", block.Hash()) + return nil + } + atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import + return manager.blockchain.PrepareBlock(block) + } + manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer) return manager, nil } diff --git a/eth/sync.go b/eth/sync.go index 5072aad775..770d6db277 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -170,7 +170,6 @@ func (pm *ProtocolManager) synchronise(peer *peer) { currentBlock := pm.blockchain.CurrentBlock() td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) pHead, pTd := peer.Head() - log.Debug("ProtocolManager synchronise ", "p", peer, "pTd", pTd, "currentTd", td) if pTd.Cmp(td) <= 0 { return } diff --git a/miner/worker.go b/miner/worker.go index 8081d53741..4c96d64583 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -310,7 +310,6 @@ func (self *worker) update() { self.commitNewWork() } } - // System stopped case <-self.chainHeadSub.Err(): return case <-self.chainSideSub.Err(): @@ -545,7 +544,6 @@ func (self *worker) commitNewWork() { } } } - tstamp := tstart.Unix() if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { tstamp = parent.Time().Int64() + 1 From 60da11de10fb4d79929e8376ba68794a8308bd10 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Thu, 6 Dec 2018 15:53:26 +0700 Subject: [PATCH 06/13] remove faster tx invalid nonce --- miner/worker.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 4c96d64583..ae39d4eba8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -658,7 +658,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs []*types.Log - // first priority for special Txs for _, tx := range specialTxs { if gp.Gas() < params.TxGas && tx.Gas() > 0 { @@ -678,6 +677,11 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB } // Start executing the transaction env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) + nonce := env.state.GetNonce(from) + if nonce != tx.Nonce() { + log.Trace("Skipping account with special transaction invalide nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", tx.To()) + continue + } err, logs := env.commitTransaction(tx, bc, coinbase, gp) switch err { case core.ErrNonceTooLow: @@ -687,7 +691,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB case core.ErrNonceTooHigh: // Reorg notification data race between the transaction pool and miner, skip account = log.Trace("Skipping account with special transaction hight nonce", "sender", from, "nonce", tx.Nonce(), "to", tx.To()) - case nil: // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) @@ -699,7 +702,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB log.Debug("Add Special Transaction failed, account skipped", "hash", tx.Hash(), "sender", from, "nonce", tx.Nonce(), "to", tx.To(), "err", err) } } - for { // If we don't have enough gas for any further transactions then we're done if gp.Gas() < params.TxGas { @@ -720,13 +722,24 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB // phase, start ignoring the sender until we do. if tx.Protected() && !env.config.IsEIP155(env.header.Number) { log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) - txs.Pop() continue } // Start executing the transaction env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) - + nonce := env.state.GetNonce(from) + if nonce > tx.Nonce() { + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + txs.Shift() + continue + } + if nonce < tx.Nonce() { + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + txs.Pop() + continue + } err, logs := env.commitTransaction(tx, bc, coinbase, gp) switch err { case core.ErrGasLimitReached: @@ -757,7 +770,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB txs.Shift() } } - if len(coalescedLogs) > 0 || env.tcount > 0 { // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined // logs by filling in the block hash when the block was mined by the local miner. This can From 5bcaa45469edd1051be1686cfe8aa8fd52b980db Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 7 Dec 2018 09:23:39 +0700 Subject: [PATCH 07/13] remove caculate uncle block posv --- miner/worker.go | 53 +++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index ae39d4eba8..b91daa79e8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -289,10 +289,11 @@ func (self *worker) update() { // Handle ChainSideEvent case ev := <-self.chainSideCh: - self.uncleMu.Lock() - self.possibleUncles[ev.Block.Hash()] = ev.Block - self.uncleMu.Unlock() - + if self.config.Posv == nil { + self.uncleMu.Lock() + self.possibleUncles[ev.Block.Hash()] = ev.Block + self.uncleMu.Unlock() + } // Handle TxPreEvent case ev := <-self.txCh: // Apply transaction to the pending state if we're not mining @@ -447,13 +448,15 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error createdAt: time.Now(), } - // when 08 is processed ancestors contain 07 (quick block) - for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { - for _, uncle := range ancestor.Uncles() { - work.family.Add(uncle.Hash()) + if self.config.Posv == nil { + // when 08 is processed ancestors contain 07 (quick block) + for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { + for _, uncle := range ancestor.Uncles() { + work.family.Add(uncle.Hash()) + } + work.family.Add(ancestor.Hash()) + work.ancestors.Add(ancestor.Hash()) } - work.family.Add(ancestor.Hash()) - work.ancestors.Add(ancestor.Hash()) } // Keep track of transactions which return errors so they can be removed @@ -608,22 +611,24 @@ func (self *worker) commitNewWork() { uncles []*types.Header badUncles []common.Hash ) - for hash, uncle := range self.possibleUncles { - if len(uncles) == 2 { - break - } - if err := self.commitUncle(work, uncle.Header()); err != nil { - log.Trace("Bad uncle found and will be removed", "hash", hash) - log.Trace(fmt.Sprint(uncle)) + if self.config.Posv == nil { + for hash, uncle := range self.possibleUncles { + if len(uncles) == 2 { + break + } + if err := self.commitUncle(work, uncle.Header()); err != nil { + log.Trace("Bad uncle found and will be removed", "hash", hash) + log.Trace(fmt.Sprint(uncle)) - badUncles = append(badUncles, hash) - } else { - log.Debug("Committing new uncle to block", "hash", hash) - uncles = append(uncles, uncle.Header()) + badUncles = append(badUncles, hash) + } else { + log.Debug("Committing new uncle to block", "hash", hash) + uncles = append(uncles, uncle.Header()) + } + } + for _, hash := range badUncles { + delete(self.possibleUncles, hash) } - } - for _, hash := range badUncles { - delete(self.possibleUncles, hash) } // Create the new block to seal with the consensus engine if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { From b19914e9f0d5cb8da96682e60378713f1863d5a2 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 7 Dec 2018 17:17:18 +0700 Subject: [PATCH 08/13] reduce timed out when sync block --- eth/downloader/downloader.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 17fd41cdd6..4c285db2e6 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -47,10 +47,10 @@ var ( MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests - rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests + rttMaxEstimate = 5 * time.Second // Maximum rount-trip time to target for download requests rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value - ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion - ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts + ttlScaling = 2 // Constant scaling factor for RTT -> TTL conversion + ttlLimit = 5 * time.Second // Maximum TTL allowance to prevent reaching crazy timeouts qosTuningPeers = 5 // Number of peers to tune based on (best peers) qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence From 8bcb58b070e33c109e0f56780ba106b53fd12ea4 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Sat, 8 Dec 2018 10:07:24 +0700 Subject: [PATCH 09/13] check nonce special transaction before promote --- core/blockchain.go | 2 +- core/tx_pool.go | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index bf6a5d7323..970fbdb2ee 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1348,7 +1348,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu return nil, err } proctime := time.Since(bstart) - log.Debug("Caculate new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), + log.Debug("Calculate new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)), "process", process) return &ResultProcessBlock{receipts: receipts, logs: logs, state: state, proctime: proctime, usedGas: usedGas}, nil } diff --git a/core/tx_pool.go b/core/tx_pool.go index 1093dc1837..ec029ed939 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -647,7 +647,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { return false, err } from, _ := types.Sender(pool.signer, tx) // already validated - if tx.IsSpecialTransaction() && pool.IsMasterNode != nil && pool.IsMasterNode(from) { + if tx.IsSpecialTransaction() && pool.IsMasterNode != nil && pool.IsMasterNode(from) && pool.pendingState.GetNonce(from) == tx.Nonce() { return pool.promoteSpecialTx(from, tx) } // If the transaction pool is full, discard underpriced transactions @@ -813,20 +813,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction) // Set the potentially new pending nonce and notify any subsystems of the new tx pool.beats[addr] = time.Now() pool.pendingState.SetNonce(addr, tx.Nonce()+1) - broadcastTxs := types.Transactions{} - for i := tx.Nonce() - 1; i > 0; i-- { - before := list.txs.Get(i) - if before == nil || before.IsSpecialTransaction() { - break - } - broadcastTxs = append(broadcastTxs, before) - } - broadcastTxs = append(broadcastTxs, tx) - go func() { - for _, btx := range broadcastTxs { - pool.txFeed.Send(TxPreEvent{btx}) - } - }() + go pool.txFeed.Send(TxPreEvent{tx}) return true, nil } From 6af064ec328b4c9bca537e8d0106aa08aa0b2786 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Sat, 8 Dec 2018 11:33:00 +0700 Subject: [PATCH 10/13] set block difficulty belong to creator turn --- consensus/posv/posv.go | 81 ++++++++++++++++++++++++++---------------- core/blockchain.go | 10 ++++-- eth/handler.go | 9 ++--- miner/worker.go | 22 ++---------- 4 files changed, 62 insertions(+), 60 deletions(-) diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index e51f01d8e6..341d20af00 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -352,12 +352,12 @@ func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, p if header.UncleHash != uncleHash { return errInvalidUncleHash } - // Ensure that the block's difficulty is meaningful (may not be correct at this point) - if number > 0 { - if header.Difficulty == nil || (header.Difficulty.Cmp(diffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) { - return errInvalidDifficulty - } - } + //// Ensure that the block's difficulty is meaningful (may not be correct at this point) + //if number > 0 { + // if header.Difficulty.Int64() != 1 { + // return errInvalidDifficulty + // } + //} // If all checks passed, validate any special fields for hard forks if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { return err @@ -483,30 +483,35 @@ func WhoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error) return m, nil } -func YourTurn(masternodes []common.Address, snap *Snapshot, header *types.Header, cur common.Address) (int, int, bool, error) { +func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header) (int, int, int, bool, error) { + masternodes := c.GetMasternodes(chain, parent) + snap, err := c.GetSnapshot(chain, parent) + if err != nil { + log.Error("Failed when trying to commit new work", "err", err) + return 0, -1, -1, false, err + } if len(masternodes) == 0 { - return -1, -1, true, nil + return 0, -1, -1, false, errors.New("Not found master nodes") } pre := common.Address{} // masternode[0] has chance to create block 1 - var err error preIndex := -1 - if header.Number.Uint64() != 0 { - pre, err = WhoIsCreator(snap, header) + if parent.Number.Uint64() != 0 { + pre, err = WhoIsCreator(snap, parent) if err != nil { - return 0, 0, false, err + return 0, 0, 0, false, err } preIndex = position(masternodes, pre) } - curIndex := position(masternodes, cur) - log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", cur, "position", curIndex) + curIndex := position(masternodes, c.signer) + log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", c.signer, "position", curIndex) for i, s := range masternodes { fmt.Printf("%d - %s\n", i, s.String()) } if (preIndex+1)%len(masternodes) == curIndex { - return preIndex, curIndex, true, nil + return len(masternodes), preIndex, curIndex, true, nil } - return preIndex, curIndex, false, nil + return len(masternodes), preIndex, curIndex, false, nil } // snapshot retrieves the authorization snapshot at a given point in time. @@ -625,6 +630,7 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par if err != nil { return err } + log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "difficulty", header.Difficulty) masternodes := c.GetMasternodes(chain, header) mstring := []string{} for _, m := range masternodes { @@ -742,9 +748,13 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error } c.lock.RUnlock() } + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } // Set the correct difficulty - header.Difficulty = big.NewInt(1) - + header.Difficulty = c.CalcDifficulty(chain, 0, parent) + log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty) // Ensure the extra data has all it's components if len(header.Extra) < extraVanity { header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) @@ -782,10 +792,7 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error header.MixDigest = common.Hash{} // Ensure the timestamp has the correct delay - parent := chain.GetHeader(header.ParentHash, number-1) - if parent == nil { - return consensus.ErrUnknownAncestor - } + header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period)) if header.Time.Int64() < time.Now().Unix() { header.Time = big.NewInt(time.Now().Unix()) @@ -928,22 +935,23 @@ func (c *Posv) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan // that a new block should have based on the previous blocks in the chain and the // current signer. func (c *Posv) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { - snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) + len, preIndex, curIndex, _, err := c.YourTurn(chain, parent) if err != nil { - return nil + return big.NewInt(int64(len + curIndex - preIndex)) } - return CalcDifficulty(snap, c.signer) + return big.NewInt(int64(len - Hop(len, preIndex, curIndex))) } // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // that a new block should have based on the previous blocks in the chain and the // current signer. -func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int { - if snap.inturn(snap.Number+1, signer) { - return new(big.Int).Set(diffInTurn) - } - return new(big.Int).Set(diffNoTurn) -} +//func CalcDifficulty(snap *Snapshot, parent *types.Header, signer common.Address) *big.Int { +// +// if snap.inturn(snap.Number+1, signer) { +// return new(big.Int).Set(diffInTurn) +// } +// return new(big.Int).Set(diffNoTurn) +//} // APIs implements consensus.Engine, returning the user facing RPC API to allow // controlling the signer voting. @@ -1057,3 +1065,14 @@ func ExtractValidatorsFromBytes(byteValidators []byte) []int64 { return validators } + +func Hop(len, pre, cur int) int { + switch { + case pre < cur: + return cur - (pre + 1) + case pre > cur: + return (len - pre) + (cur - 1) + default: + return len - 1 + } +} diff --git a/core/blockchain.go b/core/blockchain.go index 970fbdb2ee..9d1fbfff64 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -906,7 +906,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. defer bc.mu.Unlock() currentBlock := bc.CurrentBlock() - //localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) + localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) externTd := new(big.Int).Add(block.Difficulty(), ptd) // Irrelevant of the canonical status, write the block itself to the database @@ -980,8 +980,12 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf - - reorg := block.NumberU64() > currentBlock.NumberU64() + reorg := externTd.Cmp(localTd) > 0 + currentBlock = bc.CurrentBlock() + if !reorg && externTd.Cmp(localTd) == 0 { + // Split same-difficulty blocks by number + reorg = block.NumberU64() > currentBlock.NumberU64() + } if reorg { // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { diff --git a/eth/handler.go b/eth/handler.go index 17ad4df52e..f1a408d53d 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -657,17 +657,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty()) ) // Update the peers total difficulty if better than the previous - _, td := p.Head() - currentBlock := pm.blockchain.CurrentBlock() - currentTd := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) - log.Debug("NewBlockMsg", "p", p, "number", request.Block.NumberU64(), "trueTD", trueTD, "td", td, "currentTd", currentTd) - if trueTD.Cmp(td) > 0 { + if _, td := p.Head(); trueTD.Cmp(td) > 0 { p.SetHead(trueHead, trueTD) // Schedule a sync if above ours. Note, this will not fire a sync for a gap of // a singe block (as the true TD is below the propagated block), however this // scenario should easily be covered by the fetcher. - if trueTD.Cmp(currentTd) > 0 { + currentBlock := pm.blockchain.CurrentBlock() + if trueTD.Cmp(pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())) > 0 { go pm.synchronise(p) } } diff --git a/miner/worker.go b/miner/worker.go index b91daa79e8..44c1cb51e9 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -472,17 +472,6 @@ func abs(x int64) int64 { return x } -func hop(len, pre, cur int) int { - switch { - case pre < cur: - return cur - (pre + 1) - case pre > cur: - return (len - pre) + (cur - 1) - default: - return len - 1 - } -} - func (self *worker) commitNewWork() { self.mu.Lock() defer self.mu.Unlock() @@ -508,14 +497,7 @@ func (self *worker) commitNewWork() { if self.config.Posv != nil { // get masternodes set from latest checkpoint c := self.engine.(*posv.Posv) - masternodes := c.GetMasternodes(self.chain, parent.Header()) - snap, err := c.GetSnapshot(self.chain, parent.Header()) - if err != nil { - log.Error("Failed when trying to commit new work", "err", err) - return - } - signers = snap.Signers - preIndex, curIndex, ok, err := posv.YourTurn(masternodes, snap, parent.Header(), self.coinbase) + len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header()) if err != nil { log.Error("Failed when trying to commit new work", "err", err) return @@ -531,7 +513,7 @@ func (self *worker) commitNewWork() { // you're not allowed to create this block return } - h := hop(len(masternodes), preIndex, curIndex) + h := posv.Hop(len, preIndex, curIndex) gap := waitPeriod * int64(h) // Check nearest checkpoint block in hop range. nearest := self.config.Posv.Epoch - (parent.Header().Number.Uint64() % self.config.Posv.Epoch) From b2929f2b50953599bb63e2516bfdac51fe78b097 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Mon, 10 Dec 2018 11:43:58 +0700 Subject: [PATCH 11/13] refactor name flag committxwhennotmining -> announce-txs --- cmd/tomo/main.go | 2 +- cmd/utils/flags.go | 9 ++++----- consensus/posv/posv.go | 2 +- eth/backend.go | 2 +- miner/miner.go | 4 ++-- miner/worker.go | 42 +++++++++++++++++++++--------------------- node/config.go | 2 +- 7 files changed, 31 insertions(+), 32 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 0d7d60991c..9a125d0485 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -120,7 +120,7 @@ var ( //utils.GpoPercentileFlag, //utils.ExtraDataFlag, configFileFlag, - utils.CommitTxWhenNotMiningFlag, + utils.AnnounceTxsFlag, } rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 082c6a7e54..f158819e42 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -113,10 +113,9 @@ func NewApp(gitCommit, usage string) *cli.App { var ( // General settings - CommitTxWhenNotMiningFlag = DirectoryFlag{ - Name: "committxwhennotmining", + AnnounceTxsFlag = cli.BoolFlag{ + Name: "announce-txs", Usage: "Always commit transactions", - Value: DirectoryString{node.DefaultDataDir()}, } DataDirFlag = DirectoryFlag{ Name: "datadir", @@ -902,8 +901,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(NoUSBFlag.Name) { cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) } - if ctx.GlobalIsSet(CommitTxWhenNotMiningFlag.Name) { - cfg.CommitTxWhenNotMining = ctx.GlobalBool(CommitTxWhenNotMiningFlag.Name) + if ctx.GlobalIsSet(AnnounceTxsFlag.Name) { + cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name) } } diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 341d20af00..7b8e36e941 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -630,7 +630,7 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par if err != nil { return err } - log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "difficulty", header.Difficulty) + log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "difficulty", header.Difficulty, "creator", creator) masternodes := c.GetMasternodes(chain, header) mstring := []string{} for _, m := range masternodes { diff --git a/eth/backend.go b/eth/backend.go index 0cb6fe601b..ed11a8cae7 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -173,7 +173,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, ctx.GetConfig().CommitTxWhenNotMining) + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, ctx.GetConfig().AnnounceTxs) eth.miner.SetExtra(makeExtraData(config.ExtraData)) eth.ApiBackend = &EthApiBackend{eth, nil} diff --git a/miner/miner.go b/miner/miner.go index 021099e6a3..4bfab42f65 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -57,12 +57,12 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, commitTxWhenNotMining bool) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, announceTxs bool) *Miner { miner := &Miner{ eth: eth, mux: mux, engine: engine, - worker: newWorker(config, engine, common.Address{}, eth, mux, commitTxWhenNotMining), + worker: newWorker(config, engine, common.Address{}, eth, mux, announceTxs), canStart: 1, } miner.Register(NewCpuAgent(eth.BlockChain(), engine)) diff --git a/miner/worker.go b/miner/worker.go index 44c1cb51e9..2bdc132370 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -132,30 +132,30 @@ type worker struct { // atomic status counters mining int32 atWork int32 - commitTxWhenNotMining bool + announceTxs bool lastParentBlockCommit string } -func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux, commitTxWhenNotMining bool) *worker { +func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux, announceTxs bool) *worker { worker := &worker{ - config: config, - engine: engine, - eth: eth, - mux: mux, - txCh: make(chan core.TxPreEvent, txChanSize), - chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), - chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), - chainDb: eth.ChainDb(), - recv: make(chan *Result, resultQueueSize), - chain: eth.BlockChain(), - proc: eth.BlockChain().Validator(), - possibleUncles: make(map[common.Hash]*types.Block), - coinbase: coinbase, - agents: make(map[Agent]struct{}), - unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), - commitTxWhenNotMining: commitTxWhenNotMining, + config: config, + engine: engine, + eth: eth, + mux: mux, + txCh: make(chan core.TxPreEvent, txChanSize), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), + chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), + chainDb: eth.ChainDb(), + recv: make(chan *Result, resultQueueSize), + chain: eth.BlockChain(), + proc: eth.BlockChain().Validator(), + possibleUncles: make(map[common.Hash]*types.Block), + coinbase: coinbase, + agents: make(map[Agent]struct{}), + unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), + announceTxs: announceTxs, } - if worker.commitTxWhenNotMining { + if worker.announceTxs { // Subscribe TxPreEvent for tx pool worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) } @@ -253,7 +253,7 @@ func (self *worker) unregister(agent Agent) { } func (self *worker) update() { - if self.commitTxWhenNotMining { + if self.announceTxs { defer self.txSub.Unsubscribe() } defer self.chainHeadSub.Unsubscribe() @@ -486,7 +486,7 @@ func (self *worker) commitNewWork() { if parent.Hash().Hex() == self.lastParentBlockCommit { return } - if !self.commitTxWhenNotMining && atomic.LoadInt32(&self.mining) == 0 { + if !self.announceTxs && atomic.LoadInt32(&self.mining) == 0 { return } diff --git a/node/config.go b/node/config.go index e828001406..cbc27317fa 100644 --- a/node/config.go +++ b/node/config.go @@ -148,7 +148,7 @@ type Config struct { // Logger is a custom logger to use with the p2p.Server. Logger log.Logger `toml:",omitempty"` - CommitTxWhenNotMining bool `toml:",omitempty"` + AnnounceTxs bool `toml:",omitempty"` } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into From 5475ef4faa3e0d0f014aaebe8f4836cc3bad427e Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Mon, 10 Dec 2018 16:17:29 +0700 Subject: [PATCH 12/13] verify block difficulty --- consensus/posv/posv.go | 48 ++++++++++++++++++++++-------------------- miner/worker.go | 2 +- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 7b8e36e941..a36eea9150 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -352,12 +352,6 @@ func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, p if header.UncleHash != uncleHash { return errInvalidUncleHash } - //// Ensure that the block's difficulty is meaningful (may not be correct at this point) - //if number > 0 { - // if header.Difficulty.Int64() != 1 { - // return errInvalidDifficulty - // } - //} // If all checks passed, validate any special fields for hard forks if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { return err @@ -483,7 +477,7 @@ func WhoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error) return m, nil } -func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header) (int, int, int, bool, error) { +func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) { masternodes := c.GetMasternodes(chain, parent) snap, err := c.GetSnapshot(chain, parent) if err != nil { @@ -503,8 +497,10 @@ func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header) (int, } preIndex = position(masternodes, pre) } - curIndex := position(masternodes, c.signer) - log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", c.signer, "position", curIndex) + curIndex := position(masternodes, signer) + if signer == c.signer { + log.Info("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex) + } for i, s := range masternodes { fmt.Printf("%d - %s\n", i, s.String()) } @@ -630,7 +626,20 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par if err != nil { return err } - log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "difficulty", header.Difficulty, "creator", creator) + var parent *types.Header + if len(parents) > 0 { + parent = parents[len(parents)-1] + } else { + parent = chain.GetHeader(header.ParentHash, number-1) + } + difficulty := c.calcDifficulty(chain, parent, creator) + log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "block difficulty", header.Difficulty, "calc difficulty", difficulty, "creator", creator) + // Ensure that the block's difficulty is meaningful (may not be correct at this point) + if number > 0 { + if header.Difficulty.Int64() != difficulty.Int64() { + return errInvalidDifficulty + } + } masternodes := c.GetMasternodes(chain, header) mstring := []string{} for _, m := range masternodes { @@ -753,7 +762,7 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error return consensus.ErrUnknownAncestor } // Set the correct difficulty - header.Difficulty = c.CalcDifficulty(chain, 0, parent) + header.Difficulty = c.calcDifficulty(chain, parent, c.signer) log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty) // Ensure the extra data has all it's components if len(header.Extra) < extraVanity { @@ -935,24 +944,17 @@ func (c *Posv) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan // that a new block should have based on the previous blocks in the chain and the // current signer. func (c *Posv) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { - len, preIndex, curIndex, _, err := c.YourTurn(chain, parent) + return c.calcDifficulty(chain, parent, c.signer) +} + +func (c *Posv) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int { + len, preIndex, curIndex, _, err := c.YourTurn(chain, parent, signer) if err != nil { return big.NewInt(int64(len + curIndex - preIndex)) } return big.NewInt(int64(len - Hop(len, preIndex, curIndex))) } -// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty -// that a new block should have based on the previous blocks in the chain and the -// current signer. -//func CalcDifficulty(snap *Snapshot, parent *types.Header, signer common.Address) *big.Int { -// -// if snap.inturn(snap.Number+1, signer) { -// return new(big.Int).Set(diffInTurn) -// } -// return new(big.Int).Set(diffNoTurn) -//} - // APIs implements consensus.Engine, returning the user facing RPC API to allow // controlling the signer voting. func (c *Posv) APIs(chain consensus.ChainReader) []rpc.API { diff --git a/miner/worker.go b/miner/worker.go index 2bdc132370..edff6ea8fc 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -497,7 +497,7 @@ func (self *worker) commitNewWork() { if self.config.Posv != nil { // get masternodes set from latest checkpoint c := self.engine.(*posv.Posv) - len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header()) + len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header(),self.coinbase) if err != nil { log.Error("Failed when trying to commit new work", "err", err) return From bd5fcbdcdea686388a5bf29f82a3f26b0e7603c8 Mon Sep 17 00:00:00 2001 From: Tuna Date: Mon, 10 Dec 2018 17:14:49 +0700 Subject: [PATCH 13/13] tiny make up --- consensus/posv/posv.go | 2 +- core/blockchain.go | 6 +++--- core/error.go | 2 +- eth/backend.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index a36eea9150..e2916b2a5a 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -485,7 +485,7 @@ func (c *Posv) YourTurn(chain consensus.ChainReader, parent *types.Header, signe return 0, -1, -1, false, err } if len(masternodes) == 0 { - return 0, -1, -1, false, errors.New("Not found master nodes") + return 0, -1, -1, false, errors.New("Masternodes not found") } pre := common.Address{} // masternode[0] has chance to create block 1 diff --git a/core/blockchain.go b/core/blockchain.go index 9d1fbfff64..e68f0bdedd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -127,9 +127,9 @@ type BlockChain struct { bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format blockCache *lru.Cache // Cache for the most recent entire blocks futureBlocks *lru.Cache // future blocks are blocks added for later processing - resultProcess *lru.Cache - calculatingBlock *lru.Cache - downloadingBlock *lru.Cache + resultProcess *lru.Cache // Cache for processed blocks + calculatingBlock *lru.Cache // Cache for processing blocks + downloadingBlock *lru.Cache // Cache for downloading blocks (avoid duplication from fetcher) quit chan struct{} // blockchain quit channel running int32 // running must be called atomically // procInterrupt must be atomically called diff --git a/core/error.go b/core/error.go index 4af0d2922a..63be6ab83d 100644 --- a/core/error.go +++ b/core/error.go @@ -37,5 +37,5 @@ var ( ErrNotFoundM1 = errors.New("list M1 not found ") - ErrStopPreparingBlock = errors.New("stop calculate a block not vrified M2") + ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2") ) diff --git a/eth/backend.go b/eth/backend.go index ed11a8cae7..aba5539dd2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -358,7 +358,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { currentHeader := eth.blockchain.CurrentHeader() snap, err := c.GetSnapshot(eth.blockchain, currentHeader) if err != nil { - log.Error("Can't get snap shot with current header ", "number", currentHeader.Number, "hash", currentHeader.Hash().Hex()) + log.Error("Can't get snapshot with current header ", "number", currentHeader.Number, "hash", currentHeader.Hash().Hex()) return false } if _, ok := snap.Signers[address]; ok {