diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 000000000000..0b6e802ff046 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,186 @@ +name: Pre Release + +on: + push: + # Publish `pre-v1.2.3` tags as releases. + tags: + - 'pre-*' + +env: + CGO_CFLAGS: "-O -D__BLST_PORTABLE__" + CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__" + +jobs: + build: + name: Build Release + strategy: + matrix: + go-version: [1.21.x] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + + - uses: actions/cache@v3 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + # * Build cache (Mac) + # * Build cache (Windows) + path: | + ~/go/pkg/mod + ~/.cache/go-build + ~/Library/Caches/go-build + ~\AppData\Local\go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + # ============================== + # Linux/Macos/Windows Build + # ============================== + + - name: Build Binary for ${{matrix.os}} + if: matrix.os == 'ubuntu-latest' + run: | + go mod download + wget https://musl.cc/x86_64-linux-musl-cross.tgz + tar -xvf ./x86_64-linux-musl-cross.tgz + GIT_COMMIT=$(git rev-parse HEAD) + GIT_COMMIT_DATE=$(git log -n1 --pretty='format:%cd' --date=format:'%Y%m%d') + GOOS=linux GOARCH=amd64 CGO_ENABLED=1 CC=$(pwd)/x86_64-linux-musl-cross/bin/x86_64-linux-musl-gcc go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.gitDate=$GIT_COMMIT_DATE -extldflags=-static" -o ./build/bin/geth -a ./cmd/geth + + - name: Build Binary for ${{matrix.os}} + if: matrix.os != 'ubuntu-latest' + run: | + go mod download + make geth + + # ============================== + # Cross Compile for ARM + # ============================== + + - name: Build Binary for ARM + if: matrix.os == 'ubuntu-latest' + run: | + go mod download + wget https://musl.cc/aarch64-linux-musl-cross.tgz + tar -xvf ./aarch64-linux-musl-cross.tgz + GIT_COMMIT=$(git rev-parse HEAD) + GIT_COMMIT_DATE=$(git log -n1 --pretty='format:%cd' --date=format:'%Y%m%d') + GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=$(pwd)/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.gitDate=$GIT_COMMIT_DATE -extldflags=-static" -o ./build/bin/geth-linux-arm64 -a ./cmd/geth + + # ============================== + # Upload artifacts + # ============================== + + - name: Upload Linux Build + 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@v4.3.3 + if: matrix.os == 'macos-latest' + with: + name: macos + path: ./build/bin/geth + + - name: Upload Windows Build + 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@v4.3.3 + if: matrix.os == 'ubuntu-latest' + with: + name: arm64 + path: ./build/bin/geth-linux-arm64 + + release: + name: Release + needs: build + runs-on: ubuntu-latest + steps: + - name: Set Env + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Checkout Code + uses: actions/checkout@v3 + + # ============================== + # Download artifacts + # ============================== + + - name: Download Artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: linux + path: ./linux + + - name: Download Artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: macos + path: ./macos + + - name: Download Artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: windows + path: ./windows + + - name: Download Artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: arm64 + path: ./arm64 + + - name: Download Config File + run: | + . ./.github/release.env + echo "mainnet.zip url: $MAINNET_FILE_URL" + echo "testnet.zip url: $TESTNET_FILE_URL" + curl -L $MAINNET_FILE_URL -o ./mainnet.zip + curl -L $TESTNET_FILE_URL -o ./testnet.zip + # ============================== + # Create release + # ============================== + + # Rename assets + - run: | + mv ./linux/geth ./linux/geth_linux + mv ./macos/geth ./macos/geth_macos + mv ./windows/geth.exe ./windows/geth_windows.exe + + - name: Create Release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ env.RELEASE_VERSION}} + release_name: ${{ env.RELEASE_VERSION}} + body: | + versing: ${{ env.RELEASE_VERSION}} + git commit: ${{ github.sha }} + draft: true + prerelease: true + files: | + ./mainnet.zip + ./testnet.zip + ./linux/geth_linux + ./macos/geth_macos + ./windows/geth_windows.exe + ./arm64/geth-linux-arm64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a94e6eb99f5a..d76b9fb7842c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,29 +65,29 @@ jobs: # CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.gitDate=$GIT_COMMIT_DATE -extldflags=-static" -o ./build/bin/geth-linux-arm64 -a ./cmd/geth - name: Upload Linux Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.3 if: matrix.os == 'ubuntu-20.04' 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 - # if: matrix.os == 'ubuntu-20.04' + # uses: actions/upload-artifact@v4.3.3 + # if: matrix.os == 'ubuntu-latest' # with: # name: arm64 # path: ./build/bin/geth-linux-arm64 @@ -104,29 +104,39 @@ jobs: uses: actions/checkout@v3 - 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 + # - name: Download Config File + # run: | + # . ./.github/release.env + # echo "mainnet.zip url: $MAINNET_FILE_URL" + # echo "testnet.zip url: $TESTNET_FILE_URL" + # curl -L $MAINNET_FILE_URL -o ./mainnet.zip + # curl -L $TESTNET_FILE_URL -o ./testnet.zip + # ============================== + # Create release + # ============================== - name: Generate Change Log id: changelog run: | @@ -146,13 +156,12 @@ jobs: - run: ls - name: Create Release - id: create_release uses: softprops/action-gh-release@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token with: - tag_name: ${{ env.RELEASE_VERSION }} - release_name: ${{ env.RELEASE_VERSION }} + tag_name: ${{ env.RELEASE_VERSION}} + release_name: ${{ env.RELEASE_VERSION}} body: | ${{ env.CHANGELOG }} draft: false @@ -160,5 +169,8 @@ jobs: files: | ./linux/geth_linux ./macos/geth_macos - # ./arm64/geth-linux-arm64 + + # ./mainnet.zip + # ./testnet.zip # ./windows/geth_windows.exe + # ./arm64/geth-linux-arm64 diff --git a/.nancy-ignore b/.nancy-ignore index 0b64e763db15..6d058384b6b9 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 6570bb5f6ad5..6637ab484c99 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 30a36697c6d2..252813ba0539 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/README.md b/README.md index a026d903a005..4f042ab4c49d 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,15 @@ https://pkg.go.dev/badge/github.com/ethereum/go-ethereum )](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc) [![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/z2VpC455eU) -But from that baseline of EVM compatible, BNB Smart Chain introduces a system of 21 validators with Proof of Staked Authority (PoSA) consensus that can support short block time and lower fees. The most bonded validator candidates of staking will become validators and produce blocks. The double-sign detection and other slashing logic guarantee security, stability, and chain finality. +But from that baseline of EVM compatible, BNB Smart Chain introduces a system of 21 validators with Proof of Staked Authority (PoSA) consensus that can support short block time and lower fees. The most bonded validator candidates of staking will become validators and produce blocks. The double-sign detection and other slashing logic guarantee security, stability, and chain finality. -Cross-chain transfer and other communication are possible due to native support of interoperability. Relayers and on-chain contracts are developed to support that. BNB Beacon Chain DEX remains a liquid venue of the exchange of assets on both chains. This dual-chain architecture will be ideal for users to take advantage of the fast trading on one side and build their decentralized apps on the other side. **The BNB Smart Chain** will be: +**The BNB Smart Chain** will be: - **A self-sovereign blockchain**: Provides security and safety with elected validators. - **EVM-compatible**: Supports all the existing Ethereum tooling along with faster finality and cheaper transaction fees. -- **Interoperable**: Comes with efficient native dual chain communication; Optimized for scaling high-performance dApps that require fast and smooth user experience. - **Distributed with on-chain governance**: Proof of Staked Authority brings in decentralization and community participants. As the native token, BNB will serve as both the gas of smart contract execution and tokens for staking. -More details in [White Paper](https://www.bnbchain.org/en#smartChain). +More details in [White Paper](https://github.com/bnb-chain/whitepaper/blob/master/WHITEPAPER.md). ## Key features @@ -36,18 +35,8 @@ To combine DPoS and PoA for consensus, BNB Smart Chain implement a novel consens 1. Blocks are produced by a limited set of validators. 2. Validators take turns to produce blocks in a PoA manner, similar to Ethereum's Clique consensus engine. -3. Validator set are elected in and out based on a staking based governance on BNB Beacon Chain. -4. The validator set change is relayed via a cross-chain communication mechanism. -5. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/bnb-smart-chain/staking/overview/#system-contracts) to achieve liveness slash, revenue distributing and validator set renewing func. - - -### Light Client of BNB Beacon Chain - -To achieve the cross-chain communication from BNB Beacon Chain to BNB Smart Chain, need introduce a on-chain light client verification algorithm. -It contains two parts: - -1. [Stateless Precompiled contracts](https://github.com/bnb-chain/bsc/blob/master/core/vm/contracts_lightclient.go) to do tendermint header verification and Merkle Proof verification. -2. [Stateful solidity contracts](https://github.com/bnb-chain/bsc-genesis-contract/blob/master/contracts/TendermintLightClient.sol) to store validator set and trusted appHash. +3. Validator set are elected in and out based on a staking based governance on BNB Smart Chain. +4. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/bnb-smart-chain/staking/overview/#system-contracts) to achieve liveness slash, revenue distributing and validator set renewing func. ## Native Token @@ -55,7 +44,6 @@ BNB will run on BNB Smart Chain in the same way as ETH runs on Ethereum so that BNB will be used to: 1. pay `gas` to deploy or invoke Smart Contract on BSC -2. perform cross-chain operations, such as transfer token assets across BNB Smart Chain and BNB Beacon Chain. ## Building the source @@ -82,7 +70,7 @@ Caught SIGILL in blst_cgo_init, consult /bindinds/go/README.md. ``` please try to add the following environment variables and build again: ```shell -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" +export CGO_CFLAGS="-O -D__BLST_PORTABLE__" export CGO_CFLAGS_ALLOW="-O -D__BLST_PORTABLE__" ``` @@ -159,7 +147,7 @@ Download latest chaindata snapshot from [here](https://github.com/bnb-chain/bsc- ## It will run with Hash-Base Storage Scheme by default ./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0 --tries-verify-mode none -## It runs fullnode with Path-Base Storage Scheme. +## It runs fullnode with Path-Base Storage Scheme. ## It will enable inline state prune, keeping the latest 90000 blocks' history state by default. ./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0 --tries-verify-mode none --state.scheme path ``` @@ -249,13 +237,11 @@ running web servers, so malicious web pages could try to subvert locally availab APIs!** ### Operating a private network -- [BSC-Deploy](https://github.com/bnb-chain/node-deploy/): deploy tool for setting up both BNB Beacon Chain, BNB Smart Chain and the cross chain infrastructure between them. -- [BSC-Docker](https://github.com/bnb-chain/bsc-docker): deploy tool for setting up local BSC cluster in container. - +- [BSC-Deploy](https://github.com/bnb-chain/node-deploy/): deploy tool for setting up BNB Smart Chain. ## Running a bootnode -Bootnodes are super-lightweight nodes that are not behind a NAT and are running just discovery protocol. When you start up a node it should log your enode, which is a public identifier that others can use to connect to your node. +Bootnodes are super-lightweight nodes that are not behind a NAT and are running just discovery protocol. When you start up a node it should log your enode, which is a public identifier that others can use to connect to your node. First the bootnode requires a key, which can be created with the following command, which will save a key to boot.key: @@ -269,7 +255,7 @@ This key can then be used to generate a bootnode as follows: bootnode -nodekey boot.key -addr :30311 -network bsc ``` -The choice of port passed to -addr is arbitrary. +The choice of port passed to -addr is arbitrary. The bootnode command returns the following logs to the terminal, confirming that it is running: ``` diff --git a/beacon/fakebeacon/api_func.go b/beacon/fakebeacon/api_func.go new file mode 100644 index 000000000000..674bf7fb3985 --- /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 000000000000..3d3768aa42b3 --- /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 000000000000..91f48a2fbdd3 --- /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 000000000000..0b74f565ba31 --- /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 000000000000..cc6fe889b9f0 --- /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 000000000000..be3643330869 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 000000000000..65a5a9b64c93 --- /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 000000000000..1eff3f32d8a8 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 25a0cc084a03..918b60a58693 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 5c829a2f7677..10d2224a1459 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 546eb1d2e237..c7340027b8f8 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") @@ -288,6 +294,7 @@ func init() { debug.Flags, debug.FirehoseFlags, metricsFlags, + fakeBeaconFlags, ) flags.AutoEnvVars(app.Flags, "GETH") @@ -451,22 +458,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 049908857ffb..f26ce3881e20 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 97c76b9da416..8b82d8d376e0 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -69,7 +69,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 @@ -1617,12 +1616,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 b82965a99de3..d15e2cd786dc 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() { diff --git a/core/blockchain.go b/core/blockchain.go index 98e22cb5bb3c..0e912115f167 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -103,6 +103,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") @@ -2123,6 +2125,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 93fb368a7bf0..4e080f3ad3d3 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 90a89e8b47c6..094fb1460efc 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 a42734911c64..72f552cd3549 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 f084eddf2126..39b407e80abf 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 3981f6cb4f6d..9cc854e3f6c4 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 218856617c1d..3fe3d344ca1f 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 b8d571a4750b..56db9e94b103 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 09925d7d6675..8338fd93168e 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 3c113b91345d..0a007644d2a7 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 fa5a324984b5..19f8c1ffb802 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 23dba9e14de0..f26162024bcf 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 d1e07df25c93..46bc97fbb8ee 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 c55bd447f3b0..99dce6696789 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -246,7 +246,7 @@ 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 } @@ -261,6 +261,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 006e4bcc95a2..c67e7bb9e606 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -693,6 +693,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()) @@ -859,6 +867,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/miner.go b/miner/miner.go index aaef07932d83..ebf53199e998 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -62,7 +62,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 diff --git a/miner/worker.go b/miner/worker.go index 252ec76bf048..daf157e3b13f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -71,6 +71,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 ( @@ -90,6 +96,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 @@ -106,6 +113,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), @@ -896,6 +904,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) @@ -921,6 +936,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: @@ -1056,6 +1072,9 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}, firehose.NoOpContext) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state, firehose.NoOpContext) } + + env.size = uint32(env.header.Size()) + return env, nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index 65b2d942c1eb..a032f2759e04 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 31d666b01a99..fb2019fb569c 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 = "fh2.5" // Version metadata to append to the version string FirehoseVersionMajor = 2