diff --git a/CHANGELOG.md b/CHANGELOG.md index 68af7939be..5940929c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +## v1.2.9 +FEATURE +* [\#1775](https://github.com/bnb-chain/bsc/pull/1775) upgrade: several hardfork block height on mainnet: Plato, Hertz(Berlin, London) + ## v1.2.8 FEATURE * [\#1626](https://github.com/bnb-chain/bsc/pull/1626) eth/filters, ethclient/gethclient: add fullTx option to pending tx filter diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index 86a81914ff..b1c9799a75 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -134,6 +134,7 @@ func main() { PrivateKey: nodeKey, NetRestrict: restrictList, FilterFunction: filterFunction, + IsBootnode: true, } if *runv5 { if _, err := discover.ListenV5(conn, ln, cfg); err != nil { diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index aed1052c1f..7522f555ac 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -67,7 +67,7 @@ const ( systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system - collectAdditionalVotesRewardRatio = float64(1) // ratio of additional reward for collecting more votes than needed + collectAdditionalVotesRewardRatio = 100 // ratio of additional reward for collecting more votes than needed, the denominator is 100 ) var ( @@ -1027,7 +1027,7 @@ func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, sta } quorum := cmath.CeilDiv(len(snap.Validators)*2, 3) if validVoteCount > quorum { - accumulatedWeights[head.Coinbase] += uint64(float64(validVoteCount-quorum) * collectAdditionalVotesRewardRatio) + accumulatedWeights[head.Coinbase] += uint64((validVoteCount - quorum) * collectAdditionalVotesRewardRatio / 100) } } @@ -1788,7 +1788,7 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) { header.GasLimit, header.GasUsed, header.Time, - header.Extra[:len(header.Extra)-65], // this will panic if extra is too short, should check before calling encodeSigHeader + header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader header.MixDigest, header.Nonce, }) diff --git a/core/tx_pool.go b/core/tx_pool.go index b5f1d3a2fd..6758b41360 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -727,7 +727,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // If the transaction is already known, discard it hash := tx.Hash() if pool.all.Get(hash) != nil { - //log.Trace("Discarding already known transaction", "hash", hash) + log.Trace("Discarding already known transaction", "hash", hash) knownTxMeter.Mark(1) return false, ErrAlreadyKnown } @@ -737,7 +737,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // If the transaction fails basic validation, discard it if err := pool.validateTx(tx, isLocal); err != nil { - //log.Trace("Discarding invalid transaction", "hash", hash, "err", err) + log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } @@ -749,7 +749,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it if !isLocal && pool.priced.Underpriced(tx) { - //log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) + log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) return false, ErrUnderpriced } @@ -770,7 +770,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // Special case, we still can't make the room for the new remote one. if !isLocal && !success { - //log.Trace("Discarding overflown transaction", "hash", hash) + log.Trace("Discarding overflown transaction", "hash", hash) overflowedTxMeter.Mark(1) return false, ErrTxPoolOverflow } @@ -797,7 +797,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // Kick out the underpriced remote transactions. for _, tx := range drop { - //log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) + log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) dropped := pool.removeTx(tx.Hash(), false) pool.changesSinceReorg += dropped @@ -822,7 +822,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.priced.Put(tx, isLocal) pool.journalTx(from, tx) pool.queueTxEvent(tx) - //log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) + log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) // Successful promotion, bump the heartbeat pool.beats[from] = time.Now() @@ -844,7 +844,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } pool.journalTx(from, tx) - //log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To()) + log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To()) return replaced, nil } diff --git a/eth/handler.go b/eth/handler.go index f3e05b2240..88afa3afef 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -944,6 +944,8 @@ func (h *handler) voteBroadcastLoop() { for { select { case event := <-h.voteCh: + // The timeliness of votes is very important, + // so one vote will be sent instantly without waiting for other votes for batch sending by design. h.BroadcastVote(event.Vote) case <-h.votesSub.Err(): return diff --git a/eth/handler_bsc.go b/eth/handler_bsc.go index 195691989b..d01a475ed0 100644 --- a/eth/handler_bsc.go +++ b/eth/handler_bsc.go @@ -61,9 +61,15 @@ func (h *bscHandler) Handle(peer *bsc.Peer, packet bsc.Packet) error { // handleVotesBroadcast is invoked from a peer's message handler when it transmits a // votes broadcast for the local node to process. func (h *bscHandler) handleVotesBroadcast(peer *bsc.Peer, votes []*types.VoteEnvelope) error { - // Try to put votes into votepool - for _, vote := range votes { - h.votepool.PutVote(vote) + if peer.IsOverLimitAfterReceiving() { + peer.Log().Warn("peer sending votes too much, votes dropped; it may be a ddos attack, please check!") + return nil } + // Here we only put the first vote, to avoid ddos attack by sending a large batch of votes. + // This won't abandon any valid vote, because one vote is sent every time referring to func voteBroadcastLoop + if len(votes) > 0 { + h.votepool.PutVote(votes[0]) + } + return nil } diff --git a/eth/protocols/bsc/peer.go b/eth/protocols/bsc/peer.go index 202502a4b8..77ac11599f 100644 --- a/eth/protocols/bsc/peer.go +++ b/eth/protocols/bsc/peer.go @@ -1,6 +1,8 @@ package bsc import ( + "time" + mapset "github.com/deckarep/golang-set" "github.com/ethereum/go-ethereum/common" @@ -16,6 +18,15 @@ const ( // voteBufferSize is the maximum number of batch votes can be hold before sending voteBufferSize = 21 * 2 + + // used to avoid of DDOS attack + // It's the max number of received votes per second from one peer + // 21 validators exist now, so 21 votes will be produced every one block interval + // so the limit is 7 = 21/3, here set it to 10 with a buffer. + receiveRateLimitPerSecond = 10 + + // the time span of one period + secondsPerPeriod = float64(10) ) // max is a helper function which returns the larger of the two given integers. @@ -31,6 +42,8 @@ type Peer struct { id string // Unique ID for the peer, cached knownVotes *knownCache // Set of vote hashes known to be known by this peer voteBroadcast chan []*types.VoteEnvelope // Channel used to queue votes propagation requests + periodBegin time.Time // Begin time of the latest period for votes counting + periodCounter uint // Votes number in the latest period *p2p.Peer // The embedded P2P package peer rw p2p.MsgReadWriter // Input/output streams for bsc @@ -47,6 +60,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) *Peer { id: id, knownVotes: newKnownCache(maxKnownVotes), voteBroadcast: make(chan []*types.VoteEnvelope, voteBufferSize), + periodBegin: time.Now(), + periodCounter: 0, Peer: p, rw: rw, version: version, @@ -114,6 +129,18 @@ func (p *Peer) AsyncSendVotes(votes []*types.VoteEnvelope) { } } +// Step into the next period when secondsPerPeriod seconds passed, +// Otherwise, check whether the number of received votes extra (secondsPerPeriod * receiveRateLimitPerSecond) +func (p *Peer) IsOverLimitAfterReceiving() bool { + if timeInterval := time.Since(p.periodBegin).Seconds(); timeInterval >= secondsPerPeriod { + p.periodBegin = time.Now() + p.periodCounter = 0 + return false + } + p.periodCounter += 1 + return p.periodCounter > uint(secondsPerPeriod*receiveRateLimitPerSecond) +} + // broadcastVotes is a write loop that schedules votes broadcasts // to the remote peer. The goal is to have an async writer that does not lock up // node internals and at the same time rate limits queued data. diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 40aec6b3be..b26b6b5c93 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -908,6 +908,8 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex return nil, err } } + vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) + // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { if timeout, err = time.ParseDuration(*config.Timeout); err != nil { @@ -919,13 +921,13 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex <-deadlineCtx.Done() if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { tracer.Stop(errors.New("execution timeout")) + // Stop evm execution. Note cancellation is not necessarily immediate. + vmenv.Cancel() } }() defer cancel() // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - if posa, ok := api.backend.Engine().(consensus.PoSA); ok && message.From() == vmctx.Coinbase && posa.IsSystemContract(message.To()) && message.GasPrice().Cmp(big.NewInt(0)) == 0 { balance := statedb.GetBalance(consensus.SystemAddress) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 48d03b6df0..5034233a26 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -150,7 +150,6 @@ func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common. func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if atomic.LoadUint32(&l.interrupt) > 0 { - l.env.Cancel() return } // check if already accumulated the specified number of logs diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index e6d1a6728c..949504fff8 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -46,7 +46,6 @@ func init() { // 0xc281d19e-0: 1 // } type fourByteTracer struct { - env *vm.EVM ids map[string]int // ids aggregates the 4byte ids found interrupt uint32 // Atomic flag to signal execution interruption reason error // Textual reason for the interruption @@ -80,8 +79,6 @@ func (t *fourByteTracer) store(id []byte, size int) { // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.env = env - // Update list of precompiles based on current block rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil) t.activePrecompiles = vm.ActivePrecompiles(rules) @@ -100,7 +97,6 @@ func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if atomic.LoadUint32(&t.interrupt) > 0 { - t.env.Cancel() return } if len(input) < 4 { diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go new file mode 100644 index 0000000000..878e2dc9d6 --- /dev/null +++ b/eth/tracers/native/mux.go @@ -0,0 +1,138 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package native + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" +) + +func init() { + register("muxTracer", newMuxTracer) +} + +// muxTracer is a go implementation of the Tracer interface which +// runs multiple tracers in one go. +type muxTracer struct { + names []string + tracers []tracers.Tracer +} + +// newMuxTracer returns a new mux tracer. +func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { + var config map[string]json.RawMessage + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err + } + } + objects := make([]tracers.Tracer, 0, len(config)) + names := make([]string, 0, len(config)) + for k, v := range config { + t, err := tracers.New(k, ctx, v) + if err != nil { + return nil, err + } + objects = append(objects, t) + names = append(names, k) + } + + return &muxTracer{names: names, tracers: objects}, nil +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (t *muxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + for _, t := range t.tracers { + t.CaptureStart(env, from, to, create, input, gas, value) + } +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { + for _, t := range t.tracers { + t.CaptureEnd(output, gasUsed, err) + } +} + +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + for _, t := range t.tracers { + t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) + } +} + +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { + for _, t := range t.tracers { + t.CaptureFault(pc, op, gas, cost, scope, depth, err) + } +} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + for _, t := range t.tracers { + t.CaptureEnter(typ, from, to, input, gas, value) + } +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) { + for _, t := range t.tracers { + t.CaptureExit(output, gasUsed, err) + } +} + +func (t *muxTracer) CaptureTxStart(gasLimit uint64) { + for _, t := range t.tracers { + t.CaptureTxStart(gasLimit) + } +} + +func (t *muxTracer) CaptureTxEnd(restGas uint64) { + for _, t := range t.tracers { + t.CaptureTxEnd(restGas) + } +} + +// GetResult returns an empty json object. +func (t *muxTracer) GetResult() (json.RawMessage, error) { + resObject := make(map[string]json.RawMessage) + for i, tt := range t.tracers { + r, err := tt.GetResult() + if err != nil { + return nil, err + } + resObject[t.names[i]] = r + } + res, err := json.Marshal(resObject) + if err != nil { + return nil, err + } + return res, nil +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (t *muxTracer) Stop(err error) { + for _, t := range t.tracers { + t.Stop(err) + } +} diff --git a/log/async_file_writer.go b/log/async_file_writer.go index c67df94823..2f37ab9c56 100644 --- a/log/async_file_writer.go +++ b/log/async_file_writer.go @@ -10,42 +10,51 @@ import ( "time" ) -type HourTicker struct { +type TimeTicker struct { stop chan struct{} C <-chan time.Time } -func NewHourTicker() *HourTicker { - ht := &HourTicker{ +// NewTimeTicker creates a TimeTicker that notifies based on rotateHours parameter. +// if rotateHours is 1 and current time is 11:32 it means that the ticker will tick at 12:00 +// if rotateHours is 5 and current time is 09:12 means that the ticker will tick at 11:00 +func NewTimeTicker(rotateHours int) *TimeTicker { + ch := make(chan time.Time) + tt := TimeTicker{ stop: make(chan struct{}), + C: ch, } - ht.C = ht.Ticker() - return ht + + tt.startTicker(ch, rotateHours) + + return &tt } -func (ht *HourTicker) Stop() { - ht.stop <- struct{}{} +func (tt *TimeTicker) Stop() { + tt.stop <- struct{}{} } -func (ht *HourTicker) Ticker() <-chan time.Time { - ch := make(chan time.Time) +func (tt *TimeTicker) startTicker(ch chan time.Time, rotateHours int) { go func() { - hour := time.Now().Hour() + nextRotationHour := getNextRotationHour(time.Now(), rotateHours) ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case t := <-ticker.C: - if t.Hour() != hour { + if t.Hour() == nextRotationHour { ch <- t - hour = t.Hour() + nextRotationHour = getNextRotationHour(time.Now(), rotateHours) } - case <-ht.stop: + case <-tt.stop: return } } }() - return ch +} + +func getNextRotationHour(now time.Time, delta int) int { + return now.Add(time.Hour * time.Duration(delta)).Hour() } type AsyncFileWriter struct { @@ -56,10 +65,10 @@ type AsyncFileWriter struct { started int32 buf chan []byte stop chan struct{} - hourTicker *HourTicker + timeTicker *TimeTicker } -func NewAsyncFileWriter(filePath string, bufSize int64) *AsyncFileWriter { +func NewAsyncFileWriter(filePath string, maxBytesSize int64, rotateHours int) *AsyncFileWriter { absFilePath, err := filepath.Abs(filePath) if err != nil { panic(fmt.Sprintf("get file path of logger error. filePath=%s, err=%s", filePath, err)) @@ -67,9 +76,9 @@ func NewAsyncFileWriter(filePath string, bufSize int64) *AsyncFileWriter { return &AsyncFileWriter{ filePath: absFilePath, - buf: make(chan []byte, bufSize), + buf: make(chan []byte, maxBytesSize), stop: make(chan struct{}), - hourTicker: NewHourTicker(), + timeTicker: NewTimeTicker(rotateHours), } } @@ -159,7 +168,7 @@ func (w *AsyncFileWriter) SyncWrite(msg []byte) { func (w *AsyncFileWriter) rotateFile() { select { - case <-w.hourTicker.C: + case <-w.timeTicker.C: if err := w.flushAndClose(); err != nil { fmt.Fprintf(os.Stderr, "flush and close file error. err=%s", err) } @@ -174,7 +183,7 @@ func (w *AsyncFileWriter) Stop() { w.stop <- struct{}{} w.wg.Wait() - w.hourTicker.Stop() + w.timeTicker.Stop() } func (w *AsyncFileWriter) Write(msg []byte) (n int, err error) { diff --git a/log/async_file_writer_test.go b/log/async_file_writer_test.go index 3e8ba3bac8..d6eae04567 100644 --- a/log/async_file_writer_test.go +++ b/log/async_file_writer_test.go @@ -1,26 +1,69 @@ package log import ( - "io/ioutil" "os" + "strconv" "strings" "testing" + "time" ) -func TestWriter(t *testing.T) { - w := NewAsyncFileWriter("./hello.log", 100) +func TestWriterHourly(t *testing.T) { + w := NewAsyncFileWriter("./hello.log", 100, 1) w.Start() w.Write([]byte("hello\n")) w.Write([]byte("world\n")) w.Stop() - files, _ := ioutil.ReadDir("./") + files, _ := os.ReadDir("./") for _, f := range files { fn := f.Name() if strings.HasPrefix(fn, "hello") { t.Log(fn) - content, _ := ioutil.ReadFile(fn) + content, _ := os.ReadFile(fn) t.Log(content) os.Remove(fn) } } } + +func TestGetNextRotationHour(t *testing.T) { + tcs := []struct { + now time.Time + delta int + expectedHour int + }{ + { + now: time.Date(1980, 1, 6, 15, 34, 0, 0, time.UTC), + delta: 3, + expectedHour: 18, + }, + { + now: time.Date(1980, 1, 6, 23, 59, 0, 0, time.UTC), + delta: 1, + expectedHour: 0, + }, + { + now: time.Date(1980, 1, 6, 22, 15, 0, 0, time.UTC), + delta: 2, + expectedHour: 0, + }, + { + now: time.Date(1980, 1, 6, 0, 0, 0, 0, time.UTC), + delta: 1, + expectedHour: 1, + }, + } + + test := func(now time.Time, delta, expectedHour int) func(*testing.T) { + return func(t *testing.T) { + got := getNextRotationHour(now, delta) + if got != expectedHour { + t.Fatalf("Expected %d, found: %d\n", expectedHour, got) + } + } + } + + for i, tc := range tcs { + t.Run("TestGetNextRotationHour_"+strconv.Itoa(i), test(tc.now, tc.delta, tc.expectedHour)) + } +} diff --git a/log/handler.go b/log/handler.go index 199d99d7ff..5ef616ef54 100644 --- a/log/handler.go +++ b/log/handler.go @@ -74,14 +74,14 @@ func FileHandler(path string, fmtr Format) (Handler, error) { // RotatingFileHandler returns a handler which writes log records to file chunks // at the given path. When a file's size reaches the limit, the handler creates // a new file named after the timestamp of the first log record it will contain. -func RotatingFileHandler(filePath string, limit uint, formatter Format) (Handler, error) { +func RotatingFileHandler(filePath string, limit uint, formatter Format, rotateHours int) (Handler, error) { if _, err := os.Stat(path.Dir(filePath)); os.IsNotExist(err) { err := os.MkdirAll(path.Dir(filePath), 0755) if err != nil { return nil, fmt.Errorf("could not create directory %s, %v", path.Dir(filePath), err) } } - fileWriter := NewAsyncFileWriter(filePath, int64(limit)) + fileWriter := NewAsyncFileWriter(filePath, int64(limit), rotateHours) fileWriter.Start() return StreamHandler(fileWriter, formatter), nil } diff --git a/log/logger.go b/log/logger.go index e78beb72c8..bd8aa3292c 100644 --- a/log/logger.go +++ b/log/logger.go @@ -244,12 +244,8 @@ func (c Ctx) toArray() []interface{} { return arr } -func NewFileLvlHandler(logPath string, maxBytesSize uint, level string) Handler { - rfh, err := RotatingFileHandler( - logPath, - maxBytesSize, - LogfmtFormat(), - ) +func NewFileLvlHandler(logPath string, maxBytesSize uint, level string, rotateHours int) Handler { + rfh, err := RotatingFileHandler(logPath, maxBytesSize, LogfmtFormat(), rotateHours) if err != nil { panic(err) } diff --git a/miner/worker.go b/miner/worker.go index 7640c2c675..6f196015e7 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -852,28 +852,30 @@ LOOP: // Check whether the tx is replay protected. If we're not in the EIP155 hf // phase, start ignoring the sender until we do. if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { - //log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) txs.Pop() continue } // Start executing the transaction env.state.Prepare(tx.Hash(), env.tcount) + from, _ := types.Sender(env.signer, tx) + logs, err := w.commitTransaction(env, tx, bloomProcessors) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account - //log.Trace("Gas limit exceeded for current block", "sender", from) + log.Trace("Gas limit exceeded for current block", "sender", from) txs.Pop() case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift - //log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) txs.Shift() case errors.Is(err, core.ErrNonceTooHigh): // Reorg notification data race between the transaction pool and miner, skip account = - //log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) txs.Pop() case errors.Is(err, nil): @@ -884,7 +886,7 @@ LOOP: case errors.Is(err, core.ErrTxTypeNotSupported): // Pop the unsupported transaction without shifting in the next from the account - //log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) txs.Pop() default: diff --git a/node/config.go b/node/config.go index 76fbc40e45..e15dc7232f 100644 --- a/node/config.go +++ b/node/config.go @@ -520,6 +520,7 @@ type LogConfig struct { FilePath *string `toml:",omitempty"` MaxBytesSize *uint `toml:",omitempty"` Level *string `toml:",omitempty"` + RotateHours int `toml:",omitempty"` // TermTimeFormat is the time format used for console logging. TermTimeFormat *string `toml:",omitempty"` diff --git a/node/node.go b/node/node.go index ef256f77e7..dd13895b1f 100644 --- a/node/node.go +++ b/node/node.go @@ -103,7 +103,17 @@ func New(conf *Config) (*Node, error) { } else { logFilePath = path.Join(*conf.LogConfig.FileRoot, *conf.LogConfig.FilePath) } - log.Root().SetHandler(log.NewFileLvlHandler(logFilePath, *conf.LogConfig.MaxBytesSize, *conf.LogConfig.Level)) + + if conf.LogConfig.RotateHours > 24 { + return nil, errors.New("Config.LogConfig.RotateHours cannot be greater than 24") + } + + // To maintain backwards compatibility, if RotateHours is not set or set to a negative value, then it defaults to 1 + if conf.LogConfig.RotateHours < 1 { + conf.LogConfig.RotateHours = 1 + } + + log.Root().SetHandler(log.NewFileLvlHandler(logFilePath, *conf.LogConfig.MaxBytesSize, *conf.LogConfig.Level, conf.LogConfig.RotateHours)) } } if conf.Logger == nil { diff --git a/p2p/discover/common.go b/p2p/discover/common.go index b4de7ed22d..c07334d0b2 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -80,6 +80,7 @@ type Config struct { ValidSchemes enr.IdentityScheme // allowed identity schemes Clock mclock.Clock FilterFunction NodeFilterFunc // function for filtering ENR entries + IsBootnode bool // defines if it's bootnode } func (cfg Config) withDefaults() Config { diff --git a/p2p/discover/table.go b/p2p/discover/table.go index a6301c999c..8c99280de0 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -40,9 +40,10 @@ import ( ) const ( - alpha = 3 // Kademlia concurrency factor - bucketSize = 16 // Kademlia bucket size - maxReplacements = 10 // Size of per-bucket replacement list + alpha = 3 // Kademlia concurrency factor + bucketSize = 16 // Kademlia bucket size + bootNodeBucketSize = 256 // Bigger bucket size for boot nodes + maxReplacements = 10 // Size of per-bucket replacement list // We keep buckets for the upper 1/15 of distances because // it's very unlikely we'll ever encounter a node that's closer. @@ -66,11 +67,12 @@ const ( // itself up-to-date by verifying the liveness of neighbors and requesting their node // records when announcements of a new record version are received. type Table struct { - mutex sync.Mutex // protects buckets, bucket content, nursery, rand - buckets [nBuckets]*bucket // index of known nodes by distance - nursery []*node // bootstrap nodes - rand *mrand.Rand // source of randomness, periodically reseeded - ips netutil.DistinctNetSet + mutex sync.Mutex // protects buckets, bucket content, nursery, rand + buckets [nBuckets]*bucket // index of known nodes by distance + bucketSize int // size of bucket + nursery []*node // bootstrap nodes + rand *mrand.Rand // source of randomness, periodically reseeded + ips netutil.DistinctNetSet log log.Logger db *enode.DB // database of known nodes @@ -102,7 +104,7 @@ type bucket struct { ips netutil.DistinctNetSet } -func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger, filter NodeFilterFunc) (*Table, error) { +func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger, filter NodeFilterFunc, bootnode bool) (*Table, error) { tab := &Table{ net: t, db: db, @@ -114,6 +116,10 @@ func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, log: log, enrFilter: filter, + bucketSize: bucketSize, + } + if bootnode { + tab.bucketSize = bootNodeBucketSize } if err := tab.setFallbackNodes(bootnodes); err != nil { return nil, err @@ -503,7 +509,7 @@ func (tab *Table) addSeenNodeSync(n *node) { // Already in bucket, don't add. return } - if len(b.entries) >= bucketSize { + if len(b.entries) >= tab.bucketSize { // Bucket full, maybe add as replacement. tab.addReplacement(b, n) return @@ -568,7 +574,7 @@ func (tab *Table) addVerifiedNodeSync(n *node) { // Already in bucket, moved to front. return } - if len(b.entries) >= bucketSize { + if len(b.entries) >= tab.bucketSize { // Bucket full, maybe add as replacement. tab.addReplacement(b, n) return @@ -578,7 +584,7 @@ func (tab *Table) addVerifiedNodeSync(n *node) { return } // Add to front of bucket. - b.entries, _ = pushNode(b.entries, n, bucketSize) + b.entries, _ = pushNode(b.entries, n, tab.bucketSize) b.replacements = deleteNode(b.replacements, n) n.addedAt = time.Now() if tab.nodeAddedHook != nil { diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 5da68e72e1..b1a209e94b 100644 --- a/p2p/discover/table_util_test.go +++ b/p2p/discover/table_util_test.go @@ -43,7 +43,7 @@ func init() { func newTestTable(t transport) (*Table, *enode.DB) { db, _ := enode.OpenDB("") - tab, _ := newTable(t, db, nil, log.Root(), nil) + tab, _ := newTable(t, db, nil, log.Root(), nil, false) go tab.loop() return tab, db } diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 5e106f30ae..2a5edbfd2e 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -143,7 +143,7 @@ func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) { log: cfg.Log, } - tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log, cfg.FilterFunction) + tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log, cfg.FilterFunction, cfg.IsBootnode) if err != nil { return nil, err } diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index f88ce33b8b..2c28cbb6f0 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -164,7 +164,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { closeCtx: closeCtx, cancelCloseCtx: cancelCloseCtx, } - tab, err := newTable(t, t.db, cfg.Bootnodes, cfg.Log, cfg.FilterFunction) + tab, err := newTable(t, t.db, cfg.Bootnodes, cfg.Log, cfg.FilterFunction, cfg.IsBootnode) if err != nil { return nil, err } diff --git a/params/config.go b/params/config.go index 638d54647d..46fc40b7e8 100644 --- a/params/config.go +++ b/params/config.go @@ -193,11 +193,11 @@ var ( // TODO modify blockNumber, make sure the blockNumber is not an integer multiple of 200 (epoch number) // TODO Caution !!! it should be very careful !!! LubanBlock: big.NewInt(29020050), - PlatoBlock: nil, + PlatoBlock: big.NewInt(30720096), // TODO modify blockNumber, make sure HertzBlock=BerlinBlock=LondonBlock to enable Berlin and London EIPs - BerlinBlock: nil, - LondonBlock: nil, - HertzBlock: nil, + BerlinBlock: big.NewInt(31302048), + LondonBlock: big.NewInt(31302048), + HertzBlock: big.NewInt(31302048), Parlia: &ParliaConfig{ Period: 3, diff --git a/params/version.go b/params/version.go index b551bcdc2f..e3e2605a5c 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 2 // Minor version component of the current release - VersionPatch = 8 // Patch version component of the current release + VersionPatch = 9 // Patch version component of the current release VersionMeta = "" // Version metadata to append to the version string )