Compare commits

..

68 Commits

Author SHA1 Message Date
Ethan
19e8487315 feat: update system contract bytecodes of pascal hardfork 2024-10-09 19:18:13 +08:00
Vitor Nazário Coelho
55896bf610 minor Update group_prover.sage (#2716) 2024-10-08 15:49:41 +08:00
buddho
bff9d252d6 core: clearup testflag for Bohr (#2727) 2024-09-29 13:30:45 +08:00
buddho
27f67a5210 feat: add Prague hardfork (#2718) 2024-09-23 17:53:32 +08:00
Ethan
8c1acb0b22 feat: add pascal hardfork (#2692)
* feat: add pascal hardfork
* config: fix default value for PascalTime and add OverridePascal (#2693)
* chores: rename some contract name
* feat: update bytecode on dev env
2024-09-23 16:17:06 +08:00
zzzckck
089064c1ff Merge pull request #2683 from buddh0/remove_duplicate_list_check
eth/handler: remove duplicate check for lists in body
2024-09-19 10:13:27 +08:00
buddh0
5289ecdfe2 eth/protocols: add Withdrawals check before broadcastBlock 2024-09-18 17:12:43 +08:00
buddh0
d141ff06c3 Revert "eth/handler: check lists in body before broadcast blocks (#2461)"
This reverts commit 0c0958ff87.
2024-09-18 17:11:37 +08:00
zzzckck
9cbac84363 doc: update readme to remove Beacon chain part (#2697)
others changes:
  - remove bsc-docker, as it is broken
  - remove light client, as it is no longer supported
2024-09-18 16:56:10 +08:00
zzzckck
21faa2de3f release: prepare for release v1.4.15 (#2700) 2024-09-18 14:34:01 +08:00
WMQ
34059cb144 faucet: update DIN token faucet support (#2706) 2024-09-18 11:51:59 +08:00
tiaoxizhan
3e44dcaa55 chore: add missing symbols in comment (#2704) 2024-09-14 09:12:56 +08:00
zzzckck
282aee5856 faucet: add example for custimized token (#2698) 2024-09-12 15:48:11 +08:00
zzzckck
44e91bba23 faucet: support customized token (#2687) 2024-09-12 15:10:37 +08:00
zzzckck
774d1b7ddb Merge pull request #2684 from ngotchac/ngotchac/requeue-fixes 2024-09-11 10:58:06 +08:00
buddho
8bbd8fbf48 consensus/parlia: wait more time when processing huge blocks (#2689) 2024-09-11 10:14:11 +08:00
buddho
a28262b3ec miner: limit block size to eth protocol msg size (#2696) 2024-09-10 16:24:29 +08:00
buddho
7de27ca9e9 CI: nancy ignore CVE-2024-8421 (#2695) 2024-09-10 11:27:04 +08:00
zzzckck
e7e5d508b5 txpool: set default GasCeil from 30M to 0 (#2688)
This PR tries to improve https://github.com/bnb-chain/bsc/pull/2680
If the node did not set `Eth.Miner` in config.toml, the default GasCeil
will be 30000000, it is not an accurate number for both BSC mainnet and testnet.
Set it to 0 means disable the transaction GasLimit check, i.e. the TxPool will
accept transactions with any GasLimit size.
2024-09-05 22:01:24 +08:00
Nicolas Gotchac
03069a7703 fetcher: Sleep after marking block as done when requeuing
Otherwise the node will be waiting for 500ms before the block fetcher
keeps processing.
2024-09-04 11:31:12 +02:00
galaio
1bcdad851f metrics: add some extra feature flags as node stats; (#2662) 2024-09-04 16:37:43 +08:00
Nicolas Gotchac
24a46de5b2 eth: Add sidecars when available to broadcasted current block (#2675) 2024-09-04 16:22:07 +08:00
zzzckck
5c4096fffa faucet: with mainnet balance check, 0.002BNB at least (#2672) 2024-09-04 16:19:01 +08:00
dependabot[bot]
f85d19aa8f build(deps): bump actions/download-artifact in /.github/workflows (#2679)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7.
2024-09-04 16:12:07 +08:00
zzzckck
0dab664d98 txpool: apply miner's gasceil to txpool (#2680)
apply `Eth.Miner.GasCeil` to TxPool even the node did not enable `--mine` option
Otherwise, there could be accmulated pending transactions in txpool.
Take an example:
- Currently, BSC testnet's block gaslimit is 70M, as 20M gas is reserved for
  SystemTxs, the maximum acceptable GasLimit for transaction is 50M.
- TxPool recevied a transaction with GasLimit 60M, it would be accepted in TxPool but it
  will never be accepted by validators. Such kinds of transactions will be kept in txpool
  and gradually make the txpool overflowed
2024-09-04 16:02:27 +08:00
Dike.w
094519d058 beaconserver: simulated beacon api server for op-stack (#2678)
only some necessary apis are implemented.
2024-09-04 09:39:01 +08:00
zzzckck
d3450f13c9 log: add some p2p log (#2677) 2024-09-03 14:27:53 +08:00
zzzckck
75af65dbf2 Merge pull request #2669 from bnb-chain/develop
Draft release v1.4.14
2024-08-27 15:00:23 +08:00
zzzckck
959850218c release: prepare for release v1.4.14 (#2668) 2024-08-27 11:23:04 +08:00
zzzckck
af0204bd68 faucet: bump and resend faucet transaction if it has been pending for a while (#2665) 2024-08-26 16:09:34 +08:00
zzzckck
ec2d7e0228 config: setup Mainnet 2 hardfork date: HaberFix & Bohr (#2661)
expected hard fork date:
    Mainnet-HaberFix: 2024-09-26 02:02:00 AM UTC
    Mainnet-Bohr:     2024-09-26 02:20:00 AM UTC
2024-08-23 13:59:56 +08:00
Dike.w
c46d7e8bd8 ethclient: fix BlobSidecars api (#2656) 2024-08-22 11:07:05 +08:00
Chris Li
6cb4be4ebf fix: when dataset is pruned and pruneancient is set, the offset is error (#2657) 2024-08-21 15:37:42 +08:00
buddho
3bd9a2395c core: improve readability of the fork choice logic (#2658) 2024-08-20 23:06:49 +08:00
buddho
5d19f2182b internal/ethapi: make GetFinalizedHeader monotonically increasing (#2655) 2024-08-20 11:51:32 +08:00
buddho
99a2dd5ed9 internal/debug: remove memsize (#2649) 2024-08-15 11:38:14 +08:00
easyfold
3adcfabb41 core/systemcontracts: use vm.StateDB in UpgradeBuildInSystemContract (#2578) 2024-08-13 17:06:07 +08:00
buddho
b1f0a3c79b core: fix cache for receipts (#2643) 2024-08-13 14:02:38 +08:00
zzzckck
26a4d4fda6 Merge pull request #2633 from bnb-chain/develop
Draft release v1.4.13
2024-08-08 16:38:42 +08:00
buddho
e988d1574e consensus/parlia: support recovery when snapshot of parlia gone in disk (#2587) 2024-08-08 14:53:51 +08:00
zzzckck
2cce9dd3de release: prepare for release v1.4.13 (#2632) 2024-08-07 17:53:24 +08:00
zzzckck
b7e678e93d config: setup Testnet Bohr hardfork date (#2634)
expected Bohr hard fork date:
- Testnet: 2024-08-20 01:23:16 AM UTC
- Mainnet: it is not determined yet, target Later Sep 2024
2024-08-07 16:24:28 +08:00
zzzckck
b61128bd7b utils: add GetTopAddr to analyse large traffic (#2629) 2024-08-05 16:48:33 +08:00
buddho
df16ab95ab Makefile: use docker compose v2 instead of v1 (#2628) 2024-08-05 15:15:46 +08:00
Nathan
9e343669b5 core: not record zero hash beacon block root with Parlia engine (#2621) 2024-07-31 16:39:24 +08:00
Nathan
987b8c1504 consensus/parlia: exclude inturn validator when calculate backoffTime (#2618) 2024-07-31 16:37:50 +08:00
Nathan
7d907016ff tests: fix evm-test CI (#2622) 2024-07-31 15:50:43 +08:00
Satyajit Das
00cac12542 faucet: rate limit initial implementation (#2603) 2024-07-29 13:36:42 +08:00
Ethan
27f618f434 fix: update stakehub bytecode after zero address agent issue fixed (#2614) 2024-07-26 15:47:59 +08:00
irrun
46b88d11f9 fix: only take non-mempool tx to calculate bid price (#2579) 2024-07-26 09:49:16 +08:00
yuhangcangqian
f532da6ca0 chore: fix some comments (#2597)
Signed-off-by: yuhangcangqian <cuibuwei@qq.com>
2024-07-25 10:46:52 +08:00
zzzckck
c94fc290e7 Merge pull request #2612 from buddh0/merge_master_into_develop
Merge master into develop
2024-07-25 10:44:24 +08:00
buddho
7f3c5ce4cd cmd/utils: add new flag OverridePassedForkTime (#2611) 2024-07-25 10:43:32 +08:00
Nathan
313449404f consensus/parlia: modify mining time for last block in one turn (#2608) 2024-07-25 10:42:42 +08:00
buddh0
99e4e950f8 Merge branch 'master' into develop 2024-07-25 10:20:23 +08:00
zzzckck
83a9b13771 Merge pull request #2607 from NathanBSC/for_release_v1.4.12
Revert "miner/worker: broadcast block immediately once sealed (bnb-chain#2576)"
2024-07-24 12:07:13 +08:00
Ethan
cabd0f8a21 feat: add bohr upgrade contracts bytecode (#2605)
* feat: add bohr upgrade contracts bytecode

* feat: add bohr upgrade contracts bytecode on rialto network

* feat: add protector for stakehub on rialto network
2024-07-24 10:29:01 +08:00
Nathan
17e0e45a09 BEP-341: Validators can produce consecutive blocks (#2482) 2024-07-23 13:55:45 +08:00
Chris Li
6260a26971 fix: prune-state when specify --triesInMemory 32 (#2602) 2024-07-23 10:06:19 +08:00
Nathan
a44b6d8067 core: cache block after wroten into db (#2600) 2024-07-22 20:52:07 +08:00
NathanBSC
c6cb43b7ca Revert "miner/worker: broadcast block immediately once sealed (#2576)"
This reverts commit 6d5b4ad64d.
2024-07-22 18:23:10 +08:00
NathanBSC
222e10810e core: cache block after wroten into db 2024-07-22 18:22:56 +08:00
zzzckck
26b236fb5f Merge pull request #2586 from bnb-chain/master_2_develop
Draft release v1.4.12
2024-07-17 17:45:39 +08:00
zzzckck
900cf26c65 Merge branch 'master' into master_2_develop 2024-07-17 17:29:11 +08:00
dependabot[bot]
75d162983a build(deps): bump github.com/hashicorp/go-retryablehttp (#2537)
Bumps [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp) from 0.7.4 to 0.7.7.
- [Changelog](https://github.com/hashicorp/go-retryablehttp/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/go-retryablehttp/compare/v0.7.4...v0.7.7)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-retryablehttp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-28 13:51:53 +08:00
zoro
719412551a Merge pull request #2549 from bnb-chain/develop
release: prepare for v1.4.11
2024-06-27 17:37:26 +08:00
zzzckck
f0c7795542 Merge pull request #2527 from bnb-chain/develop
Draft release v1.4.10
2024-06-21 16:13:48 +08:00
zzzckck
4566ac7659 Merge pull request #2511 from bnb-chain/develop
Draft release v1.4.9
2024-06-11 11:51:22 +08:00
135 changed files with 2619 additions and 525 deletions

View File

@@ -82,28 +82,28 @@ jobs:
# ============================== # ==============================
- name: Upload Linux Build - name: Upload Linux Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
with: with:
name: linux name: linux
path: ./build/bin/geth path: ./build/bin/geth
- name: Upload MacOS Build - name: Upload MacOS Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-latest'
with: with:
name: macos name: macos
path: ./build/bin/geth path: ./build/bin/geth
- name: Upload Windows Build - name: Upload Windows Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
with: with:
name: windows name: windows
path: ./build/bin/geth.exe path: ./build/bin/geth.exe
- name: Upload ARM-64 Build - name: Upload ARM-64 Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
with: with:
name: arm64 name: arm64
@@ -125,25 +125,25 @@ jobs:
# ============================== # ==============================
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: linux name: linux
path: ./linux path: ./linux
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: macos name: macos
path: ./macos path: ./macos
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: windows name: windows
path: ./windows path: ./windows
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: arm64 name: arm64
path: ./arm64 path: ./arm64

View File

@@ -81,28 +81,28 @@ jobs:
# ============================== # ==============================
- name: Upload Linux Build - name: Upload Linux Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
with: with:
name: linux name: linux
path: ./build/bin/geth path: ./build/bin/geth
- name: Upload MacOS Build - name: Upload MacOS Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-latest'
with: with:
name: macos name: macos
path: ./build/bin/geth path: ./build/bin/geth
- name: Upload Windows Build - name: Upload Windows Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
with: with:
name: windows name: windows
path: ./build/bin/geth.exe path: ./build/bin/geth.exe
- name: Upload ARM-64 Build - name: Upload ARM-64 Build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.3.3
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
with: with:
name: arm64 name: arm64
@@ -124,25 +124,25 @@ jobs:
# ============================== # ==============================
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: linux name: linux
path: ./linux path: ./linux
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: macos name: macos
path: ./macos path: ./macos
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: windows name: windows
path: ./windows path: ./windows
- name: Download Artifacts - name: Download Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4.1.7
with: with:
name: arm64 name: arm64
path: ./arm64 path: ./arm64

View File

@@ -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-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-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.

View File

@@ -1,4 +1,70 @@
# Changelog # 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
* [\#2643](https://github.com/bnb-chain/bsc/pull/2643)core: fix cache for receipts
* [\#2656](https://github.com/bnb-chain/bsc/pull/2656)ethclient: fix BlobSidecars api
* [\#2657](https://github.com/bnb-chain/bsc/pull/2657)fix: update prunefreezers offset when pruneancient and the dataset has pruned block
### FEATURE
* [\#2661](https://github.com/bnb-chain/bsc/pull/2661)config: setup Mainnet 2 hardfork date: HaberFix & Bohr
### IMPROVEMENT
* [\#2578](https://github.com/bnb-chain/bsc/pull/2578)core/systemcontracts: use vm.StateDB in UpgradeBuildInSystemContract
* [\#2649](https://github.com/bnb-chain/bsc/pull/2649)internal/debug: remove memsize
* [\#2655](https://github.com/bnb-chain/bsc/pull/2655)internal/ethapi: make GetFinalizedHeader monotonically increasing
* [\#2658](https://github.com/bnb-chain/bsc/pull/2658)core: improve readability of the fork choice logic
* [\#2665](https://github.com/bnb-chain/bsc/pull/2665)faucet: bump and resend faucet transaction if it has been pending for a while
## v1.4.13
### BUGFIX
* [\#2602](https://github.com/bnb-chain/bsc/pull/2602) fix: prune-state when specify --triesInMemory 32
* [\#2579](https://github.com/bnb-chain/bsc/pull/00025790) fix: only take non-mempool tx to calculate bid price
### FEATURE
* [\#2634](https://github.com/bnb-chain/bsc/pull/2634) config: setup Testnet Bohr hardfork date
* [\#2482](https://github.com/bnb-chain/bsc/pull/2482) BEP-341: Validators can produce consecutive blocks
* [\#2502](https://github.com/bnb-chain/bsc/pull/2502) BEP-402: Complete Missing Fields in Block Header to Generate Signature
* [\#2558](https://github.com/bnb-chain/bsc/pull/2558) BEP-404: Clear Miner History when Switching Validators Set
* [\#2605](https://github.com/bnb-chain/bsc/pull/2605) feat: add bohr upgrade contracts bytecode
* [\#2614](https://github.com/bnb-chain/bsc/pull/2614) fix: update stakehub bytecode after zero address agent issue fixed
* [\#2608](https://github.com/bnb-chain/bsc/pull/2608) consensus/parlia: modify mining time for last block in one turn
* [\#2618](https://github.com/bnb-chain/bsc/pull/2618) consensus/parlia: exclude inturn validator when calculate backoffTime
* [\#2621](https://github.com/bnb-chain/bsc/pull/2621) core: not record zero hash beacon block root with Parlia engine
### IMPROVEMENT
* [\#2589](https://github.com/bnb-chain/bsc/pull/2589) core/vote: vote before committing state and writing block
* [\#2596](https://github.com/bnb-chain/bsc/pull/2596) core: improve the network stability when double sign happens
* [\#2600](https://github.com/bnb-chain/bsc/pull/2600) core: cache block after wroten into db
* [\#2629](https://github.com/bnb-chain/bsc/pull/2629) utils: add GetTopAddr to analyse large traffic
* [\#2591](https://github.com/bnb-chain/bsc/pull/2591) consensus/parlia: add GetJustifiedNumber and GetFinalizedNumber
* [\#2611](https://github.com/bnb-chain/bsc/pull/2611) cmd/utils: add new flag OverridePassedForkTime
* [\#2603](https://github.com/bnb-chain/bsc/pull/2603) faucet: rate limit initial implementation
* [\#2622](https://github.com/bnb-chain/bsc/pull/2622) tests: fix evm-test CI
* [\#2628](https://github.com/bnb-chain/bsc/pull/2628) Makefile: use docker compose v2 instead of v1
## v1.4.12 ## v1.4.12
@@ -35,7 +101,6 @@
* [\#2534](https://github.com/bnb-chain/bsc/pull/2534) fix: nil pointer when clear simulating bid * [\#2534](https://github.com/bnb-chain/bsc/pull/2534) fix: nil pointer when clear simulating bid
* [\#2535](https://github.com/bnb-chain/bsc/pull/2535) upgrade: add HaberFix hardfork * [\#2535](https://github.com/bnb-chain/bsc/pull/2535) upgrade: add HaberFix hardfork
## v1.4.10 ## v1.4.10
### FEATURE ### FEATURE
NA NA

View File

@@ -17,6 +17,11 @@ geth:
@echo "Done building." @echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth." @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: Build all packages and executables
all: all:
$(GORUN) build/ci.go install $(GORUN) build/ci.go install
@@ -29,11 +34,11 @@ truffle-test:
docker build . -f ./docker/Dockerfile --target bsc-genesis -t bsc-genesis docker build . -f ./docker/Dockerfile --target bsc-genesis -t bsc-genesis
docker build . -f ./docker/Dockerfile --target bsc -t bsc docker build . -f ./docker/Dockerfile --target bsc -t bsc
docker build . -f ./docker/Dockerfile.truffle -t truffle-test docker build . -f ./docker/Dockerfile.truffle -t truffle-test
docker-compose -f ./tests/truffle/docker-compose.yml up genesis docker compose -f ./tests/truffle/docker-compose.yml up genesis
docker-compose -f ./tests/truffle/docker-compose.yml up -d bsc-rpc bsc-validator1 docker compose -f ./tests/truffle/docker-compose.yml up -d bsc-rpc bsc-validator1
sleep 30 sleep 30
docker-compose -f ./tests/truffle/docker-compose.yml up --exit-code-from truffle-test truffle-test docker compose -f ./tests/truffle/docker-compose.yml up --exit-code-from truffle-test truffle-test
docker-compose -f ./tests/truffle/docker-compose.yml down docker compose -f ./tests/truffle/docker-compose.yml down
#? lint: Run certain pre-selected linters #? lint: Run certain pre-selected linters
lint: ## Run linters. lint: ## Run linters.

View File

@@ -11,14 +11,13 @@ https://pkg.go.dev/badge/github.com/ethereum/go-ethereum
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. - **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. - **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. - **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 ## Key features
@@ -34,18 +33,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. 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. 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. 3. Validator set are elected in and out based on a staking based governance on BNB Smart Chain.
4. The validator set change is relayed via a cross-chain communication mechanism. 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.
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.
## Native Token ## Native Token
@@ -53,7 +42,6 @@ BNB will run on BNB Smart Chain in the same way as ETH runs on Ethereum so that
BNB will be used to: BNB will be used to:
1. pay `gas` to deploy or invoke Smart Contract on BSC 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 ## Building the source
@@ -247,9 +235,7 @@ running web servers, so malicious web pages could try to subvert locally availab
APIs!** APIs!**
### Operating a private network ### 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-Deploy](https://github.com/bnb-chain/node-deploy/): deploy tool for setting up BNB Smart Chain.
- [BSC-Docker](https://github.com/bnb-chain/bsc-docker): deploy tool for setting up local BSC cluster in container.
## Running a bootnode ## Running a bootnode

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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},
},
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}
}
}

View File

@@ -43,7 +43,42 @@ func TestExtraParse(t *testing.T) {
} }
} }
// case 3, |---Extra Vanity---|---Empty---|---Vote Attestation---|---Extra Seal---| // case 3, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Length---|---Empty---|---Extra Seal---|
{
extraData := "0xd983010209846765746889676f312e31392e3131856c696e75780000a6bf97c1152465176c461afb316ebc773c61faee85a6515daa8a923564c6ffd37fb2fe9f118ef88092e8762c7addb526ab7eb1e772baef85181f892c731be0c1891a50e6b06262c816295e26495cef6f69dfa69911d9d8e4f3bbadb89b977cf58294f7239d515e15b24cfeb82494056cf691eaf729b165f32c9757c429dba5051155903067e56ebe3698678e912d4c407bbe49438ed859fe965b140dcf1aab71a993c1f7f6929d1fe2a17b4e14614ef9fc5bdc713d6631d675403fbeefac55611bf612700b1b65f4744861b80b0f7d6ab03f349bbafec1551819b8be1efea2fc46ca749aa184248a459464eec1a21e7fc7b71a053d9644e9bb8da4853b8f872cd7c1d6b324bf1922829830646ceadfb658d3de009a61dd481a114a2e761c554b641742c973867899d300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069c77a677c40c7fbea129d4b171a39b7a8ddabfab2317f59d86abfaf690850223d90e9e7593d91a29331dfc2f84d5adecc75fc39ecab4632c1b4400a3dd1e1298835bcca70f657164e5b75689b64b7fd1fa275f334f28e1896a26afa1295da81418593bd12814463d9f6e45c36a0e47eb4cd3e5b6af29c41e2a3a5636430155a466e216585af3ba772b61c6014342d914470ec7ac2975be345796c2b81db0422a5fd08e40db1fc2368d2245e4b18b1d0b85c921aaaafd2e341760e29fc613edd39f71254614e2055c3287a517ae2f5b9e386cd1b50a4550696d957cb4900f03ab84f83ff2df44193496793b847f64e9d6db1b3953682bb95edd096eb1e69bbd357c200992ca78050d0cbe180cfaa018e8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec73a8a257074e82b881cfa06ef3eb4efeca060c2531359abd0eab8af1e3edfa2025fca464ac9c3fd123f6c24a0d78869485a6f79b60359f141df90a0c745125b131caaffd12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b218c5d6af1f979ac42bc68d98a5a0d796c6ab01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4dd66d7c2c7e57f628210187192fb89d4b99dd4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be807dddb074639cd9fa61b47676c064fc50d62cb1f2c71577def3144fabeb75a8a1c8cb5b51d1d1b4a05eec67988b8685008baa17459ec425dbaebc852f496dc92196cdcc8e6d00c17eb431350c6c50d8b8f05176b90b11b3a3d4feb825ae9702711566df5dbf38e82add4dd1b573b95d2466fa6501ccb81e9d26a352b96150ccbf7b697fd0a419d1d6bf74282782b0b3eb1413c901d6ecf02e8e28000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2d3a739effcd3a99387d015e260eefac72ebea1956c470ddff48cb49300200b5f83497f3a3ccb3aeb83c5edd9818569038e61d197184f4aa6939ea5e9911e3e98ac6d21e9ae3261a475a27bb1028f140bc2a7c843318afd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea0a6e3c511bbd10f4519ece37dc24887e11b55db2d4c6283c44a1c7bd503aaba7666e9f0c830e0ff016c1c750a5e48757a713d0836b1cabfd5c281b1de3b77d1c192183ee226379db83cffc681495730c11fdde79ba4c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef0274e31810c9df02f98fafde0f841f4e66a1cd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e99f701bb14cb7dfb68b90bd3e6d1ca656964630de71beffc7f33f7f08ec99d336ec51ad9fad0ac84ae77ca2e8ad9512acc56e0d7c93f3c2ce7de1b69149a5a400"
extra, err := parseExtra(extraData)
assert.NoError(t, err)
{
var have = extra.ValidatorSize
var want = uint8(21)
if have != want {
t.Fatalf("extra.ValidatorSize mismatch, have %d, want %d", have, want)
}
}
{
var have = common.Bytes2Hex(extra.Validators[14].Address[:])
var want = "cc8e6d00c17eb431350c6c50d8b8f05176b90b11"
if have != want {
t.Fatalf("extra.Validators[14].Address mismatch, have %s, want %s", have, want)
}
}
{
var have = common.Bytes2Hex(extra.Validators[18].BLSPublicKey[:])
var want = "b2d4c6283c44a1c7bd503aaba7666e9f0c830e0ff016c1c750a5e48757a713d0836b1cabfd5c281b1de3b77d1c192183"
if have != want {
t.Fatalf("extra.Validators[18].BLSPublicKey mismatch, have %s, want %s", have, want)
}
}
{
var have = extra.TurnLength
var want = uint8(4)
if *have != want {
t.Fatalf("extra.TurnLength mismatch, have %d, want %d", *have, want)
}
}
}
// case 4, |---Extra Vanity---|---Empty---|---Vote Attestation---|---Extra Seal---|
{ {
extraData := "0xd883010205846765746888676f312e32302e35856c696e75780000002995c52af8b5830563efb86089cf168dcf4c5d3cb057926628ad1bf0f03ea67eef1458485578a4f8489afa8a853ecc7af45e2d145c21b70641c4b29f0febd2dd2c61fa1ba174be3fd47f1f5fa2ab9b5c318563d8b70ca58d0d51e79ee32b2fb721649e2cb9d36538361fba11f84c8401d14bb7a0fa67ddb3ba654d6006bf788710032247aa4d1be0707273e696b422b3ff72e9798401d14bbaa01225f505f5a0e1aefadcd2913b7aac9009fe4fb3d1bf57399e0b9dce5947f94280fe6d3647276c4127f437af59eb7c7985b2ae1ebe432619860695cb6106b80cc66c735bc1709afd11f233a2c97409d38ebaf7178aa53e895aea2fe0a229f71ec601" extraData := "0xd883010205846765746888676f312e32302e35856c696e75780000002995c52af8b5830563efb86089cf168dcf4c5d3cb057926628ad1bf0f03ea67eef1458485578a4f8489afa8a853ecc7af45e2d145c21b70641c4b29f0febd2dd2c61fa1ba174be3fd47f1f5fa2ab9b5c318563d8b70ca58d0d51e79ee32b2fb721649e2cb9d36538361fba11f84c8401d14bb7a0fa67ddb3ba654d6006bf788710032247aa4d1be0707273e696b422b3ff72e9798401d14bbaa01225f505f5a0e1aefadcd2913b7aac9009fe4fb3d1bf57399e0b9dce5947f94280fe6d3647276c4127f437af59eb7c7985b2ae1ebe432619860695cb6106b80cc66c735bc1709afd11f233a2c97409d38ebaf7178aa53e895aea2fe0a229f71ec601"
extra, err := parseExtra(extraData) extra, err := parseExtra(extraData)
@@ -64,9 +99,9 @@ func TestExtraParse(t *testing.T) {
} }
} }
// case 4, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Vote Attestation---|---Extra Seal---| // case 5, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Vote Attestation---|---Extra Seal---|
{ {
extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b878f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed284070808b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200" extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b878f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed28407080048b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200"
extra, err := parseExtra(extraData) extra, err := parseExtra(extraData)
assert.NoError(t, err) assert.NoError(t, err)
{ {
@@ -105,4 +140,53 @@ func TestExtraParse(t *testing.T) {
} }
} }
} }
// case 6, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Length---|---Vote Attestation---|---Extra Seal---|
{
extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b87804f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed28407080048b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200"
extra, err := parseExtra(extraData)
assert.NoError(t, err)
{
var have = common.Bytes2Hex(extra.Validators[0].Address[:])
var want = "1284214b9b9c85549ab3d2b972df0deef66ac2c9"
if have != want {
t.Fatalf("extra.Validators[0].Address mismatch, have %s, want %s", have, want)
}
}
{
var have = common.Bytes2Hex(extra.Validators[0].BLSPublicKey[:])
var want = "8e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c"
if have != want {
t.Fatalf("extra.Validators[0].BLSPublicKey mismatch, have %s, want %s", have, want)
}
}
{
var have = extra.Validators[0].VoteIncluded
var want = true
if have != want {
t.Fatalf("extra.Validators[0].VoteIncluded mismatch, have %t, want %t", have, want)
}
}
{
var have = common.Bytes2Hex(extra.Data.TargetHash[:])
var want = "0edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed284070"
if have != want {
t.Fatalf("extra.Data.TargetHash mismatch, have %s, want %s", have, want)
}
}
{
var have = extra.Data.TargetNumber
var want = uint64(32096999)
if have != want {
t.Fatalf("extra.Data.TargetNumber mismatch, have %d, want %d", have, want)
}
}
{
var have = extra.TurnLength
var want = uint8(4)
if *have != want {
t.Fatalf("extra.TurnLength mismatch, have %d, want %d", *have, want)
}
}
}
} }

View File

@@ -24,7 +24,7 @@ const (
BLSPublicKeyLength = 48 BLSPublicKeyLength = 48
// follow order in extra field // follow order in extra field
// |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---| // |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Turn Length (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---|
extraVanityLength = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity extraVanityLength = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
validatorNumberSize = 1 // Fixed number of extra prefix bytes reserved for validator number after Luban validatorNumberSize = 1 // Fixed number of extra prefix bytes reserved for validator number after Luban
validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength
@@ -35,6 +35,7 @@ type Extra struct {
ExtraVanity string ExtraVanity string
ValidatorSize uint8 ValidatorSize uint8
Validators validatorsAscending Validators validatorsAscending
TurnLength *uint8
*types.VoteAttestation *types.VoteAttestation
ExtraSeal []byte ExtraSeal []byte
} }
@@ -113,6 +114,15 @@ func parseExtra(hexData string) (*Extra, error) {
sort.Sort(extra.Validators) sort.Sort(extra.Validators)
data = data[validatorBytesTotalLength-validatorNumberSize:] data = data[validatorBytesTotalLength-validatorNumberSize:]
dataLength = len(data) dataLength = len(data)
// parse TurnLength
if dataLength > 0 {
if data[0] != '\xf8' {
extra.TurnLength = &data[0]
data = data[1:]
dataLength = len(data)
}
}
} }
// parse Vote Attestation // parse Vote Attestation
@@ -148,6 +158,10 @@ func prettyExtra(extra Extra) {
} }
} }
if extra.TurnLength != nil {
fmt.Printf("TurnLength : %d\n", *extra.TurnLength)
}
if extra.VoteAttestation != nil { if extra.VoteAttestation != nil {
fmt.Printf("Attestation :\n") fmt.Printf("Attestation :\n")
fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count()) fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count())

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -56,6 +56,7 @@ var (
genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with") genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with")
apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection") 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") 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") 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") 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") 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") 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") 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 ( var (
@@ -87,11 +94,17 @@ var (
//go:embed faucet.html //go:embed faucet.html
var websiteTmpl string 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() { func main() {
// Parse the flags and set up the logger to print everything requested // Parse the flags and set up the logger to print everything requested
flag.Parse() 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 // Construct the payout tiers
amounts := make([]string, *tiersFlag) amounts := make([]string, *tiersFlag)
for i := 0; i < *tiersFlag; i++ { for i := 0; i < *tiersFlag; i++ {
@@ -170,7 +183,7 @@ func main() {
log.Crit("Failed to unlock faucet signer account", "err", err) log.Crit("Failed to unlock faucet signer account", "err", err)
} }
// Assemble and start the faucet light service // 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 { if err != nil {
log.Crit("Failed to start faucet", "err", err) log.Crit("Failed to start faucet", "err", err)
} }
@@ -199,6 +212,7 @@ type bep2eInfo struct {
type faucet struct { type faucet struct {
config *params.ChainConfig // Chain configurations for signing config *params.ChainConfig // Chain configurations for signing
client *ethclient.Client // Client connection to the Ethereum chain 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 index []byte // Index page to serve up on the web
keystore *keystore.KeyStore // Keystore containing the single signer keystore *keystore.KeyStore // Keystore containing the single signer
@@ -228,7 +242,7 @@ type wsConn struct {
wlock sync.Mutex 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)) bep2eAbi, err := abi.JSON(strings.NewReader(bep2eAbiJson))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -237,6 +251,11 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
if err != nil { if err != nil {
return nil, err 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 // Allow 1 request per minute with burst of 5, and cache up to 1000 IPs
limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000) limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000)
@@ -247,6 +266,7 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
return &faucet{ return &faucet{
config: genesis.Config, config: genesis.Config,
client: client, client: client,
clientMainnet: clientMainnet,
index: index, index: index,
keystore: ks, keystore: ks,
account: ks.Accounts()[0], account: ks.Accounts()[0],
@@ -378,7 +398,11 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
Captcha string `json:"captcha"` Captcha string `json:"captcha"`
Symbol string `json:"symbol"` 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 { if err = conn.ReadJSON(&msg); err != nil {
log.Debug("read json message failed", "err", err, "ip", ip)
return return
} }
if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") { 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 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 != "" { if *captchaToken != "" {
form := url.Values{} form := url.Values{}
form.Add("secret", *captchaSecret) form.Add("secret", *captchaSecret)
@@ -475,24 +499,55 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
} }
continue 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() f.lock.Lock()
var (
fund bool
timeout time.Time
)
if ipTimeout := f.timeouts[ips[len(ips)-2]]; time.Now().Before(ipTimeout) { 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 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) log.Warn("Failed to send funding error to client", "err", err)
return
} }
f.lock.Unlock() log.Info("too frequent funding(ip)", "TimeLeft", common.PrettyDuration(time.Until(ipTimeout)), "ip", ips[len(ips)-2], "ipsStr", ipsStr)
continue 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
}
// 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 {
if balanceMainnet.Cmp(minMainnetBalance) < 0 {
f.lock.Unlock()
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
}
}
}
log.Info("Faucet request valid", "url", msg.URL, "tier", msg.Tier, "user", username, "address", address, "ip", ip)
if timeout = f.timeouts[id]; time.Now().After(timeout) { // now, it is ok to send tBNB or other tokens
var tx *types.Transaction var tx *types.Transaction
if msg.Symbol == "BNB" { if msg.Symbol == "BNB" {
// User wasn't funded recently, create the funding transaction // User wasn't funded recently, create the funding transaction
@@ -540,23 +595,12 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
Time: time.Now(), Time: time.Now(),
Tx: signed, Tx: signed,
}) })
timeout := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute timeoutInt64 := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute
grace := timeout / 288 // 24h timeout => 5m grace grace := timeoutInt64 / 288 // 24h timeout => 5m grace
f.timeouts[id] = time.Now().Add(timeout - grace) f.timeouts[id] = time.Now().Add(timeoutInt64 - grace)
f.timeouts[ips[len(ips)-2]] = time.Now().Add(timeout - grace) f.timeouts[ips[len(ips)-2]] = time.Now().Add(timeoutInt64 - grace)
fund = true
}
f.lock.Unlock() 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)
return
}
continue
}
if err = sendSuccess(wsconn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil { 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) log.Warn("Failed to send funding success to client", "err", err)
return return
@@ -605,9 +649,52 @@ func (f *faucet) refresh(head *types.Header) error {
f.lock.Lock() f.lock.Lock()
f.head, f.balance = head, balance f.head, f.balance = head, balance
f.price, f.nonce = price, nonce 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] 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 { for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
f.reqs = f.reqs[1:] f.reqs = f.reqs[1:]
} }

View File

@@ -62,7 +62,9 @@ var (
ArgsUsage: "<genesisPath>", ArgsUsage: "<genesisPath>",
Flags: flags.Merge([]cli.Flag{ Flags: flags.Merge([]cli.Flag{
utils.CachePreimagesFlag, utils.CachePreimagesFlag,
utils.OverrideBohr, utils.OverridePassedForkTime,
utils.OverridePascal,
utils.OverridePrague,
utils.OverrideVerkle, utils.OverrideVerkle,
utils.MultiDataBaseFlag, utils.MultiDataBaseFlag,
}, utils.DatabaseFlags), }, utils.DatabaseFlags),
@@ -253,9 +255,17 @@ func initGenesis(ctx *cli.Context) error {
defer stack.Close() defer stack.Close()
var overrides core.ChainOverrides var overrides core.ChainOverrides
if ctx.IsSet(utils.OverrideBohr.Name) { if ctx.IsSet(utils.OverridePassedForkTime.Name) {
v := ctx.Uint64(utils.OverrideBohr.Name) v := ctx.Uint64(utils.OverridePassedForkTime.Name)
overrides.OverrideBohr = &v overrides.OverridePassedForkTime = &v
}
if ctx.IsSet(utils.OverridePascal.Name) {
v := ctx.Uint64(utils.OverridePascal.Name)
overrides.OverridePascal = &v
}
if ctx.IsSet(utils.OverridePrague.Name) {
v := ctx.Uint64(utils.OverridePrague.Name)
overrides.OverridePrague = &v
} }
if ctx.IsSet(utils.OverrideVerkle.Name) { if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name) v := ctx.Uint64(utils.OverrideVerkle.Name)

View File

@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/scwallet"
"github.com/ethereum/go-ethereum/accounts/usbwallet" "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/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
@@ -96,6 +97,7 @@ type gethConfig struct {
Node node.Config Node node.Config
Ethstats ethstatsConfig Ethstats ethstatsConfig
Metrics metrics.Config Metrics metrics.Config
FakeBeacon fakebeacon.Config
} }
func loadConfig(file string, cfg *gethConfig) error { func loadConfig(file string, cfg *gethConfig) error {
@@ -185,9 +187,17 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
params.RialtoGenesisHash = common.HexToHash(v) params.RialtoGenesisHash = common.HexToHash(v)
} }
if ctx.IsSet(utils.OverrideBohr.Name) { if ctx.IsSet(utils.OverridePassedForkTime.Name) {
v := ctx.Uint64(utils.OverrideBohr.Name) v := ctx.Uint64(utils.OverridePassedForkTime.Name)
cfg.Eth.OverrideBohr = &v cfg.Eth.OverridePassedForkTime = &v
}
if ctx.IsSet(utils.OverridePascal.Name) {
v := ctx.Uint64(utils.OverridePascal.Name)
cfg.Eth.OverridePascal = &v
}
if ctx.IsSet(utils.OverridePrague.Name) {
v := ctx.Uint64(utils.OverridePrague.Name)
cfg.Eth.OverridePrague = &v
} }
if ctx.IsSet(utils.OverrideVerkle.Name) { if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name) v := ctx.Uint64(utils.OverrideVerkle.Name)
@@ -206,6 +216,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
if ctx.IsSet(utils.OverrideBreatheBlockInterval.Name) { if ctx.IsSet(utils.OverrideBreatheBlockInterval.Name) {
params.BreatheBlockInterval = ctx.Uint64(utils.OverrideBreatheBlockInterval.Name) params.BreatheBlockInterval = ctx.Uint64(utils.OverrideBreatheBlockInterval.Name)
} }
if ctx.IsSet(utils.OverrideFixedTurnLength.Name) {
params.FixedTurnLength = ctx.Uint64(utils.OverrideFixedTurnLength.Name)
}
backend, eth := utils.RegisterEthService(stack, &cfg.Eth) backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
@@ -235,11 +248,22 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL) 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() git, _ := version.VCS()
utils.SetupMetrics(ctx, utils.SetupMetrics(ctx,
utils.EnableBuildInfo(git.Commit, git.Date), utils.EnableBuildInfo(git.Commit, git.Date),
utils.EnableMinerInfo(ctx, &cfg.Eth.Miner), utils.EnableMinerInfo(ctx, &cfg.Eth.Miner),
utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()), utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()),
utils.EnableNodeTrack(ctx, &cfg.Eth, stack),
) )
return stack, backend return stack, backend
} }

View File

@@ -72,12 +72,15 @@ var (
utils.USBFlag, utils.USBFlag,
utils.SmartCardDaemonPathFlag, utils.SmartCardDaemonPathFlag,
utils.RialtoHash, utils.RialtoHash,
utils.OverrideBohr, utils.OverridePassedForkTime,
utils.OverridePascal,
utils.OverridePrague,
utils.OverrideVerkle, utils.OverrideVerkle,
utils.OverrideFullImmutabilityThreshold, utils.OverrideFullImmutabilityThreshold,
utils.OverrideMinBlocksForBlobRequests, utils.OverrideMinBlocksForBlobRequests,
utils.OverrideDefaultExtraReserveForBlobRequests, utils.OverrideDefaultExtraReserveForBlobRequests,
utils.OverrideBreatheBlockInterval, utils.OverrideBreatheBlockInterval,
utils.OverrideFixedTurnLength,
utils.EnablePersonal, utils.EnablePersonal,
utils.TxPoolLocalsFlag, utils.TxPoolLocalsFlag,
utils.TxPoolNoLocalsFlag, utils.TxPoolNoLocalsFlag,
@@ -230,6 +233,12 @@ var (
utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag, utils.MetricsInfluxDBOrganizationFlag,
} }
fakeBeaconFlags = []cli.Flag{
utils.FakeBeaconEnabledFlag,
utils.FakeBeaconAddrFlag,
utils.FakeBeaconPortFlag,
}
) )
var app = flags.NewApp("the go-ethereum command line interface") var app = flags.NewApp("the go-ethereum command line interface")
@@ -284,6 +293,7 @@ func init() {
consoleFlags, consoleFlags,
debug.Flags, debug.Flags,
metricsFlags, metricsFlags,
fakeBeaconFlags,
) )
flags.AutoEnvVars(app.Flags, "GETH") flags.AutoEnvVars(app.Flags, "GETH")
@@ -369,8 +379,6 @@ func geth(ctx *cli.Context) error {
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner. // miner.
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) { func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
debug.Memsize.Add("node", stack)
// Start up the node itself // Start up the node itself
utils.StartNode(ctx, stack, isConsole) utils.StartNode(ctx, stack, isConsole)
@@ -443,22 +451,23 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
} }
// Start auxiliary services if enabled // 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) { if ctx.Bool(utils.MiningEnabledFlag.Name) {
// Mining only makes sense if a full Ethereum node is running // Mining only makes sense if a full Ethereum node is running
if ctx.String(utils.SyncModeFlag.Name) == "light" { if ctx.String(utils.SyncModeFlag.Name) == "light" {
utils.Fatalf("Light clients do not support mining") utils.Fatalf("Light clients do not support mining")
} }
ethBackend, ok := backend.(*eth.EthAPIBackend)
if !ok { if !ok {
utils.Fatalf("Ethereum service not running") utils.Fatalf("Ethereum service not running")
} }
// Set the gas price to the limits from the CLI and start mining // Set the gas price to the limits from the CLI and start mining
gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
ethBackend.TxPool().SetGasTip(gasprice) ethBackend.TxPool().SetGasTip(gasprice)
gasCeil := ethBackend.Miner().GasCeil()
if gasCeil > params.SystemTxsGas {
ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas)
}
if err := ethBackend.StartMining(); err != nil { if err := ethBackend.StartMining(); err != nil {
utils.Fatalf("Failed to start mining: %v", err) utils.Fatalf("Failed to start mining: %v", err)
} }

View File

@@ -13,6 +13,8 @@ const main = async () => {
let gasUsedTotal = 0; let gasUsedTotal = 0;
let inturnBlocks = 0; let inturnBlocks = 0;
let justifiedBlocks = 0; let justifiedBlocks = 0;
let turnLength = await provider.send("parlia_getTurnLength", [
ethers.toQuantity(program.startNum)]);
for (let i = program.startNum; i < program.endNum; i++) { for (let i = program.startNum; i < program.endNum; i++) {
let txCount = await provider.send("eth_getBlockTransactionCountByNumber", [ let txCount = await provider.send("eth_getBlockTransactionCountByNumber", [
ethers.toQuantity(i)]); ethers.toQuantity(i)]);
@@ -35,7 +37,7 @@ const main = async () => {
} else { } else {
console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber) console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber)
} }
console.log("BlockNumber =", i, "mod =", i%4, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp) console.log("BlockNumber =", i, "mod =", i%turnLength, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp)
} }
let blockCount = program.endNum - program.startNum let blockCount = program.endNum - program.startNum

View File

@@ -0,0 +1,164 @@
import { ethers } from "ethers";
import program from "commander";
// Global Options:
program.option("--rpc <rpc>", "Rpc");
// GetTxCount Options:
program.option("--startNum <startNum>", "start num")
program.option("--endNum <endNum>", "end num")
program.option("--miner <miner>", "miner", "")
// GetVersion Options:
program.option("--num <Num>", "validator num", 21)
// GetTopAddr Options:
program.option("--topNum <Num>", "top num of address to be displayed", 20)
program.parse(process.argv);
const provider = new ethers.JsonRpcProvider(program.rpc)
function printUsage() {
console.log("Usage:");
console.log(" node getchainstatus.js --help");
console.log(" node getchainstatus.js [subcommand] [options]");
console.log("\nSubcommands:");
console.log(" GetTxCount: find the block with max tx size of a range");
console.log(" GetVersion: dump validators' binary version, based on Header.Extra");
console.log(" GetTopAddr: get hottest $topNum target address within a block range");
console.log("\nOptions:");
console.log(" --rpc specify the url of RPC endpoint");
console.log(" --startNum the start block number, for command GetTxCount");
console.log(" --endNum the end block number, for command GetTxCount");
console.log(" --miner the miner address, for command GetTxCount");
console.log(" --num the number of blocks to be checked, for command GetVersion");
console.log(" --topNum the topNum of blocks to be checked, for command GetVersion");
console.log("\nExample:");
// mainnet https://bsc-mainnet.nodereal.io/v1/454e504917db4f82b756bd0cf6317dce
console.log(" node getchainstatus.js GetTxCount --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000005")
console.log(" node getchainstatus.js GetVersion --rpc https://bsc-testnet-dataseed.bnbchain.org --num 21")
console.log(" node getchainstatus.js GetTopAddr --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000010 --topNum 10")
}
// 1.cmd: "GetTxCount", usage:
// node getchainstatus.js GetTxCount --rpc https://bsc-testnet-dataseed.bnbchain.org \
// --startNum 40000001 --endNum 40000005 \
// --miner(optional): specified: find the max txCounter from the specified validator,
// not specified: find the max txCounter from all validators
async function getTxCount() {
let txCount = 0;
let num = 0;
console.log("Find the max txs count between", program.startNum, "and", program.endNum);
for (let i = program.startNum; i < program.endNum; i++) {
if (program.miner !== "") {
let blockData = await provider.getBlock(Number(i))
if (program.miner !== blockData.miner) {
continue
}
}
let x = await provider.send("eth_getBlockTransactionCountByNumber", [
ethers.toQuantity(i)]);
let a = ethers.toNumber(x)
if (a > txCount) {
num = i;
txCount = a;
}
}
console.log("BlockNum = ", num, "TxCount =", txCount);
}
// 2.cmd: "GetVersion", usage:
// node getchainstatus.js GetVersion \
// --rpc https://bsc-testnet-dataseed.bnbchain.org \
// --num(optional): defualt 21, the number of blocks that will be checked
async function getBinaryVersion() {
const blockNum = await provider.getBlockNumber();
console.log(blockNum);
for (let i = 0; i < program.num; i++) {
let blockData = await provider.getBlock(blockNum - i);
// 1.get Geth client version
let major = ethers.toNumber(ethers.dataSlice(blockData.extraData, 2, 3))
let minor = ethers.toNumber(ethers.dataSlice(blockData.extraData, 3, 4))
let patch = ethers.toNumber(ethers.dataSlice(blockData.extraData, 4, 5))
// 2.get minimum txGasPrice based on the last non-zero-gasprice transaction
let lastGasPrice = 0
for (let txIndex = blockData.transactions.length - 1; txIndex >= 0; txIndex--) {
let txHash = blockData.transactions[txIndex]
let txData = await provider.getTransaction(txHash);
if (txData.gasPrice == 0) {
continue
}
lastGasPrice = txData.gasPrice
break
}
console.log(blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice)
}
};
// 3.cmd: "GetTopAddr", usage:
// node getchainstatus.js GetTopAddr \
// --rpc https://bsc-testnet-dataseed.bnbchain.org \
// --startNum 40000001 --endNum 40000005 \
// --topNum(optional): the top num of address to be displayed, default 20
function getTopKElements(map, k) {
let entries = Array.from(map.entries());
entries.sort((a, b) => b[1] - a[1]);
return entries.slice(0, k);
}
async function getTopAddr() {
let countMap = new Map();
let totalTxs = 0
console.log("Find the top target address, between", program.startNum, "and", program.endNum);
for (let i = program.startNum; i <= program.endNum; i++) {
let blockData = await provider.getBlock(Number(i), true)
totalTxs += blockData.transactions.length
for (let txIndex = blockData.transactions.length - 1; txIndex >= 0; txIndex--) {
let txData = await blockData.getTransaction(txIndex)
if (txData.to == null) {
console.log("Contract creation,txHash:", txData.hash)
continue
}
let toAddr = txData.to;
if (countMap.has(toAddr)) {
countMap.set(toAddr, countMap.get(toAddr) + 1);
} else {
countMap.set(toAddr, 1);
}
}
console.log("progress:", (program.endNum-i), "blocks left", "totalTxs", totalTxs)
}
let tops = getTopKElements(countMap, program.topNum)
tops.forEach((value, key) => {
// value: [ '0x40661F989826CC641Ce1601526Bb16a4221412c8', 71 ]
console.log(key+":", value[0], " ", value[1], " ", ((value[1]*100)/totalTxs).toFixed(2)+"%");
});
};
const main = async () => {
if (process.argv.length <= 2) {
console.error('invalid process.argv.length', process.argv.length);
printUsage()
return
}
const cmd = process.argv[2]
if (cmd === "--help") {
printUsage()
return
}
if (cmd === "GetTxCount") {
await getTxCount()
} else if (cmd === "GetVersion") {
await getBinaryVersion()
} else if (cmd === "GetTopAddr") {
await getTopAddr()
} else {
console.log("unsupported cmd", cmd);
printUsage()
}
}
main().then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -1,41 +0,0 @@
import { ethers } from "ethers";
import program from "commander";
program.option("--rpc <rpc>", "Rpc");
program.option("--startNum <startNum>", "start num")
program.option("--endNum <endNum>", "end num")
// --miner:
// specified: find the max txCounter from the specified validator
// not specified: find the max txCounter from all validators
program.option("--miner <miner>", "miner", "")
program.parse(process.argv);
const provider = new ethers.JsonRpcProvider(program.rpc)
const main = async () => {
let txCount = 0;
let num = 0;
console.log("Find the max txs count between", program.startNum, "and", program.endNum);
for (let i = program.startNum; i < program.endNum; i++) {
if (program.miner !== "") {
let blockData = await provider.getBlock(Number(i))
if (program.miner !== blockData.miner) {
continue
}
}
let x = await provider.send("eth_getBlockTransactionCountByNumber", [
ethers.toQuantity(i)]);
let a = ethers.toNumber(x)
if (a > txCount) {
num = i;
txCount = a;
}
}
console.log("BlockNum = ", num, "TxCount =", txCount);
};
main().then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -1,38 +0,0 @@
import { ethers } from "ethers";
import program from "commander";
program.option("--Rpc <Rpc>", "Rpc");
program.option("--Num <Num>", "validator num", 21)
program.parse(process.argv);
const provider = new ethers.JsonRpcProvider(program.Rpc);
const main = async () => {
const blockNum = await provider.getBlockNumber();
console.log(blockNum);
for (let i = 0; i < program.Num; i++) {
let blockData = await provider.getBlock(blockNum - i);
// 1.get Geth client version
let major = ethers.toNumber(ethers.dataSlice(blockData.extraData, 2, 3))
let minor = ethers.toNumber(ethers.dataSlice(blockData.extraData, 3, 4))
let patch = ethers.toNumber(ethers.dataSlice(blockData.extraData, 4, 5))
// 2.get minimum txGasPrice based on the last non-zero-gasprice transaction
let lastGasPrice = 0
for (let txIndex = blockData.transactions.length - 1; txIndex >= 0; txIndex--) {
let txHash = blockData.transactions[txIndex]
let txData = await provider.getTransaction(txHash);
if (txData.gasPrice == 0) {
continue
}
lastGasPrice = txData.gasPrice
break
}
console.log(blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice)
}
};
main().then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -35,8 +35,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "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"
"github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@@ -305,9 +308,19 @@ var (
Usage: "Manually specify the Rialto Genesis Hash, to trigger builtin network logic", Usage: "Manually specify the Rialto Genesis Hash, to trigger builtin network logic",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
OverrideBohr = &cli.Uint64Flag{ OverridePassedForkTime = &cli.Uint64Flag{
Name: "override.bohr", Name: "override.passedforktime",
Usage: "Manually specify the Bohr fork timestamp, overriding the bundled setting", Usage: "Manually specify the hard fork timestamps which have passed on the mainnet, overriding the bundled setting",
Category: flags.EthCategory,
}
OverridePascal = &cli.Uint64Flag{
Name: "override.pascal",
Usage: "Manually specify the Pascal fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
OverridePrague = &cli.Uint64Flag{
Name: "override.prague",
Usage: "Manually specify the Prague fork timestamp, overriding the bundled setting",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
OverrideVerkle = &cli.Uint64Flag{ OverrideVerkle = &cli.Uint64Flag{
@@ -339,6 +352,12 @@ var (
Value: params.BreatheBlockInterval, Value: params.BreatheBlockInterval,
Category: flags.EthCategory, Category: flags.EthCategory,
} }
OverrideFixedTurnLength = &cli.Uint64Flag{
Name: "override.fixedturnlength",
Usage: "It use fixed or random values for turn length instead of reading from the contract, only for testing purpose",
Value: params.FixedTurnLength,
Category: flags.EthCategory,
}
SyncModeFlag = &flags.TextMarshalerFlag{ SyncModeFlag = &flags.TextMarshalerFlag{
Name: "syncmode", Name: "syncmode",
Usage: `Blockchain sync mode ("snap" or "full")`, Usage: `Blockchain sync mode ("snap" or "full")`,
@@ -1135,6 +1154,25 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Value: params.DefaultExtraReserveForBlobRequests, Value: params.DefaultExtraReserveForBlobRequests,
Category: flags.MiscCategory, 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 ( var (
@@ -2286,6 +2324,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) { func SetupMetrics(ctx *cli.Context, options ...SetupMetricsOption) {
if metrics.Enabled { if metrics.Enabled {
log.Info("Enabling metrics collection") log.Info("Enabling metrics collection")

View File

@@ -2306,6 +2306,19 @@ const validatorSetABI = `
], ],
"stateMutability": "view" "stateMutability": "view"
}, },
{
"inputs": [],
"name": "getTurnLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"type": "function", "type": "function",
"name": "getValidators", "name": "getValidators",

View File

@@ -88,6 +88,19 @@ func (api *API) GetJustifiedNumber(number *rpc.BlockNumber) (uint64, error) {
return snap.Attestation.TargetNumber, nil return snap.Attestation.TargetNumber, nil
} }
func (api *API) GetTurnLength(number *rpc.BlockNumber) (uint8, error) {
header := api.getHeader(number)
// Ensure we have an actually valid block and return the validators from its snapshot
if header == nil {
return 0, errUnknownBlock
}
snap, err := api.parlia.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
if err != nil || snap.TurnLength == 0 {
return 0, err
}
return snap.TurnLength, nil
}
func (api *API) GetFinalizedNumber(number *rpc.BlockNumber) (uint64, error) { func (api *API) GetFinalizedNumber(number *rpc.BlockNumber) (uint64, error) {
header := api.getHeader(number) header := api.getHeader(number)
// Ensure we have an actually valid block and return the validators from its snapshot // Ensure we have an actually valid block and return the validators from its snapshot

View File

@@ -0,0 +1,91 @@
package parlia
import (
"context"
"errors"
"math/big"
mrand "math/rand"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
func (p *Parlia) getTurnLength(chain consensus.ChainHeaderReader, header *types.Header) (*uint8, error) {
parent := chain.GetHeaderByHash(header.ParentHash)
if parent == nil {
return nil, errors.New("parent not found")
}
var turnLength uint8
if p.chainConfig.IsBohr(parent.Number, parent.Time) {
turnLengthFromContract, err := p.getTurnLengthFromContract(parent)
if err != nil {
return nil, err
}
if turnLengthFromContract == nil {
return nil, errors.New("unexpected error when getTurnLengthFromContract")
}
turnLength = uint8(turnLengthFromContract.Int64())
} else {
turnLength = defaultTurnLength
}
log.Debug("getTurnLength", "turnLength", turnLength)
return &turnLength, nil
}
func (p *Parlia) getTurnLengthFromContract(header *types.Header) (turnLength *big.Int, err error) {
// mock to get turnLength from the contract
if params.FixedTurnLength >= 1 && params.FixedTurnLength <= 9 {
if params.FixedTurnLength == 2 {
return p.getRandTurnLength(header)
}
return big.NewInt(int64(params.FixedTurnLength)), nil
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
method := "getTurnLength"
toAddress := common.HexToAddress(systemcontracts.ValidatorContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
data, err := p.validatorSetABI.Pack(method)
if err != nil {
log.Error("Unable to pack tx for getTurnLength", "error", err)
return nil, err
}
msgData := (hexutil.Bytes)(data)
blockNr := rpc.BlockNumberOrHashWithHash(header.Hash(), false)
result, err := p.ethAPI.Call(ctx, ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, &blockNr, nil, nil)
if err != nil {
return nil, err
}
if err := p.validatorSetABI.UnpackIntoInterface(&turnLength, method, result); err != nil {
return nil, err
}
return turnLength, nil
}
// getRandTurnLength returns a random valid value, used to test switching turn length
func (p *Parlia) getRandTurnLength(header *types.Header) (turnLength *big.Int, err error) {
turnLengths := [8]uint8{1, 3, 4, 5, 6, 7, 8, 9}
r := mrand.New(mrand.NewSource(int64(header.Time)))
lengthIndex := int(r.Int31n(int32(len(turnLengths))))
return big.NewInt(int64(turnLengths[lengthIndex])), nil
}

View File

@@ -54,11 +54,13 @@ const (
inMemoryHeaders = 86400 // Number of recent headers to keep in memory for double sign detection, inMemoryHeaders = 86400 // Number of recent headers to keep in memory for double sign detection,
checkpointInterval = 1024 // Number of blocks after which to save the snapshot to the database checkpointInterval = 1024 // Number of blocks after which to save the snapshot to the database
defaultEpochLength = uint64(100) // Default number of blocks of checkpoint to update validatorSet from contract defaultEpochLength = uint64(200) // Default number of blocks of checkpoint to update validatorSet from contract
defaultTurnLength = uint8(1) // Default consecutive number of blocks a validator receives priority for block production
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
nextForkHashSize = 4 // Fixed number of extra-data suffix bytes reserved for nextForkHash. nextForkHashSize = 4 // Fixed number of extra-data suffix bytes reserved for nextForkHash.
turnLengthSize = 1 // Fixed number of extra-data suffix bytes reserved for turnLength
validatorBytesLengthBeforeLuban = common.AddressLength validatorBytesLengthBeforeLuban = common.AddressLength
validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength
@@ -66,7 +68,6 @@ const (
wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
initialBackOffTime = uint64(1) // second 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 systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system
@@ -127,6 +128,10 @@ var (
// invalid list of validators (i.e. non divisible by 20 bytes). // invalid list of validators (i.e. non divisible by 20 bytes).
errInvalidSpanValidators = errors.New("invalid validator list on sprint end block") errInvalidSpanValidators = errors.New("invalid validator list on sprint end block")
// errInvalidTurnLength is returned if a block contains an
// invalid length of turn (i.e. no data left after parsing validators).
errInvalidTurnLength = errors.New("invalid turnLength")
// errInvalidMixDigest is returned if a block's mix digest is non-zero. // errInvalidMixDigest is returned if a block's mix digest is non-zero.
errInvalidMixDigest = errors.New("non-zero mix digest") errInvalidMixDigest = errors.New("non-zero mix digest")
@@ -137,6 +142,10 @@ var (
// list of validators different than the one the local node calculated. // list of validators different than the one the local node calculated.
errMismatchingEpochValidators = errors.New("mismatching validator list on epoch block") errMismatchingEpochValidators = errors.New("mismatching validator list on epoch block")
// errMismatchingEpochTurnLength is returned if a sprint block contains a
// turn length different than the one the local node calculated.
errMismatchingEpochTurnLength = errors.New("mismatching turn length on epoch block")
// errInvalidDifficulty is returned if the difficulty of a block is missing. // errInvalidDifficulty is returned if the difficulty of a block is missing.
errInvalidDifficulty = errors.New("invalid difficulty") errInvalidDifficulty = errors.New("invalid difficulty")
@@ -370,6 +379,7 @@ func (p *Parlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*typ
// On luban fork, we introduce vote attestation into the header's extra field, so extra format is different from before. // On luban fork, we introduce vote attestation into the header's extra field, so extra format is different from before.
// Before luban fork: |---Extra Vanity---|---Validators Bytes (or Empty)---|---Extra Seal---| // Before luban fork: |---Extra Vanity---|---Validators Bytes (or Empty)---|---Extra Seal---|
// After luban fork: |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---| // After luban fork: |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---|
// After bohr fork: |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Turn Length (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---|
func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) []byte { func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) []byte {
if len(header.Extra) <= extraVanity+extraSeal { if len(header.Extra) <= extraVanity+extraSeal {
return nil return nil
@@ -386,11 +396,15 @@ func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.Chain
return nil return nil
} }
num := int(header.Extra[extraVanity]) num := int(header.Extra[extraVanity])
if num == 0 || len(header.Extra) <= extraVanity+extraSeal+num*validatorBytesLength {
return nil
}
start := extraVanity + validatorNumberSize start := extraVanity + validatorNumberSize
end := start + num*validatorBytesLength end := start + num*validatorBytesLength
extraMinLen := end + extraSeal
if chainConfig.IsBohr(header.Number, header.Time) {
extraMinLen += turnLengthSize
}
if num == 0 || len(header.Extra) < extraMinLen {
return nil
}
return header.Extra[start:end] return header.Extra[start:end]
} }
@@ -409,11 +423,14 @@ func getVoteAttestationFromHeader(header *types.Header, chainConfig *params.Chai
attestationBytes = header.Extra[extraVanity : len(header.Extra)-extraSeal] attestationBytes = header.Extra[extraVanity : len(header.Extra)-extraSeal]
} else { } else {
num := int(header.Extra[extraVanity]) num := int(header.Extra[extraVanity])
if len(header.Extra) <= extraVanity+extraSeal+validatorNumberSize+num*validatorBytesLength { start := extraVanity + validatorNumberSize + num*validatorBytesLength
if chainConfig.IsBohr(header.Number, header.Time) {
start += turnLengthSize
}
end := len(header.Extra) - extraSeal
if end <= start {
return nil, nil return nil, nil
} }
start := extraVanity + validatorNumberSize + num*validatorBytesLength
end := len(header.Extra) - extraSeal
attestationBytes = header.Extra[start:end] attestationBytes = header.Extra[start:end]
} }
@@ -721,13 +738,28 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
} }
} }
// If we're at the genesis, snapshot the initial state. // If we're at the genesis, snapshot the initial state. Alternatively if we have
// piled up more headers than allowed to be reorged (chain reinit from a freezer),
// consider the checkpoint trusted and snapshot it.
// An offset `p.config.Epoch - 1` can ensure getting the right validators.
if number == 0 || ((number+1)%p.config.Epoch == 0 && (len(headers) > int(params.FullImmutabilityThreshold))) {
var (
checkpoint *types.Header
blockHash common.Hash
)
if number == 0 { if number == 0 {
checkpoint := chain.GetHeaderByNumber(number) checkpoint = chain.GetHeaderByNumber(0)
if checkpoint != nil { if checkpoint != nil {
// get checkpoint data blockHash = checkpoint.Hash()
hash := checkpoint.Hash() }
} else {
checkpoint = chain.GetHeaderByNumber(number + 1 - p.config.Epoch)
blockHeader := chain.GetHeaderByNumber(number)
if blockHeader != nil {
blockHash = blockHeader.Hash()
}
}
if checkpoint != nil && blockHash != (common.Hash{}) {
// get validators from headers // get validators from headers
validators, voteAddrs, err := parseValidators(checkpoint, p.chainConfig, p.config) validators, voteAddrs, err := parseValidators(checkpoint, p.chainConfig, p.config)
if err != nil { if err != nil {
@@ -735,11 +767,27 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
} }
// new snapshot // new snapshot
snap = newSnapshot(p.config, p.signatures, number, hash, validators, voteAddrs, p.ethAPI) snap = newSnapshot(p.config, p.signatures, number, blockHash, validators, voteAddrs, p.ethAPI)
// get turnLength from headers and use that for new turnLength
turnLength, err := parseTurnLength(checkpoint, p.chainConfig, p.config)
if err != nil {
return nil, err
}
if turnLength != nil {
snap.TurnLength = *turnLength
}
// snap.Recents is currently empty, which affects the following:
// a. The function SignRecently - This is acceptable since an empty snap.Recents results in a more lenient check.
// b. The function blockTimeVerifyForRamanujanFork - This is also acceptable as it won't be invoked during `snap.apply`.
// c. This may cause a mismatch in the slash systemtx, but the transaction list is not verified during `snap.apply`.
// snap.Attestation is nil, but Snapshot.updateAttestation will handle it correctly.
if err := snap.store(p.db); err != nil { if err := snap.store(p.db); err != nil {
return nil, err return nil, err
} }
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash) log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", blockHash)
break break
} }
} }
@@ -896,6 +944,24 @@ func (p *Parlia) prepareValidators(header *types.Header) error {
return nil return nil
} }
func (p *Parlia) prepareTurnLength(chain consensus.ChainHeaderReader, header *types.Header) error {
if header.Number.Uint64()%p.config.Epoch != 0 ||
!p.chainConfig.IsBohr(header.Number, header.Time) {
return nil
}
turnLength, err := p.getTurnLength(chain, header)
if err != nil {
return err
}
if turnLength != nil {
header.Extra = append(header.Extra, *turnLength)
}
return nil
}
func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header) error { func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header) error {
if !p.chainConfig.IsLuban(header.Number) || header.Number.Uint64() < 2 { if !p.chainConfig.IsLuban(header.Number) || header.Number.Uint64() < 2 {
return nil return nil
@@ -1027,6 +1093,9 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header
return err return err
} }
if err := p.prepareTurnLength(chain, header); err != nil {
return err
}
// add extra seal space // add extra seal space
header.Extra = append(header.Extra, make([]byte, extraSeal)...) header.Extra = append(header.Extra, make([]byte, extraSeal)...)
@@ -1077,6 +1146,30 @@ func (p *Parlia) verifyValidators(header *types.Header) error {
return nil return nil
} }
func (p *Parlia) verifyTurnLength(chain consensus.ChainHeaderReader, header *types.Header) error {
if header.Number.Uint64()%p.config.Epoch != 0 ||
!p.chainConfig.IsBohr(header.Number, header.Time) {
return nil
}
turnLengthFromHeader, err := parseTurnLength(header, p.chainConfig, p.config)
if err != nil {
return err
}
if turnLengthFromHeader != nil {
turnLength, err := p.getTurnLength(chain, header)
if err != nil {
return err
}
if turnLength != nil && *turnLength == *turnLengthFromHeader {
log.Debug("verifyTurnLength", "turnLength", *turnLength)
return nil
}
}
return errMismatchingEpochTurnLength
}
func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header, func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header,
cx core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, cx core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction,
usedGas *uint64, mining bool) error { usedGas *uint64, mining bool) error {
@@ -1171,6 +1264,10 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
return err return err
} }
if err := p.verifyTurnLength(chain, header); err != nil {
return err
}
cx := chainContext{Chain: chain, parlia: p} cx := chainContext{Chain: chain, parlia: p}
parent := chain.GetHeaderByHash(header.ParentHash) parent := chain.GetHeaderByHash(header.ParentHash)
@@ -1197,7 +1294,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
} }
} }
if header.Difficulty.Cmp(diffInTurn) != 0 { if header.Difficulty.Cmp(diffInTurn) != 0 {
spoiledVal := snap.supposeValidator() spoiledVal := snap.inturnValidator()
signedRecently := false signedRecently := false
if p.chainConfig.IsPlato(header.Number) { if p.chainConfig.IsPlato(header.Number) {
signedRecently = snap.SignRecently(spoiledVal) signedRecently = snap.SignRecently(spoiledVal)
@@ -1288,7 +1385,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
spoiledVal := snap.supposeValidator() spoiledVal := snap.inturnValidator()
signedRecently := false signedRecently := false
if p.chainConfig.IsPlato(header.Number) { if p.chainConfig.IsPlato(header.Number) {
signedRecently = snap.SignRecently(spoiledVal) signedRecently = snap.SignRecently(spoiledVal)
@@ -1441,10 +1538,13 @@ func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOv
delay = delay - *leftOver delay = delay - *leftOver
} }
// The blocking time should be no more than half of period // The blocking time should be no more than half of period when snap.TurnLength == 1
half := time.Duration(p.config.Period) * time.Second / 2 timeForMining := time.Duration(p.config.Period) * time.Second / 2
if delay > half { if !snap.lastBlockInOneTurn(header.Number.Uint64()) {
delay = half timeForMining = time.Duration(p.config.Period) * time.Second * 2 / 3
}
if delay > timeForMining {
delay = timeForMining
} }
return &delay return &delay
} }
@@ -1515,12 +1615,15 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res
copy(header.Extra[len(header.Extra)-extraSeal:], sig) copy(header.Extra[len(header.Extra)-extraSeal:], sig)
if p.shouldWaitForCurrentBlockProcess(chain, header, snap) { 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 { select {
case <-stop: case <-stop:
log.Info("Received block process finished, abort block seal") log.Info("Received block process finished, abort block seal")
return 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() { if chain.CurrentHeader().Number.Uint64() >= header.Number.Uint64() {
log.Info("Process backoff time exhausted, and current header has updated to abort this seal") log.Info("Process backoff time exhausted, and current header has updated to abort this seal")
return return
@@ -1936,42 +2039,40 @@ func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *t
// =========================== utility function ========================== // =========================== utility function ==========================
func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Address) uint64 { func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Address) uint64 {
if snap.inturn(val) { if snap.inturn(val) {
log.Debug("backOffTime", "blockNumber", header.Number, "in turn validator", val)
return 0 return 0
} else { } else {
delay := initialBackOffTime delay := initialBackOffTime
validators := snap.validators() validators := snap.validators()
if p.chainConfig.IsPlanck(header.Number) { if p.chainConfig.IsPlanck(header.Number) {
// reverse the key/value of snap.Recents to get recentsMap counts := snap.countRecents()
recentsMap := make(map[common.Address]uint64, len(snap.Recents)) for addr, seenTimes := range counts {
bound := uint64(0) log.Debug("backOffTime", "blockNumber", header.Number, "validator", addr, "seenTimes", seenTimes)
if n, limit := header.Number.Uint64(), uint64(len(validators)/2+1); n > limit {
bound = n - limit
}
for seen, recent := range snap.Recents {
if seen <= bound {
continue
}
recentsMap[recent] = seen
} }
// The backOffTime does not matter when a validator has signed recently. // The backOffTime does not matter when a validator has signed recently.
if _, ok := recentsMap[val]; ok { if snap.signRecentlyByCounts(val, counts) {
return 0 return 0
} }
inTurnAddr := validators[(snap.Number+1)%uint64(len(validators))] inTurnAddr := snap.inturnValidator()
if _, ok := recentsMap[inTurnAddr]; ok { if snap.signRecentlyByCounts(inTurnAddr, counts) {
log.Debug("in turn validator has recently signed, skip initialBackOffTime", log.Debug("in turn validator has recently signed, skip initialBackOffTime",
"inTurnAddr", inTurnAddr) "inTurnAddr", inTurnAddr)
delay = 0 delay = 0
} }
// Exclude the recently signed validators // Exclude the recently signed validators and the in turn validator
temp := make([]common.Address, 0, len(validators)) temp := make([]common.Address, 0, len(validators))
for _, addr := range validators { for _, addr := range validators {
if _, ok := recentsMap[addr]; ok { if snap.signRecentlyByCounts(addr, counts) {
continue continue
} }
if p.chainConfig.IsBohr(header.Number, header.Time) {
if addr == inTurnAddr {
continue
}
}
temp = append(temp, addr) temp = append(temp, addr)
} }
validators = temp validators = temp
@@ -1989,7 +2090,11 @@ func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Ad
return 0 return 0
} }
s := rand.NewSource(int64(snap.Number)) randSeed := snap.Number
if p.chainConfig.IsBohr(header.Number, header.Time) {
randSeed = header.Number.Uint64() / uint64(snap.TurnLength)
}
s := rand.NewSource(int64(randSeed))
r := rand.New(s) r := rand.New(s)
n := len(validators) n := len(validators)
backOffSteps := make([]uint64, 0, n) backOffSteps := make([]uint64, 0, n)

View File

@@ -22,22 +22,44 @@ func TestImpactOfValidatorOutOfService(t *testing.T) {
testCases := []struct { testCases := []struct {
totalValidators int totalValidators int
downValidators int downValidators int
turnLength int
}{ }{
{3, 1}, {3, 1, 1},
{5, 2}, {5, 2, 1},
{10, 1}, {10, 1, 2},
{10, 4}, {10, 4, 2},
{21, 1}, {21, 1, 3},
{21, 3}, {21, 3, 3},
{21, 5}, {21, 5, 4},
{21, 10}, {21, 10, 5},
} }
for _, tc := range testCases { for _, tc := range testCases {
simulateValidatorOutOfService(tc.totalValidators, tc.downValidators) simulateValidatorOutOfService(tc.totalValidators, tc.downValidators, tc.turnLength)
} }
} }
func simulateValidatorOutOfService(totalValidators int, downValidators int) { // refer Snapshot.SignRecently
func signRecently(idx int, recents map[uint64]int, turnLength int) bool {
recentSignTimes := 0
for _, signIdx := range recents {
if signIdx == idx {
recentSignTimes += 1
}
}
return recentSignTimes >= turnLength
}
// refer Snapshot.minerHistoryCheckLen
func minerHistoryCheckLen(totalValidators int, turnLength int) uint64 {
return uint64(totalValidators/2+1)*uint64(turnLength) - 1
}
// refer Snapshot.inturnValidator
func inturnValidator(totalValidators int, turnLength int, height int) int {
return height / turnLength % totalValidators
}
func simulateValidatorOutOfService(totalValidators int, downValidators int, turnLength int) {
downBlocks := 10000 downBlocks := 10000
recoverBlocks := 10000 recoverBlocks := 10000
recents := make(map[uint64]int) recents := make(map[uint64]int)
@@ -55,12 +77,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
delete(validators, down[i]) delete(validators, down[i])
} }
isRecentSign := func(idx int) bool { isRecentSign := func(idx int) bool {
for _, signIdx := range recents { return signRecently(idx, recents, turnLength)
if signIdx == idx {
return true
}
}
return false
} }
isInService := func(idx int) bool { isInService := func(idx int) bool {
return validators[idx] return validators[idx]
@@ -68,10 +85,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
downDelay := uint64(0) downDelay := uint64(0)
for h := 1; h <= downBlocks; h++ { for h := 1; h <= downBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit { if limit := minerHistoryCheckLen(totalValidators, turnLength) + 1; uint64(h) >= limit {
delete(recents, uint64(h)-limit) delete(recents, uint64(h)-limit)
} }
proposer := h % totalValidators proposer := inturnValidator(totalValidators, turnLength, h)
if !isInService(proposer) || isRecentSign(proposer) { if !isInService(proposer) || isRecentSign(proposer) {
candidates := make(map[int]bool, totalValidators/2) candidates := make(map[int]bool, totalValidators/2)
for v := range validators { for v := range validators {
@@ -99,10 +116,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
recoverDelay := uint64(0) recoverDelay := uint64(0)
lastseen := downBlocks lastseen := downBlocks
for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ { for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit { if limit := minerHistoryCheckLen(totalValidators, turnLength) + 1; uint64(h) >= limit {
delete(recents, uint64(h)-limit) delete(recents, uint64(h)-limit)
} }
proposer := h % totalValidators proposer := inturnValidator(totalValidators, turnLength, h)
if !isInService(proposer) || isRecentSign(proposer) { if !isInService(proposer) || isRecentSign(proposer) {
lastseen = h lastseen = h
candidates := make(map[int]bool, totalValidators/2) candidates := make(map[int]bool, totalValidators/2)

View File

@@ -44,6 +44,7 @@ type Snapshot struct {
Number uint64 `json:"number"` // Block number where the snapshot was created Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
TurnLength uint8 `json:"turn_length"` // Length of `turn`, meaning the consecutive number of blocks a validator receives priority for block production
Validators map[common.Address]*ValidatorInfo `json:"validators"` // Set of authorized validators at this moment Validators map[common.Address]*ValidatorInfo `json:"validators"` // Set of authorized validators at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent validators for spam protections Recents map[uint64]common.Address `json:"recents"` // Set of recent validators for spam protections
RecentForkHashes map[uint64]string `json:"recent_fork_hashes"` // Set of recent forkHash RecentForkHashes map[uint64]string `json:"recent_fork_hashes"` // Set of recent forkHash
@@ -73,6 +74,7 @@ func newSnapshot(
sigCache: sigCache, sigCache: sigCache,
Number: number, Number: number,
Hash: hash, Hash: hash,
TurnLength: defaultTurnLength,
Recents: make(map[uint64]common.Address), Recents: make(map[uint64]common.Address),
RecentForkHashes: make(map[uint64]string), RecentForkHashes: make(map[uint64]string),
Validators: make(map[common.Address]*ValidatorInfo), Validators: make(map[common.Address]*ValidatorInfo),
@@ -115,6 +117,10 @@ func loadSnapshot(config *params.ParliaConfig, sigCache *lru.ARCCache, db ethdb.
if err := json.Unmarshal(blob, snap); err != nil { if err := json.Unmarshal(blob, snap); err != nil {
return nil, err return nil, err
} }
if snap.TurnLength == 0 { // no TurnLength field in old snapshots
snap.TurnLength = defaultTurnLength
}
snap.config = config snap.config = config
snap.sigCache = sigCache snap.sigCache = sigCache
snap.ethAPI = ethAPI snap.ethAPI = ethAPI
@@ -139,6 +145,7 @@ func (s *Snapshot) copy() *Snapshot {
sigCache: s.sigCache, sigCache: s.sigCache,
Number: s.Number, Number: s.Number,
Hash: s.Hash, Hash: s.Hash,
TurnLength: s.TurnLength,
Validators: make(map[common.Address]*ValidatorInfo), Validators: make(map[common.Address]*ValidatorInfo),
Recents: make(map[uint64]common.Address), Recents: make(map[uint64]common.Address),
RecentForkHashes: make(map[uint64]string), RecentForkHashes: make(map[uint64]string),
@@ -211,17 +218,45 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C
} }
} }
func (s *Snapshot) SignRecently(validator common.Address) bool { func (s *Snapshot) versionHistoryCheckLen() uint64 {
return uint64(len(s.Validators)) * uint64(s.TurnLength)
}
func (s *Snapshot) minerHistoryCheckLen() uint64 {
return (uint64(len(s.Validators))/2+1)*uint64(s.TurnLength) - 1
}
func (s *Snapshot) countRecents() map[common.Address]uint8 {
leftHistoryBound := uint64(0) // the bound is excluded
checkHistoryLength := s.minerHistoryCheckLen()
if s.Number > checkHistoryLength {
leftHistoryBound = s.Number - checkHistoryLength
}
counts := make(map[common.Address]uint8, len(s.Validators))
for seen, recent := range s.Recents { for seen, recent := range s.Recents {
if recent == validator { if seen <= leftHistoryBound || recent == (common.Address{}) /*when seen == `epochKey`*/ {
if limit := uint64(len(s.Validators)/2 + 1); s.Number+1 < limit || seen > s.Number+1-limit { continue
}
counts[recent] += 1
}
return counts
}
func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint8) bool {
if seenTimes, ok := counts[validator]; ok && seenTimes >= s.TurnLength {
if seenTimes > s.TurnLength {
log.Warn("produce more blocks than expected!", "validator", validator, "seenTimes", seenTimes)
}
return true return true
} }
}
}
return false return false
} }
func (s *Snapshot) SignRecently(validator common.Address) bool {
return s.signRecentlyByCounts(validator, s.countRecents())
}
func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderReader, parents []*types.Header, chainConfig *params.ChainConfig) (*Snapshot, error) { func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderReader, parents []*types.Header, chainConfig *params.ChainConfig) (*Snapshot, error) {
// Allow passing in no headers for cleaner code // Allow passing in no headers for cleaner code
if len(headers) == 0 { if len(headers) == 0 {
@@ -248,10 +283,10 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
for _, header := range headers { for _, header := range headers {
number := header.Number.Uint64() number := header.Number.Uint64()
// Delete the oldest validator from the recent list to allow it signing again // Delete the oldest validator from the recent list to allow it signing again
if limit := uint64(len(snap.Validators)/2 + 1); number >= limit { if limit := snap.minerHistoryCheckLen() + 1; number >= limit {
delete(snap.Recents, number-limit) delete(snap.Recents, number-limit)
} }
if limit := uint64(len(snap.Validators)); number >= limit { if limit := snap.versionHistoryCheckLen(); number >= limit {
delete(snap.RecentForkHashes, number-limit) delete(snap.RecentForkHashes, number-limit)
} }
// Resolve the authorization key and check against signers // Resolve the authorization key and check against signers
@@ -262,16 +297,22 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
if _, ok := snap.Validators[validator]; !ok { if _, ok := snap.Validators[validator]; !ok {
return nil, errUnauthorizedValidator(validator.String()) return nil, errUnauthorizedValidator(validator.String())
} }
if chainConfig.IsBohr(header.Number, header.Time) {
if snap.SignRecently(validator) {
return nil, errRecentlySigned
}
} else {
for _, recent := range snap.Recents { for _, recent := range snap.Recents {
if recent == validator { if recent == validator {
return nil, errRecentlySigned return nil, errRecentlySigned
} }
} }
}
snap.Recents[number] = validator snap.Recents[number] = validator
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity]) snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
snap.updateAttestation(header, chainConfig, s.config) snap.updateAttestation(header, chainConfig, s.config)
// change validator set // change validator set
if number > 0 && number%s.config.Epoch == uint64(len(snap.Validators)/2) { if number > 0 && number%s.config.Epoch == snap.minerHistoryCheckLen() {
epochKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number epochKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number
if chainConfig.IsBohr(header.Number, header.Time) { if chainConfig.IsBohr(header.Number, header.Time) {
// after switching the validator set, snap.Validators may become larger, // after switching the validator set, snap.Validators may become larger,
@@ -281,11 +322,22 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
} }
} }
checkpointHeader := FindAncientHeader(header, uint64(len(snap.Validators)/2), chain, parents) checkpointHeader := FindAncientHeader(header, snap.minerHistoryCheckLen(), chain, parents)
if checkpointHeader == nil { if checkpointHeader == nil {
return nil, consensus.ErrUnknownAncestor return nil, consensus.ErrUnknownAncestor
} }
oldVersionsLen := snap.versionHistoryCheckLen()
// get turnLength from headers and use that for new turnLength
turnLength, err := parseTurnLength(checkpointHeader, chainConfig, s.config)
if err != nil {
return nil, err
}
if turnLength != nil {
snap.TurnLength = *turnLength
log.Debug("validator set switch", "turnLength", *turnLength)
}
// get validators from headers and use that for new validator set // get validators from headers and use that for new validator set
newValArr, voteAddrs, err := parseValidators(checkpointHeader, chainConfig, s.config) newValArr, voteAddrs, err := parseValidators(checkpointHeader, chainConfig, s.config)
if err != nil { if err != nil {
@@ -315,13 +367,6 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
} }
} }
} }
oldLimit := len(snap.Validators)
newLimit := len(newVals)
if newLimit < oldLimit {
for i := 0; i < oldLimit-newLimit; i++ {
delete(snap.RecentForkHashes, number-uint64(newLimit)-uint64(i))
}
}
snap.Validators = newVals snap.Validators = newVals
if chainConfig.IsLuban(header.Number) { if chainConfig.IsLuban(header.Number) {
validators := snap.validators() validators := snap.validators()
@@ -329,6 +374,9 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
snap.Validators[val].Index = idx + 1 // offset by 1 snap.Validators[val].Index = idx + 1 // offset by 1
} }
} }
for i := snap.versionHistoryCheckLen(); i < oldVersionsLen; i++ {
delete(snap.RecentForkHashes, number-i)
}
} }
} }
snap.Number += uint64(len(headers)) snap.Number += uint64(len(headers))
@@ -346,17 +394,20 @@ func (s *Snapshot) validators() []common.Address {
return validators return validators
} }
// inturn returns if a validator at a given block height is in-turn or not. // lastBlockInOneTurn returns if the block at height `blockNumber` is the last block in current turn.
func (s *Snapshot) inturn(validator common.Address) bool { func (s *Snapshot) lastBlockInOneTurn(blockNumber uint64) bool {
validators := s.validators() return (blockNumber+1)%uint64(s.TurnLength) == 0
offset := (s.Number + 1) % uint64(len(validators))
return validators[offset] == validator
} }
// inturnValidator returns the validator at a given block height. // inturn returns if a validator at a given block height is in-turn or not.
func (s *Snapshot) inturn(validator common.Address) bool {
return s.inturnValidator() == validator
}
// inturnValidator returns the validator for the following block height.
func (s *Snapshot) inturnValidator() common.Address { func (s *Snapshot) inturnValidator() common.Address {
validators := s.validators() validators := s.validators()
offset := (s.Number + 1) % uint64(len(validators)) offset := (s.Number + 1) / uint64(s.TurnLength) % uint64(len(validators))
return validators[offset] return validators[offset]
} }
@@ -394,12 +445,6 @@ func (s *Snapshot) indexOfVal(validator common.Address) int {
return -1 return -1
} }
func (s *Snapshot) supposeValidator() common.Address {
validators := s.validators()
index := (s.Number + 1) % uint64(len(validators))
return validators[index]
}
func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) ([]common.Address, []types.BLSPublicKey, error) { func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) ([]common.Address, []types.BLSPublicKey, error) {
validatorsBytes := getValidatorBytesFromHeader(header, chainConfig, parliaConfig) validatorsBytes := getValidatorBytesFromHeader(header, chainConfig, parliaConfig)
if len(validatorsBytes) == 0 { if len(validatorsBytes) == 0 {
@@ -425,6 +470,24 @@ func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parl
return cnsAddrs, voteAddrs, nil return cnsAddrs, voteAddrs, nil
} }
func parseTurnLength(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) (*uint8, error) {
if header.Number.Uint64()%parliaConfig.Epoch != 0 ||
!chainConfig.IsBohr(header.Number, header.Time) {
return nil, nil
}
if len(header.Extra) <= extraVanity+extraSeal {
return nil, errInvalidSpanValidators
}
num := int(header.Extra[extraVanity])
pos := extraVanity + validatorNumberSize + num*validatorBytesLength
if len(header.Extra) <= pos {
return nil, errInvalidTurnLength
}
turnLength := header.Extra[pos]
return &turnLength, nil
}
func FindAncientHeader(header *types.Header, ite uint64, chain consensus.ChainHeaderReader, candidateParents []*types.Header) *types.Header { func FindAncientHeader(header *types.Header, ite uint64, chain consensus.ChainHeaderReader, candidateParents []*types.Header) *types.Header {
ancient := header ancient := header
for i := uint64(1); i <= ite; i++ { for i := uint64(1); i <= ite; i++ {

View File

@@ -66,31 +66,6 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
return validator 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 // ValidateBody validates the given block's uncles and verifies the block
// header's transaction and uncle roots. The headers are assumed to be already // header's transaction and uncle roots. The headers are assumed to be already
// validated at this point. // 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 { if err := v.engine.VerifyUncles(v.bc, block); err != nil {
return err 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{ validateFuns := []func() error{
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 { 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. // Blob transactions may be present after the Cancun fork.
var blobs int var blobs int
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {

View File

@@ -100,6 +100,8 @@ var (
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
blockRecvTimeDiffGauge = metrics.NewRegisteredGauge("chain/block/recvtimediff", nil)
errStateRootVerificationFailed = errors.New("state root verification failed") errStateRootVerificationFailed = errors.New("state root verification failed")
errInsertionInterrupted = errors.New("insertion is interrupted") errInsertionInterrupted = errors.New("insertion is interrupted")
errChainStopped = errors.New("blockchain is stopped") errChainStopped = errors.New("blockchain is stopped")
@@ -1393,7 +1395,7 @@ func (bc *BlockChain) Stop() {
if !bc.cacheConfig.TrieDirtyDisabled { if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.triedb triedb := bc.triedb
var once sync.Once var once sync.Once
for _, offset := range []uint64{0, 1, TriesInMemory - 1} { for _, offset := range []uint64{0, 1, bc.TriesInMemory() - 1} {
if number := bc.CurrentBlock().Number.Uint64(); number > offset { if number := bc.CurrentBlock().Number.Uint64(); number > offset {
recent := bc.GetBlockByNumber(number - offset) recent := bc.GetBlockByNumber(number - offset)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root()) log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
@@ -1801,6 +1803,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
if err := blockBatch.Write(); err != nil { if err := blockBatch.Write(); err != nil {
log.Crit("Failed to write block into disk", "err", err) log.Crit("Failed to write block into disk", "err", err)
} }
bc.hc.tdCache.Add(block.Hash(), externTd)
bc.blockCache.Add(block.Hash(), block)
bc.cacheReceipts(block.Hash(), receipts, block)
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
bc.sidecarsCache.Add(block.Hash(), block.Sidecars())
}
wg.Done() wg.Done()
}() }()
@@ -1825,7 +1833,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
// Flush limits are not considered for the first TriesInMemory blocks. // Flush limits are not considered for the first TriesInMemory blocks.
current := block.NumberU64() current := block.NumberU64()
if current <= TriesInMemory { if current <= bc.TriesInMemory() {
return nil return nil
} }
// If we exceeded our memory allowance, flush matured singleton nodes to disk // If we exceeded our memory allowance, flush matured singleton nodes to disk
@@ -1911,18 +1919,18 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
// WriteBlockAndSetHead writes the given block and all associated state to the database, // WriteBlockAndSetHead writes the given block and all associated state to the database,
// and applies the block as the new chain head. // and applies the block as the new chain head.
func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool, mux *event.TypeMux) (status WriteStatus, err error) { func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
if !bc.chainmu.TryLock() { if !bc.chainmu.TryLock() {
return NonStatTy, errChainStopped return NonStatTy, errChainStopped
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent, mux) return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent)
} }
// writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead. // writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead.
// This function expects the chain mutex to be held. // This function expects the chain mutex to be held.
func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool, mux *event.TypeMux) (status WriteStatus, err error) { func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
currentBlock := bc.CurrentBlock() currentBlock := bc.CurrentBlock()
reorg, err := bc.forker.ReorgNeededWithFastFinality(currentBlock, block.Header()) reorg, err := bc.forker.ReorgNeededWithFastFinality(currentBlock, block.Header())
if err != nil { if err != nil {
@@ -1931,9 +1939,6 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
if reorg { if reorg {
bc.highestVerifiedBlock.Store(types.CopyHeader(block.Header())) bc.highestVerifiedBlock.Store(types.CopyHeader(block.Header()))
bc.highestVerifiedBlockFeed.Send(HighestVerifiedBlockEvent{Header: block.Header()}) bc.highestVerifiedBlockFeed.Send(HighestVerifiedBlockEvent{Header: block.Header()})
if mux != nil {
mux.Post(NewSealedBlockEvent{Block: block})
}
} }
if err := bc.writeBlockWithState(block, receipts, state); err != nil { if err := bc.writeBlockWithState(block, receipts, state); err != nil {
@@ -2052,6 +2057,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
return 0, nil 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) // 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()) signer := types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time())
go SenderCacher.RecoverFromBlocks(signer, chain) go SenderCacher.RecoverFromBlocks(signer, chain)
@@ -2311,14 +2319,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// Don't set the head, only insert the block // Don't set the head, only insert the block
err = bc.writeBlockWithState(block, receipts, statedb) err = bc.writeBlockWithState(block, receipts, statedb)
} else { } else {
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false, nil) status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
} }
if err != nil { if err != nil {
return it.index, err return it.index, err
} }
bc.cacheReceipts(block.Hash(), receipts, block)
// Update the metrics touched during block commit // Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them

View File

@@ -27,10 +27,7 @@ type NewTxsEvent struct{ Txs []*types.Transaction }
// ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration. // ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration.
type ReannoTxsEvent struct{ Txs []*types.Transaction } type ReannoTxsEvent struct{ Txs []*types.Transaction }
// NewSealedBlockEvent is posted when a block has been sealed. // NewMinedBlockEvent is posted when a block has been imported.
type NewSealedBlockEvent struct{ Block *types.Block }
// NewMinedBlockEvent is posted when a block has been mined.
type NewMinedBlockEvent struct{ Block *types.Block } type NewMinedBlockEvent struct{ Block *types.Block }
// RemovedLogsEvent is posted when a reorg happens // RemovedLogsEvent is posted when a reorg happens

View File

@@ -121,12 +121,19 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b
if f.preserve != nil { if f.preserve != nil {
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern) currentPreserve, externPreserve = f.preserve(current), f.preserve(extern)
} }
choiceRules := func() bool {
if extern.Time == current.Time {
doubleSign := (extern.Coinbase == current.Coinbase) doubleSign := (extern.Coinbase == current.Coinbase)
reorg = !currentPreserve && (externPreserve || if doubleSign {
extern.Time < current.Time || return extern.Hash().Cmp(current.Hash()) < 0
extern.Time == current.Time && } else {
((doubleSign && extern.Hash().Cmp(current.Hash()) < 0) || return f.rand.Float64() < 0.5
(!doubleSign && f.rand.Float64() < 0.5))) }
} else {
return extern.Time < current.Time
}
}
reorg = !currentPreserve && (externPreserve || choiceRules())
} }
return reorg, nil return reorg, nil
} }

View File

@@ -216,7 +216,9 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config // ChainOverrides contains the changes to chain config
// Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes. // Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes.
type ChainOverrides struct { type ChainOverrides struct {
OverrideBohr *uint64 OverridePassedForkTime *uint64
OverridePascal *uint64
OverridePrague *uint64
OverrideVerkle *uint64 OverrideVerkle *uint64
} }
@@ -243,8 +245,21 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
} }
applyOverrides := func(config *params.ChainConfig) { applyOverrides := func(config *params.ChainConfig) {
if config != nil { if config != nil {
if overrides != nil && overrides.OverrideBohr != nil { if overrides != nil && overrides.OverridePassedForkTime != nil {
config.BohrTime = overrides.OverrideBohr config.ShanghaiTime = overrides.OverridePassedForkTime
config.KeplerTime = overrides.OverridePassedForkTime
config.FeynmanTime = overrides.OverridePassedForkTime
config.FeynmanFixTime = overrides.OverridePassedForkTime
config.CancunTime = overrides.OverridePassedForkTime
config.HaberTime = overrides.OverridePassedForkTime
config.HaberFixTime = overrides.OverridePassedForkTime
config.BohrTime = overrides.OverridePassedForkTime
}
if overrides != nil && overrides.OverridePascal != nil {
config.PascalTime = overrides.OverridePascal
}
if overrides != nil && overrides.OverridePrague != nil {
config.PragueTime = overrides.OverridePrague
} }
if overrides != nil && overrides.OverrideVerkle != nil { if overrides != nil && overrides.OverrideVerkle != nil {
config.VerkleTime = overrides.OverrideVerkle config.VerkleTime = overrides.OverrideVerkle

View File

@@ -68,6 +68,7 @@ func newPrunedFreezer(datadir string, db ethdb.KeyValueStore, offset uint64) (*p
// repair init frozen , compatible disk-ancientdb and pruner-block-tool. // repair init frozen , compatible disk-ancientdb and pruner-block-tool.
func (f *prunedfreezer) repair(datadir string) error { func (f *prunedfreezer) repair(datadir string) error {
offset := atomic.LoadUint64(&f.frozen)
// compatible freezer // compatible freezer
minItems := uint64(math.MaxUint64) minItems := uint64(math.MaxUint64)
for name, disableSnappy := range chainFreezerNoSnappy { for name, disableSnappy := range chainFreezerNoSnappy {
@@ -96,19 +97,14 @@ func (f *prunedfreezer) repair(datadir string) error {
table.Close() table.Close()
} }
// If minItems is non-zero, it indicates that the chain freezer was previously enabled, and we should use minItems as the current frozen value. // If the dataset has undergone a prune block, the offset is a non-zero value, otherwise the offset is a zero value.
// If minItems is zero, it indicates that the pruneAncient was previously enabled, and we should continue using frozen // The minItems is the value relative to offset
// (retrieved from CurrentAncientFreezer) as the current frozen value. offset += minItems
offset := minItems
if offset == 0 {
// no item in ancientDB, init `offset` to the `f.frozen`
offset = atomic.LoadUint64(&f.frozen)
}
log.Info("Read ancientdb item counts", "items", minItems, "offset", offset)
// FrozenOfAncientFreezer is the progress of the last prune-freezer freeze. // FrozenOfAncientFreezer is the progress of the last prune-freezer freeze.
frozenInDB := ReadFrozenOfAncientFreezer(f.db) frozenInDB := ReadFrozenOfAncientFreezer(f.db)
maxOffset := max(offset, frozenInDB) maxOffset := max(offset, frozenInDB)
log.Info("Read ancient db item counts", "items", minItems, "frozen", maxOffset)
atomic.StoreUint64(&f.frozen, maxOffset) atomic.StoreUint64(&f.frozen, maxOffset)
if err := f.Sync(); err != nil { if err := f.Sync(); err != nil {
@@ -161,12 +157,12 @@ func (f *prunedfreezer) AncientOffSet() uint64 {
// MigrateTable processes the entries in a given table in sequence // MigrateTable processes the entries in a given table in sequence
// converting them to a new format if they're of an old format. // converting them to a new format if they're of an old format.
func (db *prunedfreezer) MigrateTable(kind string, convert convertLegacyFn) error { func (f *prunedfreezer) MigrateTable(kind string, convert convertLegacyFn) error {
return errNotSupported return errNotSupported
} }
// AncientDatadir returns an error as we don't have a backing chain freezer. // AncientDatadir returns an error as we don't have a backing chain freezer.
func (db *prunedfreezer) AncientDatadir() (string, error) { func (f *prunedfreezer) AncientDatadir() (string, error) {
return "", errNotSupported return "", errNotSupported
} }

View File

@@ -231,6 +231,13 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
// contract. This method is exported to be used in tests. // contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) { func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
// Return immediately if beaconRoot equals the zero hash when using the Parlia engine.
if beaconRoot == (common.Hash{}) {
if chainConfig := vmenv.ChainConfig(); chainConfig != nil && chainConfig.Parlia != nil {
return
}
}
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
// the new root // the new root
msg := &Message{ msg := &Message{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
package bohr
import _ "embed"
// contract codes for Mainnet upgrade
var (
//go:embed mainnet/ValidatorContract
MainnetValidatorContract string
//go:embed mainnet/StakeHubContract
MainnetStakeHubContract string
)
// contract codes for Chapel upgrade
var (
//go:embed chapel/ValidatorContract
ChapelValidatorContract string
//go:embed chapel/StakeHubContract
ChapelStakeHubContract string
)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,168 @@
package pascal
import _ "embed"
// contract codes for Mainnet upgrade
var (
//go:embed mainnet/ValidatorContract
MainnetValidatorContract string
//go:embed mainnet/SlashContract
MainnetSlashContract string
//go:embed mainnet/SystemRewardContract
MainnetSystemRewardContract string
//go:embed mainnet/LightClientContract
MainnetLightClientContract string
//go:embed mainnet/TokenHubContract
MainnetTokenHubContract string
//go:embed mainnet/RelayerIncentivizeContract
MainnetRelayerIncentivizeContract string
//go:embed mainnet/RelayerHubContract
MainnetRelayerHubContract string
//go:embed mainnet/GovHubContract
MainnetGovHubContract string
//go:embed mainnet/TokenManagerContract
MainnetTokenManagerContract string
//go:embed mainnet/CrossChainContract
MainnetCrossChainContract string
//go:embed mainnet/StakingContract
MainnetStakingContract string
//go:embed mainnet/StakeHubContract
MainnetStakeHubContract string
//go:embed mainnet/StakeCreditContract
MainnetStakeCreditContract string
//go:embed mainnet/GovernorContract
MainnetGovernorContract string
//go:embed mainnet/GovTokenContract
MainnetGovTokenContract string
//go:embed mainnet/TimelockContract
MainnetTimelockContract string
//go:embed mainnet/TokenRecoverPortalContract
MainnetTokenRecoverPortalContract string
)
// contract codes for Chapel upgrade
var (
//go:embed chapel/ValidatorContract
ChapelValidatorContract string
//go:embed chapel/SlashContract
ChapelSlashContract string
//go:embed chapel/SystemRewardContract
ChapelSystemRewardContract string
//go:embed chapel/LightClientContract
ChapelLightClientContract string
//go:embed chapel/TokenHubContract
ChapelTokenHubContract string
//go:embed chapel/RelayerIncentivizeContract
ChapelRelayerIncentivizeContract string
//go:embed chapel/RelayerHubContract
ChapelRelayerHubContract string
//go:embed chapel/GovHubContract
ChapelGovHubContract string
//go:embed chapel/TokenManagerContract
ChapelTokenManagerContract string
//go:embed chapel/CrossChainContract
ChapelCrossChainContract string
//go:embed chapel/StakingContract
ChapelStakingContract string
//go:embed chapel/StakeHubContract
ChapelStakeHubContract string
//go:embed chapel/StakeCreditContract
ChapelStakeCreditContract string
//go:embed chapel/GovernorContract
ChapelGovernorContract string
//go:embed chapel/GovTokenContract
ChapelGovTokenContract string
//go:embed chapel/TimelockContract
ChapelTimelockContract string
//go:embed chapel/TokenRecoverPortalContract
ChapelTokenRecoverPortalContract string
)
// contract codes for Rialto upgrade
var (
//go:embed rialto/ValidatorContract
RialtoValidatorContract string
//go:embed rialto/SlashContract
RialtoSlashContract string
//go:embed rialto/SystemRewardContract
RialtoSystemRewardContract string
//go:embed rialto/LightClientContract
RialtoLightClientContract string
//go:embed rialto/TokenHubContract
RialtoTokenHubContract string
//go:embed rialto/RelayerIncentivizeContract
RialtoRelayerIncentivizeContract string
//go:embed rialto/RelayerHubContract
RialtoRelayerHubContract string
//go:embed rialto/GovHubContract
RialtoGovHubContract string
//go:embed rialto/TokenManagerContract
RialtoTokenManagerContract string
//go:embed rialto/CrossChainContract
RialtoCrossChainContract string
//go:embed rialto/StakingContract
RialtoStakingContract string
//go:embed rialto/StakeHubContract
RialtoStakeHubContract string
//go:embed rialto/StakeCreditContract
RialtoStakeCreditContract string
//go:embed rialto/GovernorContract
RialtoGovernorContract string
//go:embed rialto/GovTokenContract
RialtoGovTokenContract string
//go:embed rialto/TimelockContract
RialtoTimelockContract string
//go:embed rialto/TokenRecoverPortalContract
RialtoTokenRecoverPortalContract string
)

View File

@@ -4,10 +4,11 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/big" "math/big"
"reflect"
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/systemcontracts/bohr"
"github.com/ethereum/go-ethereum/core/systemcontracts/bruno" "github.com/ethereum/go-ethereum/core/systemcontracts/bruno"
"github.com/ethereum/go-ethereum/core/systemcontracts/euler" "github.com/ethereum/go-ethereum/core/systemcontracts/euler"
"github.com/ethereum/go-ethereum/core/systemcontracts/feynman" "github.com/ethereum/go-ethereum/core/systemcontracts/feynman"
@@ -19,9 +20,11 @@ import (
"github.com/ethereum/go-ethereum/core/systemcontracts/mirror" "github.com/ethereum/go-ethereum/core/systemcontracts/mirror"
"github.com/ethereum/go-ethereum/core/systemcontracts/moran" "github.com/ethereum/go-ethereum/core/systemcontracts/moran"
"github.com/ethereum/go-ethereum/core/systemcontracts/niels" "github.com/ethereum/go-ethereum/core/systemcontracts/niels"
"github.com/ethereum/go-ethereum/core/systemcontracts/pascal"
"github.com/ethereum/go-ethereum/core/systemcontracts/planck" "github.com/ethereum/go-ethereum/core/systemcontracts/planck"
"github.com/ethereum/go-ethereum/core/systemcontracts/plato" "github.com/ethereum/go-ethereum/core/systemcontracts/plato"
"github.com/ethereum/go-ethereum/core/systemcontracts/ramanujan" "github.com/ethereum/go-ethereum/core/systemcontracts/ramanujan"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@@ -39,7 +42,7 @@ type Upgrade struct {
Configs []*UpgradeConfig Configs []*UpgradeConfig
} }
type upgradeHook func(blockNumber *big.Int, contractAddr common.Address, statedb *state.StateDB) error type upgradeHook func(blockNumber *big.Int, contractAddr common.Address, statedb vm.StateDB) error
const ( const (
mainNet = "Mainnet" mainNet = "Mainnet"
@@ -78,6 +81,10 @@ var (
feynmanFixUpgrade = make(map[string]*Upgrade) feynmanFixUpgrade = make(map[string]*Upgrade)
haberFixUpgrade = make(map[string]*Upgrade) haberFixUpgrade = make(map[string]*Upgrade)
bohrUpgrade = make(map[string]*Upgrade)
pascalUpgrade = make(map[string]*Upgrade)
) )
func init() { func init() {
@@ -736,12 +743,318 @@ func init() {
}, },
}, },
} }
bohrUpgrade[mainNet] = &Upgrade{
UpgradeName: "bohr",
Configs: []*UpgradeConfig{
{
ContractAddr: common.HexToAddress(ValidatorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
Code: bohr.MainnetValidatorContract,
},
{
ContractAddr: common.HexToAddress(StakeHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
Code: bohr.MainnetStakeHubContract,
},
},
} }
func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb *state.StateDB) { bohrUpgrade[chapelNet] = &Upgrade{
if config == nil || blockNumber == nil || statedb == nil { UpgradeName: "bohr",
Configs: []*UpgradeConfig{
{
ContractAddr: common.HexToAddress(ValidatorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
Code: bohr.ChapelValidatorContract,
},
{
ContractAddr: common.HexToAddress(StakeHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
Code: bohr.ChapelStakeHubContract,
},
},
}
pascalUpgrade[mainNet] = &Upgrade{
UpgradeName: "pascal",
Configs: []*UpgradeConfig{
{
ContractAddr: common.HexToAddress(ValidatorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetValidatorContract,
},
{
ContractAddr: common.HexToAddress(SlashContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetSlashContract,
},
{
ContractAddr: common.HexToAddress(SystemRewardContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetSystemRewardContract,
},
{
ContractAddr: common.HexToAddress(LightClientContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetLightClientContract,
},
{
ContractAddr: common.HexToAddress(TokenHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetTokenHubContract,
},
{
ContractAddr: common.HexToAddress(RelayerIncentivizeContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetRelayerIncentivizeContract,
},
{
ContractAddr: common.HexToAddress(RelayerHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetRelayerHubContract,
},
{
ContractAddr: common.HexToAddress(GovHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetGovHubContract,
},
{
ContractAddr: common.HexToAddress(TokenManagerContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetTokenManagerContract,
},
{
ContractAddr: common.HexToAddress(CrossChainContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetCrossChainContract,
},
{
ContractAddr: common.HexToAddress(StakingContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetStakingContract,
},
{
ContractAddr: common.HexToAddress(StakeHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetStakeHubContract,
},
{
ContractAddr: common.HexToAddress(StakeCreditContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetStakeCreditContract,
},
{
ContractAddr: common.HexToAddress(GovernorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetGovernorContract,
},
{
ContractAddr: common.HexToAddress(GovTokenContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetGovTokenContract,
},
{
ContractAddr: common.HexToAddress(TimelockContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetTimelockContract,
},
{
ContractAddr: common.HexToAddress(TokenRecoverPortalContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.MainnetTokenRecoverPortalContract,
},
},
}
pascalUpgrade[chapelNet] = &Upgrade{
UpgradeName: "pascal",
Configs: []*UpgradeConfig{
{
ContractAddr: common.HexToAddress(ValidatorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelValidatorContract,
},
{
ContractAddr: common.HexToAddress(SlashContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelSlashContract,
},
{
ContractAddr: common.HexToAddress(SystemRewardContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelSystemRewardContract,
},
{
ContractAddr: common.HexToAddress(LightClientContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelLightClientContract,
},
{
ContractAddr: common.HexToAddress(TokenHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelTokenHubContract,
},
{
ContractAddr: common.HexToAddress(RelayerIncentivizeContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelRelayerIncentivizeContract,
},
{
ContractAddr: common.HexToAddress(RelayerHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelRelayerHubContract,
},
{
ContractAddr: common.HexToAddress(GovHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelGovHubContract,
},
{
ContractAddr: common.HexToAddress(TokenManagerContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelTokenManagerContract,
},
{
ContractAddr: common.HexToAddress(CrossChainContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelCrossChainContract,
},
{
ContractAddr: common.HexToAddress(StakingContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelStakingContract,
},
{
ContractAddr: common.HexToAddress(StakeHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelStakeHubContract,
},
{
ContractAddr: common.HexToAddress(StakeCreditContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelStakeCreditContract,
},
{
ContractAddr: common.HexToAddress(GovernorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelGovernorContract,
},
{
ContractAddr: common.HexToAddress(GovTokenContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelGovTokenContract,
},
{
ContractAddr: common.HexToAddress(TimelockContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelTimelockContract,
},
{
ContractAddr: common.HexToAddress(TokenRecoverPortalContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.ChapelTokenRecoverPortalContract,
},
},
}
pascalUpgrade[rialtoNet] = &Upgrade{
UpgradeName: "pascal",
Configs: []*UpgradeConfig{
{
ContractAddr: common.HexToAddress(ValidatorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoValidatorContract,
},
{
ContractAddr: common.HexToAddress(SlashContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoSlashContract,
},
{
ContractAddr: common.HexToAddress(SystemRewardContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoSystemRewardContract,
},
{
ContractAddr: common.HexToAddress(LightClientContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoLightClientContract,
},
{
ContractAddr: common.HexToAddress(TokenHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoTokenHubContract,
},
{
ContractAddr: common.HexToAddress(RelayerIncentivizeContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoRelayerIncentivizeContract,
},
{
ContractAddr: common.HexToAddress(RelayerHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoRelayerHubContract,
},
{
ContractAddr: common.HexToAddress(GovHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoGovHubContract,
},
{
ContractAddr: common.HexToAddress(TokenManagerContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoTokenManagerContract,
},
{
ContractAddr: common.HexToAddress(CrossChainContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoCrossChainContract,
},
{
ContractAddr: common.HexToAddress(StakingContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoStakingContract,
},
{
ContractAddr: common.HexToAddress(StakeHubContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoStakeHubContract,
},
{
ContractAddr: common.HexToAddress(StakeCreditContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoStakeCreditContract,
},
{
ContractAddr: common.HexToAddress(GovernorContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoGovernorContract,
},
{
ContractAddr: common.HexToAddress(GovTokenContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoGovTokenContract,
},
{
ContractAddr: common.HexToAddress(TimelockContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoTimelockContract,
},
{
ContractAddr: common.HexToAddress(TokenRecoverPortalContract),
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/d1564095cca3bfbd303a5e3de2ad90e719d12564",
Code: pascal.RialtoTokenRecoverPortalContract,
},
},
}
}
func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb vm.StateDB) {
if config == nil || blockNumber == nil || statedb == nil || reflect.ValueOf(statedb).IsNil() {
return return
} }
var network string var network string
switch GenesisHash { switch GenesisHash {
/* Add mainnet genesis hash */ /* Add mainnet genesis hash */
@@ -816,12 +1129,20 @@ func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.I
applySystemContractUpgrade(haberFixUpgrade[network], blockNumber, statedb, logger) applySystemContractUpgrade(haberFixUpgrade[network], blockNumber, statedb, logger)
} }
if config.IsOnBohr(blockNumber, lastBlockTime, blockTime) {
applySystemContractUpgrade(bohrUpgrade[network], blockNumber, statedb, logger)
}
if config.IsOnPascal(blockNumber, lastBlockTime, blockTime) {
applySystemContractUpgrade(pascalUpgrade[network], blockNumber, statedb, logger)
}
/* /*
apply other upgrades apply other upgrades
*/ */
} }
func applySystemContractUpgrade(upgrade *Upgrade, blockNumber *big.Int, statedb *state.StateDB, logger log.Logger) { func applySystemContractUpgrade(upgrade *Upgrade, blockNumber *big.Int, statedb vm.StateDB, logger log.Logger) {
if upgrade == nil { if upgrade == nil {
logger.Info("Empty upgrade config", "height", blockNumber.String()) logger.Info("Empty upgrade config", "height", blockNumber.String())
return return

View File

@@ -2,9 +2,13 @@ package systemcontracts
import ( import (
"crypto/sha256" "crypto/sha256"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -39,3 +43,31 @@ func TestAllCodesHash(t *testing.T) {
allCodeHash := sha256.Sum256(allCodes) allCodeHash := sha256.Sum256(allCodes)
require.Equal(t, allCodeHash[:], common.Hex2Bytes("833cc0fc87c46ad8a223e44ccfdc16a51a7e7383525136441bd0c730f06023df")) require.Equal(t, allCodeHash[:], common.Hex2Bytes("833cc0fc87c46ad8a223e44ccfdc16a51a7e7383525136441bd0c730f06023df"))
} }
func TestUpgradeBuildInSystemContractNilInterface(t *testing.T) {
var (
config = params.BSCChainConfig
blockNumber = big.NewInt(37959559)
lastBlockTime uint64 = 1713419337
blockTime uint64 = 1713419340
statedb vm.StateDB
)
GenesisHash = params.BSCGenesisHash
UpgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}
func TestUpgradeBuildInSystemContractNilValue(t *testing.T) {
var (
config = params.BSCChainConfig
blockNumber = big.NewInt(37959559)
lastBlockTime uint64 = 1713419337
blockTime uint64 = 1713419340
statedb vm.StateDB = (*state.StateDB)(nil)
)
GenesisHash = params.BSCGenesisHash
UpgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}

View File

@@ -2,10 +2,12 @@ package types
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@@ -53,3 +55,40 @@ func (s *BlobSidecar) SanityCheck(blockNumber *big.Int, blockHash common.Hash) e
} }
return nil return nil
} }
func (s *BlobSidecar) MarshalJSON() ([]byte, error) {
fields := map[string]interface{}{
"blockHash": s.BlockHash,
"blockNumber": hexutil.EncodeUint64(s.BlockNumber.Uint64()),
"txHash": s.TxHash,
"txIndex": hexutil.EncodeUint64(s.TxIndex),
}
fields["blobSidecar"] = s.BlobTxSidecar
return json.Marshal(fields)
}
func (s *BlobSidecar) UnmarshalJSON(input []byte) error {
type blobSidecar struct {
BlobSidecar BlobTxSidecar `json:"blobSidecar"`
BlockNumber *hexutil.Big `json:"blockNumber"`
BlockHash common.Hash `json:"blockHash"`
TxIndex *hexutil.Big `json:"txIndex"`
TxHash common.Hash `json:"txHash"`
}
var blob blobSidecar
if err := json.Unmarshal(input, &blob); err != nil {
return err
}
s.BlobTxSidecar = blob.BlobSidecar
if blob.BlockNumber == nil {
return errors.New("missing required field 'blockNumber' for BlobSidecar")
}
s.BlockNumber = blob.BlockNumber.ToInt()
s.BlockHash = blob.BlockHash
if blob.TxIndex == nil {
return errors.New("missing required field 'txIndex' for BlobSidecar")
}
s.TxIndex = blob.TxIndex.ToInt().Uint64()
s.TxHash = blob.TxHash
return nil
}

View File

@@ -3,6 +3,7 @@ package vote
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math/big"
"sync" "sync"
"time" "time"
@@ -19,7 +20,13 @@ import (
const blocksNumberSinceMining = 5 // the number of blocks need to wait before voting, counting from the validator begin to mine const blocksNumberSinceMining = 5 // the number of blocks need to wait before voting, counting from the validator begin to mine
var diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures
var votesManagerCounter = metrics.NewRegisteredCounter("votesManager/local", nil) var votesManagerCounter = metrics.NewRegisteredCounter("votesManager/local", nil)
var notJustified = metrics.NewRegisteredCounter("votesManager/notJustified", nil)
var inTurnJustified = metrics.NewRegisteredCounter("votesManager/inTurnJustified", nil)
var notInTurnJustified = metrics.NewRegisteredCounter("votesManager/notInTurnJustified", nil)
var continuousJustified = metrics.NewRegisteredCounter("votesManager/continuousJustified", nil)
var notContinuousJustified = metrics.NewRegisteredCounter("votesManager/notContinuousJustified", nil)
// Backend wraps all methods required for voting. // Backend wraps all methods required for voting.
type Backend interface { type Backend interface {
@@ -155,7 +162,7 @@ func (voteManager *VoteManager) loop() {
func(bLSPublicKey *types.BLSPublicKey) bool { func(bLSPublicKey *types.BLSPublicKey) bool {
return bytes.Equal(voteManager.signer.PubKey[:], bLSPublicKey[:]) return bytes.Equal(voteManager.signer.PubKey[:], bLSPublicKey[:])
}) { }) {
log.Debug("cur validator is not within the validatorSet at curHead") log.Debug("local validator with voteKey is not within the validatorSet at curHead")
continue continue
} }
@@ -202,6 +209,36 @@ func (voteManager *VoteManager) loop() {
voteManager.pool.PutVote(voteMessage) voteManager.pool.PutVote(voteMessage)
votesManagerCounter.Inc(1) votesManagerCounter.Inc(1)
} }
// check the latest justified block, which indicating the stability of the network
curJustifiedNumber, _, err := voteManager.engine.GetJustifiedNumberAndHash(voteManager.chain, []*types.Header{curHead})
if err == nil && curJustifiedNumber != 0 {
if curJustifiedNumber+1 != curHead.Number.Uint64() {
log.Debug("not justified", "blockNumber", curHead.Number.Uint64()-1)
notJustified.Inc(1)
} else {
parent := voteManager.chain.GetHeaderByHash(curHead.ParentHash)
if parent != nil {
if parent.Difficulty.Cmp(diffInTurn) == 0 {
inTurnJustified.Inc(1)
} else {
log.Debug("not in turn block justified", "blockNumber", parent.Number.Int64(), "blockHash", parent.Hash())
notInTurnJustified.Inc(1)
}
lastJustifiedNumber, _, err := voteManager.engine.GetJustifiedNumberAndHash(voteManager.chain, []*types.Header{parent})
if err == nil {
if lastJustifiedNumber == 0 || lastJustifiedNumber+1 == curJustifiedNumber {
continuousJustified.Inc(1)
} else {
log.Debug("not continuous block justified", "lastJustified", lastJustifiedNumber, "curJustified", curJustifiedNumber)
notContinuousJustified.Inc(1)
}
}
}
}
}
case event := <-voteManager.syncVoteCh: case event := <-voteManager.syncVoteCh:
voteMessage := event.Vote voteMessage := event.Vote
if voteManager.eth.IsMining() || !bytes.Equal(voteManager.signer.PubKey[:], voteMessage.VoteAddress[:]) { if voteManager.eth.IsMining() || !bytes.Equal(voteManager.signer.PubKey[:], voteMessage.VoteAddress[:]) {

View File

@@ -1,7 +1,7 @@
package bn256 package bn256
// For details of the algorithms used, see "Multiplication and Squaring on // 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. // http://eprint.iacr.org/2006/471.pdf.
import ( import (

Some files were not shown because too many files have changed in this diff Show More