From 86293f9a54c5ff33f3c40c41853b3d836931e27a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 13:34:38 +0100 Subject: [PATCH 1/9] eth/gasprice: fix percentile validation in eth_feeHistory (#28954) --- eth/gasprice/feehistory.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 78bade5ae..d82c251cd 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -207,8 +207,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast if p < 0 || p > 100 { return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) } - if i > 0 && p < rewardPercentiles[i-1] { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) + if i > 0 && p <= rewardPercentiles[i-1] { + return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } var ( From af906cc8e286d6c9487fddc54b06b9e5e98f1572 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Aug 2023 16:00:31 +0200 Subject: [PATCH 2/9] p2p: move ping handling into pingLoop goroutine (#27887) Moving the response sending there allows tracking all peer goroutines in the peer WaitGroup. --- p2p/peer.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/p2p/peer.go b/p2p/peer.go index 84efde6a4..dedceba16 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -113,6 +113,7 @@ type Peer struct { wg sync.WaitGroup protoErr chan error closed chan struct{} + pingRecv chan struct{} disc chan DiscReason // events receives message send / receive events if set @@ -249,6 +250,7 @@ func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { disc: make(chan DiscReason), protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop closed: make(chan struct{}), + pingRecv: make(chan struct{}, 16), log: log.New("id", conn.node.ID(), "conn", conn.flags), } return p @@ -309,9 +311,11 @@ loop: } func (p *Peer) pingLoop() { - ping := time.NewTimer(pingInterval) defer p.wg.Done() + + ping := time.NewTimer(pingInterval) defer ping.Stop() + for { select { case <-ping.C: @@ -320,6 +324,10 @@ func (p *Peer) pingLoop() { return } ping.Reset(pingInterval) + + case <-p.pingRecv: + SendItems(p.rw, pongMsg) + case <-p.closed: return } @@ -346,9 +354,10 @@ func (p *Peer) handle(msg Msg) error { switch { case msg.Code == pingMsg: msg.Discard() - gopool.Submit(func() { - SendItems(p.rw, pongMsg) - }) + select { + case p.pingRecv <- struct{}{}: + case <-p.closed: + } case msg.Code == discMsg: // This is the last message. We don't need to discard or // check errors because, the connection will be closed after it. From 5ebb5fc8e29f225194a603fc753a3f2f006c178a Mon Sep 17 00:00:00 2001 From: Matus Kysel Date: Mon, 8 May 2023 09:54:12 +0200 Subject: [PATCH 3/9] p2p/discover: implement ENR node filtering (#1320) --- cmd/bootnode/main.go | 39 ++++++++++++++--------- eth/backend.go | 1 + eth/protocols/eth/discovery.go | 6 ++++ p2p/discover/common.go | 45 ++++++++++++++++++++++---- p2p/discover/table.go | 52 +++++++++++++++++++++++++++--- p2p/discover/table_test.go | 56 +++++++++++++++++++++++++++------ p2p/discover/table_util_test.go | 4 +-- p2p/discover/v4_udp.go | 4 +-- p2p/discover/v5_udp.go | 2 +- p2p/discover/v5_udp_test.go | 2 +- p2p/server.go | 43 +++++++++++++++++++------ 11 files changed, 204 insertions(+), 50 deletions(-) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index a34a0e7cc..f2b24f067 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -35,19 +35,21 @@ import ( func main() { var ( - listenAddr = flag.String("addr", ":30301", "listen address") - genKey = flag.String("genkey", "", "generate a node key") - writeAddr = flag.Bool("writeaddress", false, "write out the node's public key and quit") - nodeKeyFile = flag.String("nodekey", "", "private key filename") - nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") - natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") - netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") - runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") - verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)") - vmodule = flag.String("vmodule", "", "log verbosity pattern") - - nodeKey *ecdsa.PrivateKey - err error + listenAddr = flag.String("addr", ":30301", "listen address") + genKey = flag.String("genkey", "", "generate a node key") + writeAddr = flag.Bool("writeaddress", false, "write out the node's public key and quit") + nodeKeyFile = flag.String("nodekey", "", "private key filename") + nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") + natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") + netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") + runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") + verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)") + vmodule = flag.String("vmodule", "", "log verbosity pattern") + networkFilter = flag.String("network", "", " filters nodes by eth ENR entry") + + nodeKey *ecdsa.PrivateKey + filterFunction discover.NodeFilterFunc + err error ) flag.Parse() @@ -86,6 +88,12 @@ func main() { } } + if *networkFilter != "" { + if filterFunction, err = discover.ParseEthFilter(*networkFilter); err != nil { + utils.Fatalf("-network: %v", err) + } + } + if *writeAddr { fmt.Printf("%x\n", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:]) os.Exit(0) @@ -124,8 +132,9 @@ func main() { db, _ := enode.OpenDB("") ln := enode.NewLocalNode(db, nodeKey) cfg := discover.Config{ - PrivateKey: nodeKey, - NetRestrict: restrictList, + PrivateKey: nodeKey, + NetRestrict: restrictList, + FilterFunction: filterFunction, } if *runv5 { if _, err := discover.ListenV5(conn, ln, cfg); err != nil { diff --git a/eth/backend.go b/eth/backend.go index 840d47d31..b5d782611 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -593,6 +593,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Lifecycle, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *Ethereum) Start() error { + eth.StartENRFilter(s.blockchain, s.p2pServer) eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) // Start the bloom bits servicing goroutines diff --git a/eth/protocols/eth/discovery.go b/eth/protocols/eth/discovery.go index 025479b42..e8aeb2e1c 100644 --- a/eth/protocols/eth/discovery.go +++ b/eth/protocols/eth/discovery.go @@ -19,6 +19,7 @@ package eth import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" ) @@ -57,6 +58,11 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) { }() } +func StartENRFilter(chain *core.BlockChain, p2p *p2p.Server) { + forkFilter := forkid.NewFilter(chain) + p2p.SetFilter(forkFilter) +} + // currentENREntry constructs an `eth` ENR entry based on the current state of the chain. func currentENREntry(chain *core.BlockChain) *enrEntry { return &enrEntry{ diff --git a/p2p/discover/common.go b/p2p/discover/common.go index e389821fd..b4de7ed22 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -18,13 +18,17 @@ package discover import ( "crypto/ecdsa" + "fmt" "net" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) // UDPConn is a network connection on which discovery can operate. @@ -35,18 +39,47 @@ type UDPConn interface { LocalAddr() net.Addr } +type NodeFilterFunc func(*enr.Record) bool + +func ParseEthFilter(chain string) (NodeFilterFunc, error) { + var filter forkid.Filter + switch chain { + case "bsc": + filter = forkid.NewStaticFilter(params.BSCChainConfig, params.BSCGenesisHash) + case "chapel": + filter = forkid.NewStaticFilter(params.ChapelChainConfig, params.ChapelGenesisHash) + case "rialto": + filter = forkid.NewStaticFilter(params.RialtoChainConfig, params.RialtoGenesisHash) + default: + return nil, fmt.Errorf("unknown network %q", chain) + } + + f := func(r *enr.Record) bool { + var eth struct { + ForkID forkid.ID + Tail []rlp.RawValue `rlp:"tail"` + } + if r.Load(enr.WithEntry("eth", ð)) != nil { + return false + } + return filter(eth.ForkID) == nil + } + return f, nil +} + // Config holds settings for the discovery listener. type Config struct { // These settings are required and configure the UDP listener: PrivateKey *ecdsa.PrivateKey // These settings are optional: - NetRestrict *netutil.Netlist // list of allowed IP networks - Bootnodes []*enode.Node // list of bootstrap nodes - Unhandled chan<- ReadPacket // unhandled packets are sent on this channel - Log log.Logger // if set, log messages go here - ValidSchemes enr.IdentityScheme // allowed identity schemes - Clock mclock.Clock + NetRestrict *netutil.Netlist // list of allowed IP networks + Bootnodes []*enode.Node // list of bootstrap nodes + Unhandled chan<- ReadPacket // unhandled packets are sent on this channel + Log log.Logger // if set, log messages go here + ValidSchemes enr.IdentityScheme // allowed identity schemes + Clock mclock.Clock + FilterFunction NodeFilterFunc // function for filtering ENR entries } func (cfg Config) withDefaults() Config { diff --git a/p2p/discover/table.go b/p2p/discover/table.go index bf136cf48..a6301c999 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -80,6 +80,8 @@ type Table struct { closeReq chan struct{} closed chan struct{} + enrFilter NodeFilterFunc + nodeAddedHook func(*node) // for testing } @@ -100,7 +102,7 @@ type bucket struct { ips netutil.DistinctNetSet } -func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger) (*Table, error) { +func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger, filter NodeFilterFunc) (*Table, error) { tab := &Table{ net: t, db: db, @@ -111,6 +113,7 @@ func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger rand: mrand.New(mrand.NewSource(0)), ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, log: log, + enrFilter: filter, } if err := tab.setFallbackNodes(bootnodes); err != nil { return nil, err @@ -339,10 +342,16 @@ func (tab *Table) doRevalidate(done chan<- struct{}) { // Also fetch record if the node replied and returned a higher sequence number. if last.Seq() < remoteSeq { - n, err := tab.net.RequestENR(unwrapNode(last)) - if err != nil { - tab.log.Debug("ENR request failed", "id", last.ID(), "addr", last.addr(), "err", err) + n, enrErr := tab.net.RequestENR(unwrapNode(last)) + if enrErr != nil { + tab.log.Debug("ENR request failed", "id", last.ID(), "addr", last.addr(), "err", enrErr) } else { + if tab.enrFilter != nil { + if !tab.enrFilter(n.Record()) { + tab.log.Trace("ENR record filter out", "id", last.ID(), "addr", last.addr()) + err = fmt.Errorf("filtered node") + } + } last = &node{Node: *n, addedAt: last.addedAt, livenessChecks: last.livenessChecks} } } @@ -473,10 +482,20 @@ func (tab *Table) bucketAtDistance(d int) *bucket { // // The caller must not hold tab.mutex. func (tab *Table) addSeenNode(n *node) { + gopool.Submit(func() { + tab.addSeenNodeSync(n) + }) +} + +func (tab *Table) addSeenNodeSync(n *node) { if n.ID() == tab.self().ID() { return } + if tab.filterNode(n) { + return + } + tab.mutex.Lock() defer tab.mutex.Unlock() b := tab.bucket(n.ID()) @@ -502,6 +521,20 @@ func (tab *Table) addSeenNode(n *node) { } } +func (tab *Table) filterNode(n *node) bool { + if tab.enrFilter == nil { + return false + } + if node, err := tab.net.RequestENR(unwrapNode(n)); err != nil { + tab.log.Debug("ENR request failed", "id", n.ID(), "addr", n.addr(), "err", err) + return false + } else if !tab.enrFilter(node.Record()) { + tab.log.Trace("ENR record filter out", "id", n.ID(), "addr", n.addr()) + return true + } + return false +} + // addVerifiedNode adds a node whose existence has been verified recently to the front of a // bucket. If the node is already in the bucket, it is moved to the front. If the bucket // has no space, the node is added to the replacements list. @@ -511,14 +544,23 @@ func (tab *Table) addSeenNode(n *node) { // ping repeatedly. // // The caller must not hold tab.mutex. + func (tab *Table) addVerifiedNode(n *node) { + gopool.Submit(func() { + tab.addVerifiedNodeSync(n) + }) +} + +func (tab *Table) addVerifiedNodeSync(n *node) { if !tab.isInitDone() { return } if n.ID() == tab.self().ID() { return } - + if tab.filterNode(n) { + return + } tab.mutex.Lock() defer tab.mutex.Unlock() b := tab.bucket(n.ID()) diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index 5f40c967f..cc5884e32 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -27,10 +27,13 @@ import ( "testing/quick" "time" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) func TestTable_pingReplace(t *testing.T) { @@ -65,7 +68,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding // its bucket if it is unresponsive. Revalidate again to ensure that transport.dead[last.ID()] = !lastInBucketIsResponding transport.dead[pingSender.ID()] = !newNodeIsResponding - tab.addSeenNode(pingSender) + tab.addSeenNodeSync(pingSender) tab.doRevalidate(make(chan struct{}, 1)) tab.doRevalidate(make(chan struct{}, 1)) @@ -148,7 +151,7 @@ func TestTable_IPLimit(t *testing.T) { for i := 0; i < tableIPLimit+1; i++ { n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)}) - tab.addSeenNode(n) + tab.addSeenNodeSync(n) } if tab.len() > tableIPLimit { t.Errorf("too many nodes in table") @@ -314,8 +317,8 @@ func TestTable_addVerifiedNode(t *testing.T) { // Insert two nodes. n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1}) n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2}) - tab.addSeenNode(n1) - tab.addSeenNode(n2) + tab.addSeenNodeSync(n1) + tab.addSeenNodeSync(n2) // Verify bucket content: bcontent := []*node{n1, n2} @@ -327,7 +330,7 @@ func TestTable_addVerifiedNode(t *testing.T) { newrec := n2.Record() newrec.Set(enr.IP{99, 99, 99, 99}) newn2 := wrapNode(enode.SignNull(newrec, n2.ID())) - tab.addVerifiedNode(newn2) + tab.addVerifiedNodeSync(newn2) // Check that bucket is updated correctly. newBcontent := []*node{newn2, n1} @@ -346,8 +349,8 @@ func TestTable_addSeenNode(t *testing.T) { // Insert two nodes. n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1}) n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2}) - tab.addSeenNode(n1) - tab.addSeenNode(n2) + tab.addSeenNodeSync(n1) + tab.addSeenNodeSync(n2) // Verify bucket content: bcontent := []*node{n1, n2} @@ -359,7 +362,7 @@ func TestTable_addSeenNode(t *testing.T) { newrec := n2.Record() newrec.Set(enr.IP{99, 99, 99, 99}) newn2 := wrapNode(enode.SignNull(newrec, n2.ID())) - tab.addSeenNode(newn2) + tab.addSeenNodeSync(newn2) // Check that bucket content is unchanged. if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) { @@ -382,7 +385,7 @@ func TestTable_revalidateSyncRecord(t *testing.T) { r.Set(enr.IP(net.IP{127, 0, 0, 1})) id := enode.ID{1} n1 := wrapNode(enode.SignNull(&r, id)) - tab.addSeenNode(n1) + tab.addSeenNodeSync(n1) // Update the node record. r.Set(enr.WithEntry("foo", "bar")) @@ -396,6 +399,41 @@ func TestTable_revalidateSyncRecord(t *testing.T) { } } +// This test checks that ENR filtering is working properly +func TestTable_filterNode(t *testing.T) { + // Create ENR filter + type eth struct { + ForkID forkid.ID + Tail []rlp.RawValue `rlp:"tail"` + } + + enrFilter, _ := ParseEthFilter("bsc") + + // Check test ENR record + var r1 enr.Record + r1.Set(enr.WithEntry("foo", "bar")) + if enrFilter(&r1) { + t.Fatalf("filterNode doesn't work correctly for entry") + } + t.Logf("Check test ENR record - passed") + + // Check wrong genesis ENR record + var r2 enr.Record + r2.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.BSCChainConfig, params.ChapelGenesisHash, uint64(0))})) + if enrFilter(&r2) { + t.Fatalf("filterNode doesn't work correctly for wrong genesis entry") + } + t.Logf("Check wrong genesis ENR record - passed") + + // Check correct genesis ENR record + var r3 enr.Record + r3.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.BSCChainConfig, params.BSCGenesisHash, uint64(0))})) + if !enrFilter(&r3) { + t.Fatalf("filterNode doesn't work correctly for correct genesis entry") + } + t.Logf("Check correct genesis ENR record - passed") +} + // gen wraps quick.Value so it's easier to use. // it generates a random value of the given value's type. func gen(typ interface{}, rand *rand.Rand) interface{} { diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 47a2e7ac3..5da68e72e 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()) + tab, _ := newTable(t, db, nil, log.Root(), nil) go tab.loop() return tab, db } @@ -110,7 +110,7 @@ func fillBucket(tab *Table, n *node) (last *node) { // if the bucket is not full. The caller must not hold tab.mutex. func fillTable(tab *Table, nodes []*node) { for _, n := range nodes { - tab.addSeenNode(n) + tab.addSeenNodeSync(n) } } diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index d743cc741..5e106f30a 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -42,7 +42,7 @@ var ( errExpired = errors.New("expired") errUnsolicitedReply = errors.New("unsolicited reply") errUnknownNode = errors.New("unknown node") - errTimeout = errors.New("RPC timeout") + errTimeout = errors.New("udp timeout") errClockWarp = errors.New("reply deadline too far in the future") errClosed = errors.New("socket closed") errLowPort = errors.New("low port") @@ -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) + tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log, cfg.FilterFunction) if err != nil { return nil, err } diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 1e9909559..e9b83cada 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) + tab, err := newTable(t, t.db, cfg.Bootnodes, cfg.Log, cfg.FilterFunction) if err != nil { return nil, err } diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index f061f5ab4..0290ab4e2 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -145,7 +145,7 @@ func TestUDPv5_unknownPacket(t *testing.T) { // Make node known. n := test.getNode(test.remotekey, test.remoteaddr).Node() - test.table.addSeenNode(wrapNode(n)) + test.table.addSeenNodeSync(wrapNode(n)) test.packetIn(&v5wire.Unknown{Nonce: nonce}) test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { diff --git a/p2p/server.go b/p2p/server.go index 38c2d73a0..8008ed8ae 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/gopool" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -40,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/rlp" ) const ( @@ -193,6 +195,8 @@ type Server struct { discmix *enode.FairMix dialsched *dialScheduler + forkFilter forkid.Filter + // Channels into the run loop. quit chan struct{} addtrusted chan *enode.Node @@ -593,6 +597,21 @@ func (srv *Server) setupDiscovery() error { } srv.localnode.SetFallbackUDP(realaddr.Port) + // ENR filter function + f := func(r *enr.Record) bool { + if srv.forkFilter == nil { + return true + } + var eth struct { + ForkID forkid.ID + Tail []rlp.RawValue `rlp:"tail"` + } + if r.Load(enr.WithEntry("eth", ð)) != nil { + return false + } + return srv.forkFilter(eth.ForkID) == nil + } + // Discovery V4 var unhandled chan discover.ReadPacket var sconn *sharedUDPConn @@ -602,11 +621,12 @@ func (srv *Server) setupDiscovery() error { sconn = &sharedUDPConn{conn, unhandled} } cfg := discover.Config{ - PrivateKey: srv.PrivateKey, - NetRestrict: srv.NetRestrict, - Bootnodes: srv.BootstrapNodes, - Unhandled: unhandled, - Log: srv.log, + PrivateKey: srv.PrivateKey, + NetRestrict: srv.NetRestrict, + Bootnodes: srv.BootstrapNodes, + Unhandled: unhandled, + Log: srv.log, + FilterFunction: f, } ntab, err := discover.ListenV4(conn, srv.localnode, cfg) if err != nil { @@ -619,10 +639,11 @@ func (srv *Server) setupDiscovery() error { // Discovery V5 if srv.DiscoveryV5 { cfg := discover.Config{ - PrivateKey: srv.PrivateKey, - NetRestrict: srv.NetRestrict, - Bootnodes: srv.BootstrapNodesV5, - Log: srv.log, + PrivateKey: srv.PrivateKey, + NetRestrict: srv.NetRestrict, + Bootnodes: srv.BootstrapNodesV5, + Log: srv.log, + FilterFunction: f, } var err error if sconn != nil { @@ -666,6 +687,10 @@ func (srv *Server) maxInboundConns() int { return srv.MaxPeers - srv.maxDialedConns() } +func (srv *Server) SetFilter(f forkid.Filter) { + srv.forkFilter = f +} + func (srv *Server) maxDialedConns() (limit int) { if srv.NoDial || srv.MaxPeers == 0 { return 0 From 9c16f6f616a32017491d6b74864efec1479a2d6a Mon Sep 17 00:00:00 2001 From: Matus Kysel Date: Wed, 12 Jul 2023 02:48:13 +0200 Subject: [PATCH 4/9] discov: increase bucket size for bootnodes (#1727) --- cmd/bootnode/main.go | 1 + p2p/discover/common.go | 1 + p2p/discover/table.go | 30 ++++++++++++++++++------------ p2p/discover/table_util_test.go | 2 +- p2p/discover/v4_udp.go | 2 +- p2p/discover/v5_udp.go | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index f2b24f067..7a8ab1e9a 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -135,6 +135,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/p2p/discover/common.go b/p2p/discover/common.go index b4de7ed22..c07334d0b 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 a6301c999..8c99280de 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 5da68e72e..b1a209e94 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 5e106f30a..2a5edbfd2 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 e9b83cada..73f20db1c 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 } From 06ce8f77ad4648cc2a0ca56e10f34054dc985d34 Mon Sep 17 00:00:00 2001 From: Matus Kysel Date: Thu, 27 Jul 2023 15:39:04 +0200 Subject: [PATCH 5/9] discov: do not filter out bootnodes (#1773) --- p2p/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/server.go b/p2p/server.go index 8008ed8ae..56f5a6041 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -607,7 +607,7 @@ func (srv *Server) setupDiscovery() error { Tail []rlp.RawValue `rlp:"tail"` } if r.Load(enr.WithEntry("eth", ð)) != nil { - return false + return true } return srv.forkFilter(eth.ForkID) == nil } From 788f951cbf4671eb60f807d58280198c77044c3c Mon Sep 17 00:00:00 2001 From: zfliex Date: Mon, 15 Apr 2024 13:53:15 +0800 Subject: [PATCH 6/9] discov: fix ENR node --- cmd/bootnode/main.go | 2 +- p2p/discover/common.go | 10 ++++------ p2p/discover/table_test.go | 6 +++--- p2p/peer.go | 1 - 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index 7a8ab1e9a..95a86757f 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -45,7 +45,7 @@ func main() { runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)") vmodule = flag.String("vmodule", "", "log verbosity pattern") - networkFilter = flag.String("network", "", " filters nodes by eth ENR entry") + networkFilter = flag.String("network", "", " filters nodes by eth ENR entry") nodeKey *ecdsa.PrivateKey filterFunction discover.NodeFilterFunc diff --git a/p2p/discover/common.go b/p2p/discover/common.go index c07334d0b..1ea184f49 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -44,12 +44,10 @@ type NodeFilterFunc func(*enr.Record) bool func ParseEthFilter(chain string) (NodeFilterFunc, error) { var filter forkid.Filter switch chain { - case "bsc": - filter = forkid.NewStaticFilter(params.BSCChainConfig, params.BSCGenesisHash) - case "chapel": - filter = forkid.NewStaticFilter(params.ChapelChainConfig, params.ChapelGenesisHash) - case "rialto": - filter = forkid.NewStaticFilter(params.RialtoChainConfig, params.RialtoGenesisHash) + case "core": + filter = forkid.NewStaticFilter(params.CoreChainConfig, params.CoreGenesisHash) + case "buffalo": + filter = forkid.NewStaticFilter(params.BuffaloChainConfig, params.BuffaloGenesisHash) default: return nil, fmt.Errorf("unknown network %q", chain) } diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index cc5884e32..86b03e05a 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -407,7 +407,7 @@ func TestTable_filterNode(t *testing.T) { Tail []rlp.RawValue `rlp:"tail"` } - enrFilter, _ := ParseEthFilter("bsc") + enrFilter, _ := ParseEthFilter("core") // Check test ENR record var r1 enr.Record @@ -419,7 +419,7 @@ func TestTable_filterNode(t *testing.T) { // Check wrong genesis ENR record var r2 enr.Record - r2.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.BSCChainConfig, params.ChapelGenesisHash, uint64(0))})) + r2.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.CoreChainConfig, params.BuffaloGenesisHash, uint64(0))})) if enrFilter(&r2) { t.Fatalf("filterNode doesn't work correctly for wrong genesis entry") } @@ -427,7 +427,7 @@ func TestTable_filterNode(t *testing.T) { // Check correct genesis ENR record var r3 enr.Record - r3.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.BSCChainConfig, params.BSCGenesisHash, uint64(0))})) + r3.Set(enr.WithEntry("eth", eth{ForkID: forkid.NewID(params.CoreChainConfig, params.CoreGenesisHash, uint64(0))})) if !enrFilter(&r3) { t.Fatalf("filterNode doesn't work correctly for correct genesis entry") } diff --git a/p2p/peer.go b/p2p/peer.go index dedceba16..c27b98513 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -25,7 +25,6 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/common/gopool" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" From 96abe9d1c72baac567020a20f4fdb3538bef32f5 Mon Sep 17 00:00:00 2001 From: Eric <45141191+zlacfzy@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:05:12 +0800 Subject: [PATCH 7/9] eth/gasprice: add query limit to defend DDOS attack (#2423) --- eth/gasprice/feehistory.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index d82c251cd..1dfd39792 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -42,6 +42,7 @@ const ( // maxBlockFetchers is the max number of goroutines to spin up to pull blocks // for the fee history calculation (mostly relevant for LES). maxBlockFetchers = 4 + maxQueryLimit = 100 ) // blockFees represents a single block for processing @@ -199,6 +200,9 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast if len(rewardPercentiles) != 0 { maxFeeHistory = oracle.maxBlockHistory } + if len(rewardPercentiles) > maxQueryLimit { + return common.Big0, nil, nil, nil, fmt.Errorf("%w: over the query limit %d", errInvalidPercentile, maxQueryLimit) + } if blocks > maxFeeHistory { log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) blocks = maxFeeHistory From e82d6bbf91b75927ca2002e82b1adb0152de2c35 Mon Sep 17 00:00:00 2001 From: zfliex Date: Mon, 24 Jun 2024 17:07:11 +0800 Subject: [PATCH 8/9] upgrade version to v1.0.9 --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index fdcb70f31..e86b29b0a 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 = 0 // 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 ) From cccc049c9d11053b0656bb5c0aabc68843ecf8a3 Mon Sep 17 00:00:00 2001 From: charles2022wood Date: Mon, 24 Jun 2024 20:06:42 +0800 Subject: [PATCH 9/9] Release: Prepare for release v1.0.9 Signed-off-by: charles2022wood --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65b0b42d0..32e4ad8b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,35 @@ # Changelog + + +## v1.0.9 + +Improvements +* [#1](https://github.com/coredao-org/core-chain/commit/96abe9d1c72baac567020a20f4fdb3538bef32f5) Add query limit to defend DDOS attack. +* [#2](https://github.com/coredao-org/core-chain/commit/af906cc8e286d6c9487fddc54b06b9e5e98f1572) Moving the response sending there allows tracking all peer goroutines +* [#3] Enlarge the default block gas limit from 40m to 50m. + +## v1.0.8 + +Same changes with 1.0.7, but this version is for mainnet. + +## v1.0.7 + +Improvements +* [#1](https://github.com/coredao-org/core-genesis-contract/commit/fbb4a12b0e7d7239fff0eaf15f37edfe762e987e) Enables self custody BTC staking on Core blockchain. + +## v1.0.6 + +Same changes with 1.0.5, but this version is for mainnet. + +## v1.0.5 + +Improvements +* [#1](https://github.com/coredao-org/core-genesis-contract/commit/8b8442e8917715734b38018b76f77431e57990d7) support to verify normal bitcoin transaction base on the system contracts named BtcLightClient + +## v1.0.4 + +Same changes with 1.0.3, but this version is for mainnet. + ## v1.0.3 Improvements