diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 60c3951a62..0b6e802ff0 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -82,28 +82,28 @@ jobs: # ============================== - name: Upload Linux Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'ubuntu-latest' with: name: linux path: ./build/bin/geth - name: Upload MacOS Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'macos-latest' with: name: macos path: ./build/bin/geth - name: Upload Windows Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'windows-latest' with: name: windows path: ./build/bin/geth.exe - name: Upload ARM-64 Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'ubuntu-latest' with: name: arm64 @@ -125,25 +125,25 @@ jobs: # ============================== - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: linux path: ./linux - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: macos path: ./macos - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: windows path: ./windows - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: arm64 path: ./arm64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 304d6f5d0a..c90c28c6e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,28 +81,28 @@ jobs: # ============================== - name: Upload Linux Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'ubuntu-latest' with: name: linux path: ./build/bin/geth - name: Upload MacOS Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'macos-latest' with: name: macos path: ./build/bin/geth - name: Upload Windows Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'windows-latest' with: name: windows path: ./build/bin/geth.exe - name: Upload ARM-64 Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'ubuntu-latest' with: name: arm64 @@ -124,25 +124,25 @@ jobs: # ============================== - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: linux path: ./linux - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: macos path: ./macos - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: windows path: ./windows - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: arm64 path: ./arm64 diff --git a/.nancy-ignore b/.nancy-ignore index 0b64e763db..6d058384b6 100644 --- a/.nancy-ignore +++ b/.nancy-ignore @@ -1,2 +1,3 @@ CVE-2024-34478 # "CWE-754: Improper Check for Unusual or Exceptional Conditions." This vulnerability is BTC only, BSC does not have the issue. CVE-2024-6104 # "CWE-532: Information Exposure Through Log Files" This is caused by the vulnerabilities go-retryablehttp@v0.7.4, it is only used in cmd devp2p, impact is limited. will upgrade to v0.7.7 later +CVE-2024-8421 # "CWE-400: Uncontrolled Resource Consumption (Resource Exhaustion)" This vulnerability is caused by issues in the golang.org/x/net package. Even the latest version(v0.29.0) has not yet addressed it, but we will continue to monitor updates closely. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5907ec247c..513ffbc2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,26 @@ # Changelog +## v1.4.15 +### BUGFIX +* [\#2680](https://github.com/bnb-chain/bsc/pull/2680) txpool: apply miner's gasceil to txpool +* [\#2688](https://github.com/bnb-chain/bsc/pull/2688) txpool: set default GasCeil from 30M to 0 +* [\#2696](https://github.com/bnb-chain/bsc/pull/2696) miner: limit block size to eth protocol msg size +* [\#2684](https://github.com/bnb-chain/bsc/pull/2684) eth: Add sidecars when available to broadcasted current block + +### FEATURE +* [\#2672](https://github.com/bnb-chain/bsc/pull/2672) faucet: with mainnet balance check, 0.002BNB at least +* [\#2678](https://github.com/bnb-chain/bsc/pull/2678) beaconserver: simulated beacon api server for op-stack +* [\#2687](https://github.com/bnb-chain/bsc/pull/2687) faucet: support customized token +* [\#2698](https://github.com/bnb-chain/bsc/pull/2698) faucet: add example for custimized token +* [\#2706](https://github.com/bnb-chain/bsc/pull/2706) faucet: update DIN token faucet support + +### IMPROVEMENT +* [\#2677](https://github.com/bnb-chain/bsc/pull/2677) log: add some p2p log +* [\#2679](https://github.com/bnb-chain/bsc/pull/2679) build(deps): bump actions/download-artifact in /.github/workflows +* [\#2662](https://github.com/bnb-chain/bsc/pull/2662) metrics: add some extra feature flags as node stats +* [\#2675](https://github.com/bnb-chain/bsc/pull/2675) fetcher: Sleep after marking block as done when requeuing +* [\#2695](https://github.com/bnb-chain/bsc/pull/2695) CI: nancy ignore CVE-2024-8421 +* [\#2689](https://github.com/bnb-chain/bsc/pull/2689) consensus/parlia: wait more time when processing huge blocks + ## v1.4.14 ### BUGFIX diff --git a/Makefile b/Makefile index 30a36697c6..252813ba05 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,11 @@ geth: @echo "Done building." @echo "Run \"$(GOBIN)/geth\" to launch geth." +#? faucet: Build faucet +faucet: + $(GORUN) build/ci.go install ./cmd/faucet + @echo "Done building faucet" + #? all: Build all packages and executables all: $(GORUN) build/ci.go install diff --git a/beacon/fakebeacon/api_func.go b/beacon/fakebeacon/api_func.go new file mode 100644 index 0000000000..674bf7fb39 --- /dev/null +++ b/beacon/fakebeacon/api_func.go @@ -0,0 +1,87 @@ +package fakebeacon + +import ( + "context" + "sort" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" +) + +type BlobSidecar struct { + Blob kzg4844.Blob `json:"blob"` + Index int `json:"index"` + KZGCommitment kzg4844.Commitment `json:"kzg_commitment"` + KZGProof kzg4844.Proof `json:"kzg_proof"` +} + +type APIGetBlobSidecarsResponse struct { + Data []*BlobSidecar `json:"data"` +} + +type ReducedGenesisData struct { + GenesisTime string `json:"genesis_time"` +} + +type APIGenesisResponse struct { + Data ReducedGenesisData `json:"data"` +} + +type ReducedConfigData struct { + SecondsPerSlot string `json:"SECONDS_PER_SLOT"` +} + +type IndexedBlobHash struct { + Index int // absolute index in the block, a.k.a. position in sidecar blobs array + Hash common.Hash // hash of the blob, used for consistency checks +} + +func configSpec() ReducedConfigData { + return ReducedConfigData{SecondsPerSlot: "1"} +} + +func beaconGenesis() APIGenesisResponse { + return APIGenesisResponse{Data: ReducedGenesisData{GenesisTime: "0"}} +} + +func beaconBlobSidecars(ctx context.Context, backend ethapi.Backend, slot uint64, indices []int) (APIGetBlobSidecarsResponse, error) { + var blockNrOrHash rpc.BlockNumberOrHash + header, err := fetchBlockNumberByTime(ctx, int64(slot), backend) + if err != nil { + log.Error("Error fetching block number", "slot", slot, "indices", indices) + return APIGetBlobSidecarsResponse{}, err + } + sideCars, err := backend.GetBlobSidecars(ctx, header.Hash()) + if err != nil { + log.Error("Error fetching Sidecars", "blockNrOrHash", blockNrOrHash, "err", err) + return APIGetBlobSidecarsResponse{}, err + } + sort.Ints(indices) + fullBlob := len(indices) == 0 + res := APIGetBlobSidecarsResponse{} + idx := 0 + curIdx := 0 + for _, sideCar := range sideCars { + for i := 0; i < len(sideCar.Blobs); i++ { + //hash := kZGToVersionedHash(sideCar.Commitments[i]) + if !fullBlob && curIdx >= len(indices) { + break + } + if fullBlob || idx == indices[curIdx] { + res.Data = append(res.Data, &BlobSidecar{ + Index: idx, + Blob: sideCar.Blobs[i], + KZGCommitment: sideCar.Commitments[i], + KZGProof: sideCar.Proofs[i], + }) + curIdx++ + } + idx++ + } + } + + return res, nil +} diff --git a/beacon/fakebeacon/handlers.go b/beacon/fakebeacon/handlers.go new file mode 100644 index 0000000000..3d3768aa42 --- /dev/null +++ b/beacon/fakebeacon/handlers.go @@ -0,0 +1,88 @@ +package fakebeacon + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/prysmaticlabs/prysm/v5/api/server/structs" + field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + "github.com/prysmaticlabs/prysm/v5/network/httputil" +) + +var ( + versionMethod = "/eth/v1/node/version" + specMethod = "/eth/v1/config/spec" + genesisMethod = "/eth/v1/beacon/genesis" + sidecarsMethodPrefix = "/eth/v1/beacon/blob_sidecars/{slot}" +) + +func VersionMethod(w http.ResponseWriter, r *http.Request) { + resp := &structs.GetVersionResponse{ + Data: &structs.Version{ + Version: "", + }, + } + httputil.WriteJson(w, resp) +} + +func SpecMethod(w http.ResponseWriter, r *http.Request) { + httputil.WriteJson(w, &structs.GetSpecResponse{Data: configSpec()}) +} + +func GenesisMethod(w http.ResponseWriter, r *http.Request) { + httputil.WriteJson(w, beaconGenesis()) +} + +func (s *Service) SidecarsMethod(w http.ResponseWriter, r *http.Request) { + indices, err := parseIndices(r.URL) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusBadRequest) + return + } + segments := strings.Split(r.URL.Path, "/") + slot, err := strconv.ParseUint(segments[len(segments)-1], 10, 64) + if err != nil { + httputil.HandleError(w, "not a valid slot(timestamp)", http.StatusBadRequest) + return + } + + resp, err := beaconBlobSidecars(r.Context(), s.backend, slot, indices) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusBadRequest) + return + } + httputil.WriteJson(w, resp) +} + +// parseIndices filters out invalid and duplicate blob indices +func parseIndices(url *url.URL) ([]int, error) { + rawIndices := url.Query()["indices"] + indices := make([]int, 0, field_params.MaxBlobsPerBlock) + invalidIndices := make([]string, 0) +loop: + for _, raw := range rawIndices { + ix, err := strconv.Atoi(raw) + if err != nil { + invalidIndices = append(invalidIndices, raw) + continue + } + if ix >= field_params.MaxBlobsPerBlock { + invalidIndices = append(invalidIndices, raw) + continue + } + for i := range indices { + if ix == indices[i] { + continue loop + } + } + indices = append(indices, ix) + } + + if len(invalidIndices) > 0 { + return nil, fmt.Errorf("requested blob indices %v are invalid", invalidIndices) + } + return indices, nil +} diff --git a/beacon/fakebeacon/server.go b/beacon/fakebeacon/server.go new file mode 100644 index 0000000000..91f48a2fbd --- /dev/null +++ b/beacon/fakebeacon/server.go @@ -0,0 +1,97 @@ +package fakebeacon + +import ( + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/gorilla/mux" + "github.com/prysmaticlabs/prysm/v5/api/server" +) + +const ( + DefaultAddr = "localhost" + DefaultPort = 8686 +) + +type Config struct { + Enable bool + Addr string + Port int +} + +func defaultConfig() *Config { + return &Config{ + Enable: false, + Addr: DefaultAddr, + Port: DefaultPort, + } +} + +type Service struct { + cfg *Config + router *mux.Router + backend ethapi.Backend +} + +func NewService(cfg *Config, backend ethapi.Backend) *Service { + cfgs := defaultConfig() + if cfg.Addr != "" { + cfgs.Addr = cfg.Addr + } + if cfg.Port > 0 { + cfgs.Port = cfg.Port + } + + s := &Service{ + cfg: cfgs, + backend: backend, + } + router := s.newRouter() + s.router = router + return s +} + +func (s *Service) Run() { + _ = http.ListenAndServe(s.cfg.Addr+":"+strconv.Itoa(s.cfg.Port), s.router) +} + +func (s *Service) newRouter() *mux.Router { + r := mux.NewRouter() + r.Use(server.NormalizeQueryValuesHandler) + for _, e := range s.endpoints() { + r.HandleFunc(e.path, e.handler).Methods(e.methods...) + } + return r +} + +type endpoint struct { + path string + handler http.HandlerFunc + methods []string +} + +func (s *Service) endpoints() []endpoint { + return []endpoint{ + { + path: versionMethod, + handler: VersionMethod, + methods: []string{http.MethodGet}, + }, + { + path: specMethod, + handler: SpecMethod, + methods: []string{http.MethodGet}, + }, + { + path: genesisMethod, + handler: GenesisMethod, + methods: []string{http.MethodGet}, + }, + { + path: sidecarsMethodPrefix, + handler: s.SidecarsMethod, + methods: []string{http.MethodGet}, + }, + } +} diff --git a/beacon/fakebeacon/server_test.go b/beacon/fakebeacon/server_test.go new file mode 100644 index 0000000000..0b74f565ba --- /dev/null +++ b/beacon/fakebeacon/server_test.go @@ -0,0 +1,90 @@ +package fakebeacon + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +// +//func TestFetchBlockNumberByTime(t *testing.T) { +// blockNum, err := fetchBlockNumberByTime(context.Background(), 1724052941, client) +// assert.Nil(t, err) +// assert.Equal(t, uint64(41493946), blockNum) +// +// blockNum, err = fetchBlockNumberByTime(context.Background(), 1734052941, client) +// assert.Equal(t, err, errors.New("time too large")) +// +// blockNum, err = fetchBlockNumberByTime(context.Background(), 1600153618, client) +// assert.Nil(t, err) +// assert.Equal(t, uint64(493946), blockNum) +//} +// +//func TestBeaconBlobSidecars(t *testing.T) { +// indexBlobHash := []IndexedBlobHash{ +// {Hash: common.HexToHash("0x01231952ecbaede62f8d0398b656072c072db36982c9ef106fbbc39ce14f983c"), Index: 0}, +// {Hash: common.HexToHash("0x012c21a8284d2d707bb5318e874d2e1b97a53d028e96abb702b284a2cbb0f79c"), Index: 1}, +// {Hash: common.HexToHash("0x011196c8d02536ede0382aa6e9fdba6c460169c0711b5f97fcd701bd8997aee3"), Index: 2}, +// {Hash: common.HexToHash("0x019c86b46b27401fb978fd175d1eb7dadf4976d6919501b0c5280d13a5bab57b"), Index: 3}, +// {Hash: common.HexToHash("0x01e00db7ee99176b3fd50aab45b4fae953292334bbf013707aac58c455d98596"), Index: 4}, +// {Hash: common.HexToHash("0x0117d23b68123d578a98b3e1aa029661e0abda821a98444c21992eb1e5b7208f"), Index: 5}, +// //{Hash: common.HexToHash("0x01e00db7ee99176b3fd50aab45b4fae953292334bbf013707aac58c455d98596"), Index: 1}, +// } +// +// resp, err := beaconBlobSidecars(context.Background(), 1724055046, []int{0, 1, 2, 3, 4, 5}) // block: 41494647 +// assert.Nil(t, err) +// assert.NotNil(t, resp) +// assert.NotEmpty(t, resp.Data) +// for i, sideCar := range resp.Data { +// assert.Equal(t, indexBlobHash[i].Index, sideCar.Index) +// assert.Equal(t, indexBlobHash[i].Hash, kZGToVersionedHash(sideCar.KZGCommitment)) +// } +// +// apiscs := make([]*BlobSidecar, 0, len(indexBlobHash)) +// // filter and order by hashes +// for _, h := range indexBlobHash { +// for _, apisc := range resp.Data { +// if h.Index == int(apisc.Index) { +// apiscs = append(apiscs, apisc) +// break +// } +// } +// } +// +// assert.Equal(t, len(apiscs), len(resp.Data)) +// assert.Equal(t, len(apiscs), len(indexBlobHash)) +//} + +type TimeToSlotFn func(timestamp uint64) (uint64, error) + +// GetTimeToSlotFn returns a function that converts a timestamp to a slot number. +func GetTimeToSlotFn(ctx context.Context) (TimeToSlotFn, error) { + genesis := beaconGenesis() + config := configSpec() + + genesisTime, _ := strconv.ParseUint(genesis.Data.GenesisTime, 10, 64) + secondsPerSlot, _ := strconv.ParseUint(config.SecondsPerSlot, 10, 64) + if secondsPerSlot == 0 { + return nil, fmt.Errorf("got bad value for seconds per slot: %v", config.SecondsPerSlot) + } + timeToSlotFn := func(timestamp uint64) (uint64, error) { + if timestamp < genesisTime { + return 0, fmt.Errorf("provided timestamp (%v) precedes genesis time (%v)", timestamp, genesisTime) + } + return (timestamp - genesisTime) / secondsPerSlot, nil + } + return timeToSlotFn, nil +} + +func TestAPI(t *testing.T) { + slotFn, err := GetTimeToSlotFn(context.Background()) + assert.Nil(t, err) + + expTx := uint64(123151345) + gotTx, err := slotFn(expTx) + assert.Nil(t, err) + assert.Equal(t, expTx, gotTx) +} diff --git a/beacon/fakebeacon/utils.go b/beacon/fakebeacon/utils.go new file mode 100644 index 0000000000..cc6fe889b9 --- /dev/null +++ b/beacon/fakebeacon/utils.go @@ -0,0 +1,65 @@ +package fakebeacon + +import ( + "context" + "errors" + "fmt" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rpc" +) + +func fetchBlockNumberByTime(ctx context.Context, ts int64, backend ethapi.Backend) (*types.Header, error) { + // calc the block number of the ts. + currentHeader := backend.CurrentHeader() + blockTime := int64(currentHeader.Time) + if ts > blockTime { + return nil, errors.New("time too large") + } + blockNum := currentHeader.Number.Uint64() + estimateEndNumber := int64(blockNum) - (blockTime-ts)/3 + // find the end number + for { + header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(estimateEndNumber)) + if err != nil { + time.Sleep(time.Duration(rand.Int()%180) * time.Millisecond) + continue + } + if header == nil { + estimateEndNumber -= 1 + time.Sleep(time.Duration(rand.Int()%180) * time.Millisecond) + continue + } + headerTime := int64(header.Time) + if headerTime == ts { + return header, nil + } + + // let the estimateEndNumber a little bigger than real value + if headerTime > ts+8 { + estimateEndNumber -= (headerTime - ts) / 3 + } else if headerTime < ts { + estimateEndNumber += (ts-headerTime)/3 + 1 + } else { + // search one by one + for headerTime >= ts { + header, err = backend.HeaderByNumber(ctx, rpc.BlockNumber(estimateEndNumber-1)) + if err != nil { + time.Sleep(time.Duration(rand.Int()%180) * time.Millisecond) + continue + } + headerTime = int64(header.Time) + if headerTime == ts { + return header, nil + } + estimateEndNumber -= 1 + if headerTime < ts { //found the real endNumber + return nil, fmt.Errorf("block not found by time %d", ts) + } + } + } + } +} diff --git a/cmd/faucet/customized/DIN.png b/cmd/faucet/customized/DIN.png new file mode 100644 index 0000000000..be36433308 Binary files /dev/null and b/cmd/faucet/customized/DIN.png differ diff --git a/cmd/faucet/customized/README.md b/cmd/faucet/customized/README.md new file mode 100644 index 0000000000..65a5a9b64c --- /dev/null +++ b/cmd/faucet/customized/README.md @@ -0,0 +1,23 @@ +# 1.Background +This is to support some projects with customized tokens that they want to integrate into the BSC faucet tool. + +## 1.1. How to Integrate Your Token +- Step 1: Fund the faucet address by sending a specific amount of your BEP-20 token to the faucet address (0xaa25aa7a19f9c426e07dee59b12f944f4d9f1dd3) on the BSC testnet. +- Step 2: Update this README.md file and create a Pull Request on [bsc github](https://github.com/bnb-chain/bsc) with relevant information. + +We will review the request, and once it is approved, the faucet tool will start to support the customized token and list it on https://www.bnbchain.org/en/testnet-faucet. + +# 2.Token List +## 2.1.DemoToken +- symbol: DEMO +- amount: 10000000000000000000 +- icon: ./demotoken.png +- addr: https://testnet.bscscan.com/address/0xe15c158d768c306dae87b96430a94f884333e55d +- fundTx: [0xa499dc9aaf918aff0507538a8aa80a88d0af6ca15054e6acc57b69c651945280](https://testnet.bscscan.com/tx/0x2a3f334b6ca756b64331bdec9e6cf3207ac50a4839fda6379e909de4d9a194ca) +- +## 2.2.DIN token +- symbol: DIN +- amount: 10000000000000000000 +- icon: ./DIN.png +- addr: https://testnet.bscscan.com/address/0xb8b40FcC5B4519Dba0E07Ac8821884CE90BdE677 +- fundTx: [0x17fc4c1db133830c7c146a0d41ca1df31cb446989ec11b382d58bb6176d6fde3](https://testnet.bscscan.com/tx/0x17fc4c1db133830c7c146a0d41ca1df31cb446989ec11b382d58bb6176d6fde3) diff --git a/cmd/faucet/customized/demotoken.png b/cmd/faucet/customized/demotoken.png new file mode 100644 index 0000000000..1eff3f32d8 Binary files /dev/null and b/cmd/faucet/customized/demotoken.png differ diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 25a0cc084a..918b60a586 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -53,9 +53,10 @@ import ( ) var ( - genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with") - apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection") - wsEndpoint = flag.String("ws", "http://127.0.0.1:7777/", "Url to ws endpoint") + genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with") + apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection") + wsEndpoint = flag.String("ws", "http://127.0.0.1:7777/", "Url to ws endpoint") + wsEndpointMainnet = flag.String("ws.mainnet", "", "Url to ws endpoint of BSC mainnet") netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet") payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request") @@ -77,6 +78,12 @@ var ( fixGasPrice = flag.Int64("faucet.fixedprice", 0, "Will use fixed gas price if specified") twitterTokenFlag = flag.String("twitter.token", "", "Bearer token to authenticate with the v2 Twitter API") twitterTokenV1Flag = flag.String("twitter.token.v1", "", "Bearer token to authenticate with the v1.1 Twitter API") + + resendInterval = 15 * time.Second + resendBatchSize = 3 + resendMaxGasPrice = big.NewInt(50 * params.GWei) + wsReadTimeout = 5 * time.Minute + minMainnetBalance = big.NewInt(2 * 1e6 * params.GWei) // 0.002 bnb ) var ( @@ -87,11 +94,17 @@ var ( //go:embed faucet.html var websiteTmpl string +func weiToEtherStringFx(wei *big.Int, prec int) string { + etherValue := new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(params.Ether)) + // Format the big.Float directly to a string with the specified precision + return etherValue.Text('f', prec) +} + func main() { // Parse the flags and set up the logger to print everything requested flag.Parse() - log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*logFlag), true))) - + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*logFlag), false))) + log.Info("faucet started") // Construct the payout tiers amounts := make([]string, *tiersFlag) for i := 0; i < *tiersFlag; i++ { @@ -170,7 +183,7 @@ func main() { log.Crit("Failed to unlock faucet signer account", "err", err) } // Assemble and start the faucet light service - faucet, err := newFaucet(genesis, *wsEndpoint, ks, website.Bytes(), bep2eInfos) + faucet, err := newFaucet(genesis, *wsEndpoint, *wsEndpointMainnet, ks, website.Bytes(), bep2eInfos) if err != nil { log.Crit("Failed to start faucet", "err", err) } @@ -197,9 +210,10 @@ type bep2eInfo struct { // faucet represents a crypto faucet backed by an Ethereum light client. type faucet struct { - config *params.ChainConfig // Chain configurations for signing - client *ethclient.Client // Client connection to the Ethereum chain - index []byte // Index page to serve up on the web + config *params.ChainConfig // Chain configurations for signing + client *ethclient.Client // Client connection to the Ethereum chain + clientMainnet *ethclient.Client // Client connection to BSC mainnet for balance check + index []byte // Index page to serve up on the web keystore *keystore.KeyStore // Keystore containing the single signer account accounts.Account // Account funding user faucet requests @@ -228,7 +242,7 @@ type wsConn struct { wlock sync.Mutex } -func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index []byte, bep2eInfos map[string]bep2eInfo) (*faucet, error) { +func newFaucet(genesis *core.Genesis, url string, mainnetUrl string, ks *keystore.KeyStore, index []byte, bep2eInfos map[string]bep2eInfo) (*faucet, error) { bep2eAbi, err := abi.JSON(strings.NewReader(bep2eAbiJson)) if err != nil { return nil, err @@ -237,6 +251,11 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [ if err != nil { return nil, err } + clientMainnet, err := ethclient.Dial(mainnetUrl) + if err != nil { + // skip mainnet balance check if it there is no available mainnet endpoint + log.Warn("dail mainnet endpoint failed", "mainnetUrl", mainnetUrl, "err", err) + } // Allow 1 request per minute with burst of 5, and cache up to 1000 IPs limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000) @@ -245,16 +264,17 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [ } return &faucet{ - config: genesis.Config, - client: client, - index: index, - keystore: ks, - account: ks.Accounts()[0], - timeouts: make(map[string]time.Time), - update: make(chan struct{}, 1), - bep2eInfos: bep2eInfos, - bep2eAbi: bep2eAbi, - limiter: limiter, + config: genesis.Config, + client: client, + clientMainnet: clientMainnet, + index: index, + keystore: ks, + account: ks.Accounts()[0], + timeouts: make(map[string]time.Time), + update: make(chan struct{}, 1), + bep2eInfos: bep2eInfos, + bep2eAbi: bep2eAbi, + limiter: limiter, }, nil } @@ -378,7 +398,11 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { Captcha string `json:"captcha"` Symbol string `json:"symbol"` } + // not sure if it helps or not, but set a read deadline could help prevent resource leakage + // if user did not give response for too long, then the routine will be stuck. + conn.SetReadDeadline(time.Now().Add(wsReadTimeout)) if err = conn.ReadJSON(&msg); err != nil { + log.Debug("read json message failed", "err", err, "ip", ip) return } if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") { @@ -396,9 +420,9 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { } continue } - log.Info("Faucet funds requested", "url", msg.URL, "tier", msg.Tier) + log.Info("Faucet funds requested", "url", msg.URL, "tier", msg.Tier, "ip", ip) - // If captcha verifications are enabled, make sure we're not dealing with a robot + // check #1: captcha verifications to exclude robot if *captchaToken != "" { form := url.Values{} form.Add("secret", *captchaSecret) @@ -475,88 +499,108 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { } continue } - log.Info("Faucet request valid", "url", msg.URL, "tier", msg.Tier, "user", username, "address", address) - // Ensure the user didn't request funds too recently + // check #2: check IP and ID(address) to ensure the user didn't request funds too frequently f.lock.Lock() - var ( - fund bool - timeout time.Time - ) if ipTimeout := f.timeouts[ips[len(ips)-2]]; time.Now().Before(ipTimeout) { + f.lock.Unlock() if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(ipTimeout)))); err != nil { // nolint: gosimple log.Warn("Failed to send funding error to client", "err", err) + return } + log.Info("too frequent funding(ip)", "TimeLeft", common.PrettyDuration(time.Until(ipTimeout)), "ip", ips[len(ips)-2], "ipsStr", ipsStr) + continue + } + if idTimeout := f.timeouts[id]; time.Now().Before(idTimeout) { f.lock.Unlock() + // Send an error if too frequent funding, otherwise a success + if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(idTimeout)))); err != nil { // nolint: gosimple + log.Warn("Failed to send funding error to client", "err", err) + return + } + log.Info("too frequent funding(id)", "TimeLeft", common.PrettyDuration(time.Until(idTimeout)), "id", id) continue } - - if timeout = f.timeouts[id]; time.Now().After(timeout) { - var tx *types.Transaction - if msg.Symbol == "BNB" { - // User wasn't funded recently, create the funding transaction - amount := new(big.Int).Div(new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(10)) - amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil)) - amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil)) - - tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil) + // check #3: minimum mainnet balance check, internal error will bypass the check to avoid blocking the faucet service + if f.clientMainnet != nil { + mainnetAddr := address + balanceMainnet, err := f.clientMainnet.BalanceAt(context.Background(), mainnetAddr, nil) + if err != nil { + log.Warn("check balance failed, call BalanceAt", "err", err) + } else if balanceMainnet == nil { + log.Warn("check balance failed, balanceMainnet is nil") } else { - tokenInfo, ok := f.bep2eInfos[msg.Symbol] - if !ok { - f.lock.Unlock() - log.Warn("Failed to find symbol", "symbol", msg.Symbol) - continue - } - input, err := f.bep2eAbi.Pack("transfer", address, &tokenInfo.Amount) - if err != nil { + if balanceMainnet.Cmp(minMainnetBalance) < 0 { f.lock.Unlock() - log.Warn("Failed to pack transfer transaction", "err", err) + log.Warn("insufficient BNB on BSC mainnet", "address", mainnetAddr, + "balanceMainnet", balanceMainnet, "minMainnetBalance", minMainnetBalance) + // Send an error if failed to meet the minimum balance requirement + if err = sendError(wsconn, fmt.Errorf("insufficient BNB on BSC mainnet (require >=%sBNB)", + weiToEtherStringFx(minMainnetBalance, 3))); err != nil { + log.Warn("Failed to send mainnet minimum balance error to client", "err", err) + return + } continue } - tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), tokenInfo.Contract, nil, 420000, f.price, input) } - signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID) - if err != nil { + } + log.Info("Faucet request valid", "url", msg.URL, "tier", msg.Tier, "user", username, "address", address, "ip", ip) + + // now, it is ok to send tBNB or other tokens + var tx *types.Transaction + if msg.Symbol == "BNB" { + // User wasn't funded recently, create the funding transaction + amount := new(big.Int).Div(new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(10)) + amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil)) + amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil)) + + tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil) + } else { + tokenInfo, ok := f.bep2eInfos[msg.Symbol] + if !ok { f.lock.Unlock() - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send transaction creation error to client", "err", err) - return - } + log.Warn("Failed to find symbol", "symbol", msg.Symbol) continue } - // Submit the transaction and mark as funded if successful - if err := f.client.SendTransaction(context.Background(), signed); err != nil { + input, err := f.bep2eAbi.Pack("transfer", address, &tokenInfo.Amount) + if err != nil { f.lock.Unlock() - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send transaction transmission error to client", "err", err) - return - } + log.Warn("Failed to pack transfer transaction", "err", err) continue } - f.reqs = append(f.reqs, &request{ - Avatar: avatar, - Account: address, - Time: time.Now(), - Tx: signed, - }) - timeout := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute - grace := timeout / 288 // 24h timeout => 5m grace - - f.timeouts[id] = time.Now().Add(timeout - grace) - f.timeouts[ips[len(ips)-2]] = time.Now().Add(timeout - grace) - fund = true + tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), tokenInfo.Contract, nil, 420000, f.price, input) } - f.lock.Unlock() - - // Send an error if too frequent funding, otherwise a success - if !fund { - if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple - log.Warn("Failed to send funding error to client", "err", err) + signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID) + if err != nil { + f.lock.Unlock() + if err = sendError(wsconn, err); err != nil { + log.Warn("Failed to send transaction creation error to client", "err", err) return } continue } + // Submit the transaction and mark as funded if successful + if err := f.client.SendTransaction(context.Background(), signed); err != nil { + f.lock.Unlock() + if err = sendError(wsconn, err); err != nil { + log.Warn("Failed to send transaction transmission error to client", "err", err) + return + } + continue + } + f.reqs = append(f.reqs, &request{ + Avatar: avatar, + Account: address, + Time: time.Now(), + Tx: signed, + }) + timeoutInt64 := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute + grace := timeoutInt64 / 288 // 24h timeout => 5m grace + + f.timeouts[id] = time.Now().Add(timeoutInt64 - grace) + f.timeouts[ips[len(ips)-2]] = time.Now().Add(timeoutInt64 - grace) + f.lock.Unlock() if err = sendSuccess(wsconn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil { log.Warn("Failed to send funding success to client", "err", err) return @@ -605,9 +649,52 @@ func (f *faucet) refresh(head *types.Header) error { f.lock.Lock() f.head, f.balance = head, balance f.price, f.nonce = price, nonce - if len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() > f.nonce { + if len(f.reqs) == 0 { + log.Debug("refresh len(f.reqs) == 0", "f.nonce", f.nonce) + f.lock.Unlock() + return nil + } + if f.reqs[0].Tx.Nonce() == f.nonce { + // if the next Tx failed to be included for a certain time(resendInterval), try to + // resend it with higher gasPrice, as it could be discarded in the network. + // Also resend extra following txs, as they could be discarded as well. + if time.Now().After(f.reqs[0].Time.Add(resendInterval)) { + for i, req := range f.reqs { + if i >= resendBatchSize { + break + } + prePrice := req.Tx.GasPrice() + // bump gas price 20% to replace the previous tx + newPrice := new(big.Int).Add(prePrice, new(big.Int).Div(prePrice, big.NewInt(5))) + if newPrice.Cmp(resendMaxGasPrice) >= 0 { + log.Info("resendMaxGasPrice reached", "newPrice", newPrice, "resendMaxGasPrice", resendMaxGasPrice, "nonce", req.Tx.Nonce()) + break + } + newTx := types.NewTransaction(req.Tx.Nonce(), *req.Tx.To(), req.Tx.Value(), req.Tx.Gas(), newPrice, req.Tx.Data()) + newSigned, err := f.keystore.SignTx(f.account, newTx, f.config.ChainID) + if err != nil { + log.Error("resend sign tx failed", "err", err) + } + log.Info("reqs[0] Tx has been stuck for a while, trigger resend", + "resendInterval", resendInterval, "resendTxSize", resendBatchSize, + "preHash", req.Tx.Hash().Hex(), "newHash", newSigned.Hash().Hex(), + "newPrice", newPrice, "nonce", req.Tx.Nonce(), "req.Tx.Gas()", req.Tx.Gas()) + if err := f.client.SendTransaction(context.Background(), newSigned); err != nil { + log.Warn("resend tx failed", "err", err) + continue + } + req.Tx = newSigned + } + } + } + // it is abnormal that reqs[0] has larger nonce than next expected nonce. + // could be caused by reorg? reset it + if f.reqs[0].Tx.Nonce() > f.nonce { + log.Warn("reset due to nonce gap", "f.nonce", f.nonce, "f.reqs[0].Tx.Nonce()", f.reqs[0].Tx.Nonce()) f.reqs = f.reqs[:0] } + // remove the reqs if they have smaller nonce, which means it is no longer valid, + // either has been accepted or replaced. for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce { f.reqs = f.reqs[1:] } diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 5c829a2f76..10d2224a14 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" + "github.com/ethereum/go-ethereum/beacon/fakebeacon" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -92,10 +93,11 @@ type ethstatsConfig struct { } type gethConfig struct { - Eth ethconfig.Config - Node node.Config - Ethstats ethstatsConfig - Metrics metrics.Config + Eth ethconfig.Config + Node node.Config + Ethstats ethstatsConfig + Metrics metrics.Config + FakeBeacon fakebeacon.Config } func loadConfig(file string, cfg *gethConfig) error { @@ -242,11 +244,22 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL) } + if ctx.IsSet(utils.FakeBeaconAddrFlag.Name) { + cfg.FakeBeacon.Addr = ctx.String(utils.FakeBeaconAddrFlag.Name) + } + if ctx.IsSet(utils.FakeBeaconPortFlag.Name) { + cfg.FakeBeacon.Port = ctx.Int(utils.FakeBeaconPortFlag.Name) + } + if cfg.FakeBeacon.Enable || ctx.IsSet(utils.FakeBeaconEnabledFlag.Name) { + go fakebeacon.NewService(&cfg.FakeBeacon, backend).Run() + } + git, _ := version.VCS() utils.SetupMetrics(ctx, utils.EnableBuildInfo(git.Commit, git.Date), utils.EnableMinerInfo(ctx, &cfg.Eth.Miner), utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()), + utils.EnableNodeTrack(ctx, &cfg.Eth, stack), ) return stack, backend } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 9b88d57025..042571779b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -233,6 +233,12 @@ var ( utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, } + + fakeBeaconFlags = []cli.Flag{ + utils.FakeBeaconEnabledFlag, + utils.FakeBeaconAddrFlag, + utils.FakeBeaconPortFlag, + } ) var app = flags.NewApp("the go-ethereum command line interface") @@ -287,6 +293,7 @@ func init() { consoleFlags, debug.Flags, metricsFlags, + fakeBeaconFlags, ) flags.AutoEnvVars(app.Flags, "GETH") @@ -444,22 +451,23 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon } // Start auxiliary services if enabled + ethBackend, ok := backend.(*eth.EthAPIBackend) + gasCeil := ethBackend.Miner().GasCeil() + if gasCeil > params.SystemTxsGas { + ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas) + } if ctx.Bool(utils.MiningEnabledFlag.Name) { // Mining only makes sense if a full Ethereum node is running if ctx.String(utils.SyncModeFlag.Name) == "light" { utils.Fatalf("Light clients do not support mining") } - ethBackend, ok := backend.(*eth.EthAPIBackend) + if !ok { utils.Fatalf("Ethereum service not running") } // Set the gas price to the limits from the CLI and start mining gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) ethBackend.TxPool().SetGasTip(gasprice) - gasCeil := ethBackend.Miner().GasCeil() - if gasCeil > params.SystemTxsGas { - ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas) - } if err := ethBackend.StartMining(); err != nil { utils.Fatalf("Failed to start mining: %v", err) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 049908857f..f26ce3881e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -35,8 +35,11 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/internal/version" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/beacon/fakebeacon" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/core" @@ -1146,6 +1149,25 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: params.DefaultExtraReserveForBlobRequests, Category: flags.MiscCategory, } + + // Fake beacon + FakeBeaconEnabledFlag = &cli.BoolFlag{ + Name: "fake-beacon", + Usage: "Enable the HTTP-RPC server of fake-beacon", + Category: flags.APICategory, + } + FakeBeaconAddrFlag = &cli.StringFlag{ + Name: "fake-beacon.addr", + Usage: "HTTP-RPC server listening addr of fake-beacon", + Value: fakebeacon.DefaultAddr, + Category: flags.APICategory, + } + FakeBeaconPortFlag = &cli.IntFlag{ + Name: "fake-beacon.port", + Usage: "HTTP-RPC server listening port of fake-beacon", + Value: fakebeacon.DefaultPort, + Category: flags.APICategory, + } ) var ( @@ -2297,6 +2319,67 @@ func EnableNodeInfo(poolConfig *legacypool.Config, nodeInfo *p2p.NodeInfo) Setup } } +func EnableNodeTrack(ctx *cli.Context, cfg *ethconfig.Config, stack *node.Node) SetupMetricsOption { + nodeInfo := stack.Server().NodeInfo() + return func() { + // register node info into metrics + metrics.NewRegisteredLabel("node-stats", nil).Mark(map[string]interface{}{ + "NodeType": parseNodeType(), + "ENR": nodeInfo.ENR, + "Mining": ctx.Bool(MiningEnabledFlag.Name), + "Etherbase": parseEtherbase(cfg), + "MiningFeatures": parseMiningFeatures(ctx, cfg), + "DBFeatures": parseDBFeatures(cfg, stack), + }) + } +} + +func parseEtherbase(cfg *ethconfig.Config) string { + if cfg.Miner.Etherbase == (common.Address{}) { + return "" + } + return cfg.Miner.Etherbase.String() +} + +func parseNodeType() string { + git, _ := version.VCS() + version := []string{params.VersionWithMeta} + if len(git.Commit) >= 7 { + version = append(version, git.Commit[:7]) + } + if git.Date != "" { + version = append(version, git.Date) + } + arch := []string{runtime.GOOS, runtime.GOARCH} + infos := []string{"BSC", strings.Join(version, "-"), strings.Join(arch, "-"), runtime.Version()} + return strings.Join(infos, "/") +} + +func parseDBFeatures(cfg *ethconfig.Config, stack *node.Node) string { + var features []string + if cfg.StateScheme == rawdb.PathScheme { + features = append(features, "PBSS") + } + if stack.CheckIfMultiDataBase() { + features = append(features, "MultiDB") + } + return strings.Join(features, "|") +} + +func parseMiningFeatures(ctx *cli.Context, cfg *ethconfig.Config) string { + if !ctx.Bool(MiningEnabledFlag.Name) { + return "" + } + var features []string + if cfg.Miner.Mev.Enabled { + features = append(features, "MEV") + } + if cfg.Miner.VoteEnable { + features = append(features, "FFVoting") + } + return strings.Join(features, "|") +} + func SetupMetrics(ctx *cli.Context, options ...SetupMetricsOption) { if metrics.Enabled { log.Info("Enabling metrics collection") diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 8ae7ac53a2..5b2626cd4a 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -68,7 +68,6 @@ const ( wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers initialBackOffTime = uint64(1) // second - processBackOffTime = uint64(1) // second systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system @@ -1616,12 +1615,15 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res copy(header.Extra[len(header.Extra)-extraSeal:], sig) if p.shouldWaitForCurrentBlockProcess(chain, header, snap) { - log.Info("Waiting for received in turn block to process") + highestVerifiedHeader := chain.GetHighestVerifiedHeader() + // including time for writing and committing blocks + waitProcessEstimate := math.Ceil(float64(highestVerifiedHeader.GasUsed) / float64(100_000_000)) + log.Info("Waiting for received in turn block to process", "waitProcessEstimate(Seconds)", waitProcessEstimate) select { case <-stop: log.Info("Received block process finished, abort block seal") return - case <-time.After(time.Duration(processBackOffTime) * time.Second): + case <-time.After(time.Duration(waitProcessEstimate) * time.Second): if chain.CurrentHeader().Number.Uint64() >= header.Number.Uint64() { log.Info("Process backoff time exhausted, and current header has updated to abort this seal") return diff --git a/core/block_validator.go b/core/block_validator.go index b82965a99d..bac3386c7f 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -66,31 +66,6 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin return validator } -// ValidateListsInBody validates that UncleHash, WithdrawalsHash, and WithdrawalsHash correspond to the lists in the block body, respectively. -func ValidateListsInBody(block *types.Block) error { - header := block.Header() - if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { - return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash) - } - if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { - return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) - } - // Withdrawals are present after the Shanghai fork. - if header.WithdrawalsHash != nil { - // Withdrawals list must be present in body after Shanghai. - if block.Withdrawals() == nil { - return errors.New("missing withdrawals in block body") - } - if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash { - return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash) - } - } else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars - // Withdrawals are not allowed prior to shanghai fork - return errors.New("withdrawals present in block body") - } - return nil -} - // ValidateBody validates the given block's uncles and verifies the block // header's transaction and uncle roots. The headers are assumed to be already // validated at this point. @@ -108,12 +83,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if err := v.engine.VerifyUncles(v.bc, block); err != nil { return err } + if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { + return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash) + } validateFuns := []func() error{ func() error { - return ValidateListsInBody(block) + if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { + return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) + } + return nil }, func() error { + // Withdrawals are present after the Shanghai fork. + if header.WithdrawalsHash != nil { + // Withdrawals list must be present in body after Shanghai. + if block.Withdrawals() == nil { + return errors.New("missing withdrawals in block body") + } + if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash { + return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash) + } + } else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars + // Withdrawals are not allowed prior to shanghai fork + return errors.New("withdrawals present in block body") + } // Blob transactions may be present after the Cancun fork. var blobs int for i, tx := range block.Transactions() { @@ -262,3 +256,8 @@ func CalcGasLimit(parentGasLimit, desiredLimit uint64) uint64 { } return limit } + +// CalcGasLimitForBuilder computes the gas limit of the next block. +func CalcGasLimitForBuilder(parentGasLimit, desiredLimit uint64) uint64 { + return CalcGasLimit(parentGasLimit, desiredLimit) / 2 +} diff --git a/core/blockchain.go b/core/blockchain.go index 3e09f95ef7..be26b39233 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -100,6 +100,8 @@ var ( blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) + blockRecvTimeDiffGauge = metrics.NewRegisteredGauge("chain/block/recvtimediff", nil) + errStateRootVerificationFailed = errors.New("state root verification failed") errInsertionInterrupted = errors.New("insertion is interrupted") errChainStopped = errors.New("blockchain is stopped") @@ -2055,6 +2057,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) return 0, nil } + if len(chain) > 0 { + blockRecvTimeDiffGauge.Update(time.Now().Unix() - int64(chain[0].Time())) + } // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) signer := types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()) go SenderCacher.RecoverFromBlocks(signer, chain) diff --git a/crypto/bn256/cloudflare/gfp12.go b/crypto/bn256/cloudflare/gfp12.go index 93fb368a7b..4e080f3ad3 100644 --- a/crypto/bn256/cloudflare/gfp12.go +++ b/crypto/bn256/cloudflare/gfp12.go @@ -1,7 +1,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( diff --git a/crypto/bn256/cloudflare/gfp2.go b/crypto/bn256/cloudflare/gfp2.go index 90a89e8b47..094fb1460e 100644 --- a/crypto/bn256/cloudflare/gfp2.go +++ b/crypto/bn256/cloudflare/gfp2.go @@ -1,7 +1,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. // gfP2 implements a field of size p² as a quadratic extension of the base field diff --git a/crypto/bn256/cloudflare/gfp6.go b/crypto/bn256/cloudflare/gfp6.go index a42734911c..72f552cd35 100644 --- a/crypto/bn256/cloudflare/gfp6.go +++ b/crypto/bn256/cloudflare/gfp6.go @@ -1,7 +1,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. // gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ diff --git a/crypto/bn256/google/gfp12.go b/crypto/bn256/google/gfp12.go index f084eddf21..39b407e80a 100644 --- a/crypto/bn256/google/gfp12.go +++ b/crypto/bn256/google/gfp12.go @@ -5,7 +5,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( diff --git a/crypto/bn256/google/gfp2.go b/crypto/bn256/google/gfp2.go index 3981f6cb4f..9cc854e3f6 100644 --- a/crypto/bn256/google/gfp2.go +++ b/crypto/bn256/google/gfp2.go @@ -5,7 +5,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( diff --git a/crypto/bn256/google/gfp6.go b/crypto/bn256/google/gfp6.go index 218856617c..3fe3d344ca 100644 --- a/crypto/bn256/google/gfp6.go +++ b/crypto/bn256/google/gfp6.go @@ -5,7 +5,7 @@ package bn256 // For details of the algorithms used, see "Multiplication and Squaring on -// Pairing-Friendly Fields, Devegili et al. +// Pairing-Friendly Fields", Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( diff --git a/eth/api_miner.go b/eth/api_miner.go index b8d571a475..56db9e94b1 100644 --- a/eth/api_miner.go +++ b/eth/api_miner.go @@ -73,7 +73,7 @@ func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { // SetGasLimit sets the gaslimit to target towards during mining. func (api *MinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool { api.e.Miner().SetGasCeil(uint64(gasLimit)) - if api.e.Miner().Mining() && uint64(gasLimit) > params.SystemTxsGas { + if uint64(gasLimit) > params.SystemTxsGas { api.e.TxPool().SetMaxGas(uint64(gasLimit) - params.SystemTxsGas) } return true diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 09925d7d66..8338fd9316 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -327,7 +327,7 @@ func (d *Downloader) UnregisterPeer(id string) error { // LegacySync tries to sync up our local blockchain with a remote peer, both // adding various sanity checks and wrapping it with various log entries. -func (d *Downloader) LegacySync(id string, head common.Hash, td *big.Int, ttd *big.Int, mode SyncMode) error { +func (d *Downloader) LegacySync(id string, head common.Hash, name string, td *big.Int, ttd *big.Int, mode SyncMode) error { err := d.synchronise(id, head, td, ttd, mode, false, nil) switch err { @@ -337,7 +337,7 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td *big.Int, ttd *b if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) || errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) || errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) { - log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err) + log.Warn("Synchronisation failed, dropping peer", "peer", id, "name", name, "td", td, "err", err) if d.dropPeer == nil { // The dropPeer method is nil when `--copydb` is used for a local copy. // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 3c113b9134..0a007644d2 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -902,7 +902,7 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { // Simulate a synchronisation and check the required result tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result } - tester.downloader.LegacySync(id, tester.chain.Genesis().Hash(), big.NewInt(1000), nil, FullSync) + tester.downloader.LegacySync(id, tester.chain.Genesis().Hash(), "", big.NewInt(1000), nil, FullSync) if _, ok := tester.peers[id]; !ok != tt.drop { t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop) } diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index fa5a324984..19f8c1ffb8 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -868,9 +868,9 @@ func (f *BlockFetcher) importHeaders(op *blockOrHeaderInject) { parent := f.getHeader(header.ParentHash) if parent == nil { log.Debug("Unknown parent of propagated header", "peer", peer, "number", header.Number, "hash", hash, "parent", header.ParentHash) - time.Sleep(reQueueBlockTimeout) // forget block first, then re-queue f.done <- hash + time.Sleep(reQueueBlockTimeout) f.requeue <- op return } @@ -909,9 +909,9 @@ func (f *BlockFetcher) importBlocks(op *blockOrHeaderInject) { parent := f.getBlock(block.ParentHash()) if parent == nil { log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash()) - time.Sleep(reQueueBlockTimeout) // forget block first, then re-queue f.done <- hash + time.Sleep(reQueueBlockTimeout) f.requeue <- op return } diff --git a/eth/handler.go b/eth/handler.go index 23dba9e14d..f26162024b 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -321,21 +321,14 @@ func newHandler(config *handlerConfig) (*handler, error) { broadcastBlockWithCheck := func(block *types.Block, propagate bool) { if propagate { - checkErrs := make(chan error, 2) - - go func() { - checkErrs <- core.ValidateListsInBody(block) - }() - go func() { - checkErrs <- core.IsDataAvailable(h.chain, block) - }() - - for i := 0; i < cap(checkErrs); i++ { - err := <-checkErrs - if err != nil { - log.Error("Propagating invalid block", "number", block.Number(), "hash", block.Hash(), "err", err) - return - } + if !(block.Header().WithdrawalsHash == nil && block.Withdrawals() == nil) && + !(block.Header().EmptyWithdrawalsHash() && block.Withdrawals() != nil && len(block.Withdrawals()) == 0) { + log.Error("Propagated block has invalid withdrawals") + return + } + if err := core.IsDataAvailable(h.chain, block); err != nil { + log.Error("Propagating block with invalid sidecars", "number", block.Number(), "hash", block.Hash(), "err", err) + return } } h.BroadcastBlock(block, propagate) @@ -483,13 +476,13 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { h.peersPerIP[remoteIP] = h.peersPerIP[remoteIP] + 1 h.peerPerIPLock.Unlock() } - peer.Log().Debug("Ethereum peer connected", "name", peer.Name()) // Register the peer locally if err := h.peers.registerPeer(peer, snap, trust, bsc); err != nil { peer.Log().Error("Ethereum peer registration failed", "err", err) return err } + peer.Log().Debug("Ethereum peer connected", "name", peer.Name(), "peers.len", h.peers.len()) defer h.unregisterPeer(peer.ID()) p := h.peers.peer(peer.ID()) @@ -632,7 +625,7 @@ func (h *handler) runBscExtension(peer *bsc.Peer, handler bsc.Handler) error { bsc.EgressRegistrationErrorMeter.Mark(1) } } - peer.Log().Error("Bsc extension registration failed", "err", err) + peer.Log().Error("Bsc extension registration failed", "err", err, "name", peer.Name()) return err } return handler(peer) diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index d1e07df25c..46bc97fbb8 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -46,7 +47,7 @@ var ProtocolVersions = []uint{ETH68} var protocolLengths = map[uint]uint64{ETH68: 17} // maxMessageSize is the maximum cap on the size of a protocol message. -const maxMessageSize = 10 * 1024 * 1024 +var maxMessageSize = params.MaxMessageSize const ( StatusMsg = 0x00 diff --git a/eth/sync.go b/eth/sync.go index 3b04d09920..db0ec62617 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -233,7 +233,7 @@ func (cs *chainSyncer) startSync(op *chainSyncOp) { // doSync synchronizes the local blockchain with a remote peer. func (h *handler) doSync(op *chainSyncOp) error { // Run the sync cycle, and disable snap sync if we're past the pivot block - err := h.downloader.LegacySync(op.peer.ID(), op.head, op.td, h.chain.Config().TerminalTotalDifficulty, op.mode) + err := h.downloader.LegacySync(op.peer.ID(), op.head, op.peer.Name(), op.td, h.chain.Config().TerminalTotalDifficulty, op.mode) if err != nil { return err } @@ -248,6 +248,9 @@ func (h *handler) doSync(op *chainSyncOp) error { // degenerate connectivity, but it should be healthy for the mainnet too to // more reliably update peers or the local TD state. if block := h.chain.GetBlock(head.Hash(), head.Number.Uint64()); block != nil { + if h.chain.Config().IsCancun(block.Number(), block.Time()) { + block = block.WithSidecars(h.chain.GetSidecarsByHash(block.Hash())) + } h.BroadcastBlock(block, false) } } diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index f96d3c1a8a..498f1ec2b2 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -692,6 +692,14 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { return } + // check bid size + if bidRuntime.env.size+blockReserveSize > params.MaxMessageSize { + log.Error("BidSimulator: failed to check bid size", "builder", bidRuntime.bid.Builder, + "bidHash", bidRuntime.bid.Hash(), "env.size", bidRuntime.env.size) + err = errors.New("invalid bid size") + return + } + bestBid := b.GetBestBid(parentHash) if bestBid == nil { log.Info("[BID RESULT]", "win", "true[first]", "builder", bidRuntime.bid.Builder, "hash", bidRuntime.bid.Hash().TerminalString()) @@ -858,6 +866,7 @@ func (r *BidRuntime) commitTransaction(chain *core.BlockChain, chainConfig *para } r.env.tcount++ + r.env.size += uint32(tx.Size()) return nil } diff --git a/miner/bidder.go b/miner/bidder.go index 4387079fee..8c8da93fe3 100644 --- a/miner/bidder.go +++ b/miner/bidder.go @@ -119,11 +119,7 @@ func (b *Bidder) mainLoop() { betterBidBefore = bidutil.BidBetterBefore(parentHeader, b.chain.Config().Parlia.Period, b.delayLeftOver, bidSimulationLeftOver) - if time.Now().After(betterBidBefore) { - timer.Reset(0) - } else { - timer.Reset(time.Until(betterBidBefore) / time.Duration(maxBid)) - } + timer.Reset(0) } if bidNum < maxBid && b.isBestWork(work) { // update the bestWork and do bid @@ -278,7 +274,8 @@ func (b *Bidder) bid(work *environment) { _, err := cli.SendBid(context.Background(), bidArgs) if err != nil { b.deleteBestWork(work) - log.Error("Bidder: bidding failed", "err", err, "number", work.header.Number, "txcount", len(work.txs), "unrevertible", len(work.UnRevertible)) + log.Error("Bidder: bidding failed", "err", err, "number", work.header.Number, "txcount", len(work.txs), + "unrevertible", len(work.UnRevertible), "packing_duration", work.duration.Milliseconds()) var bidErr rpc.Error ok := errors.As(err, &bidErr) @@ -290,7 +287,8 @@ func (b *Bidder) bid(work *environment) { } b.deleteBestWork(work) - log.Info("Bidder: bidding success", "number", work.header.Number, "txs", len(work.txs)) + log.Info("Bidder: bidding success", "number", work.header.Number, "txs", len(work.txs), + "packing_duration", work.duration.Milliseconds()) } // isBestWork returns the work is better than the current best work diff --git a/miner/miner.go b/miner/miner.go index a6474bb694..c15b06951d 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -69,7 +69,7 @@ type Config struct { // DefaultConfig contains default settings for miner. var DefaultConfig = Config{ - GasCeil: 30000000, + GasCeil: 0, GasPrice: big.NewInt(params.GWei), // The default recommit time is chosen as two seconds since @@ -358,7 +358,7 @@ func (miner *Miner) prepareSimulationEnv(parent *types.Header, state *state.Stat header := &types.Header{ ParentHash: parent.Hash(), Number: new(big.Int).Add(parent.Number, common.Big1), - GasLimit: core.CalcGasLimit(parent.GasLimit, miner.worker.config.GasCeil), + GasLimit: core.CalcGasLimitForBuilder(parent.GasLimit, miner.worker.config.GasCeil), Extra: miner.worker.extra, Time: uint64(timestamp), Coinbase: miner.worker.etherbase(), diff --git a/miner/worker.go b/miner/worker.go index ca95eae8de..7831f9f7ea 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -69,6 +69,12 @@ const ( // the default to wait for the mev miner to finish waitMEVMinerEndTimeLimit = 50 * time.Millisecond + + // Reserve block size for the following 3 components: + // a. System transactions at the end of the block + // b. Seal in the block header + // c. Overhead from RLP encoding + blockReserveSize = 100 * 1024 ) var ( @@ -88,6 +94,7 @@ type environment struct { signer types.Signer state *state.StateDB // apply state changes here tcount int // tx count in cycle + size uint32 // almost accurate block size, gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address @@ -99,6 +106,7 @@ type environment struct { profit *big.Int // block gas fee + BNBSentToSystem UnRevertible []common.Hash + duration time.Duration } // copy creates a deep copy of environment. @@ -107,6 +115,7 @@ func (env *environment) copy() *environment { signer: env.signer, state: env.state.Copy(), tcount: env.tcount, + size: env.size, coinbase: env.coinbase, header: types.CopyHeader(env.header), receipts: copyReceipts(env.receipts), @@ -921,6 +930,13 @@ LOOP: txs.Pop() continue } + // If we don't have enough size left for the next transaction, skip it. + if env.size+uint32(tx.Size())+blockReserveSize > params.MaxMessageSize { + log.Trace("Not enough size left for transaction", "hash", ltx.Hash, + "env.size", env.size, "needed", uint32(tx.Size())) + txs.Pop() + continue + } // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) @@ -946,6 +962,7 @@ LOOP: // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) env.tcount++ + env.size += uint32(tx.Size()) // size of BlobTxSidecar included txs.Shift() default: @@ -1016,7 +1033,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { header := &types.Header{ ParentHash: parent.Hash(), Number: new(big.Int).Add(parent.Number, common.Big1), - GasLimit: core.CalcGasLimit(parent.GasLimit, w.config.GasCeil), + GasLimit: core.CalcGasLimitForBuilder(parent.GasLimit, w.config.GasCeil), Time: timestamp, Coinbase: genParams.coinbase, } @@ -1033,7 +1050,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { header.BaseFee = eip1559.CalcBaseFee(w.chainConfig, parent) if w.chainConfig.Parlia == nil && !w.chainConfig.IsLondon(parent.Number) { parentGasLimit := parent.GasLimit * w.chainConfig.ElasticityMultiplier() - header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) + header.GasLimit = core.CalcGasLimitForBuilder(parentGasLimit, w.config.GasCeil) } } // Run the consensus preparation with the default or customized consensus engine. @@ -1081,6 +1098,9 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) } + + env.size = uint32(env.header.Size()) + return env, nil } @@ -1311,6 +1331,7 @@ LOOP: fillStart := time.Now() err = w.fillTransactionsAndBundles(interruptCh, work, stopTimer) fillDuration := time.Since(fillStart) + work.duration = fillDuration switch { case errors.Is(err, errBlockInterruptedByNewHead): // work.discard() diff --git a/miner/worker_builder.go b/miner/worker_builder.go index fdd41752c9..e8d0d7711f 100644 --- a/miner/worker_builder.go +++ b/miner/worker_builder.go @@ -20,6 +20,8 @@ import ( "github.com/ethereum/go-ethereum/params" ) +const smallBundleGas = 10 * params.TxGas + var ( errNonRevertingTxInBundleFailed = errors.New("non-reverting tx in bundle failed") errBundlePriceTooLow = errors.New("bundle price too low") @@ -349,6 +351,11 @@ func (w *worker) mergeBundles( } for _, bundle := range bundles { + // if we don't have enough gas for any further transactions then we're done + if gasPool.Gas() < smallBundleGas { + break + } + prevState := currentState.Copy() prevGasPool := new(core.GasPool).AddGas(gasPool.Gas()) diff --git a/params/protocol_params.go b/params/protocol_params.go index 65b2d942c1..a032f2759e 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -29,6 +29,8 @@ const ( GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. PayBidTxGasLimit uint64 = 25000 // Gas limit of the PayBidTx in the types.BidArgs. + MaxMessageSize uint32 = 10 * 1024 * 1024 // MaxMessageSize is the maximum cap on the size of a eth protocol message. + MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. ForkIDSize uint64 = 4 // The length of fork id ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. diff --git a/params/version.go b/params/version.go index 4327f17d11..4fc05b1ed8 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 = 4 // Minor version component of the current release - VersionPatch = 14 // Patch version component of the current release + VersionPatch = 15 // Patch version component of the current release VersionMeta = "" // Version metadata to append to the version string )