commit
26a4d4fda6
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,4 +1,31 @@
|
||||
# Changelog
|
||||
## 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
|
||||
|
||||
@ -35,7 +62,6 @@
|
||||
* [\#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
|
||||
|
||||
|
||||
## v1.4.10
|
||||
### FEATURE
|
||||
NA
|
||||
|
8
Makefile
8
Makefile
@ -29,11 +29,11 @@ truffle-test:
|
||||
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.truffle -t truffle-test
|
||||
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 genesis
|
||||
docker compose -f ./tests/truffle/docker-compose.yml up -d bsc-rpc bsc-validator1
|
||||
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 down
|
||||
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
|
||||
|
||||
#? lint: Run certain pre-selected linters
|
||||
lint: ## Run linters.
|
||||
|
@ -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"
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ const (
|
||||
BLSPublicKeyLength = 48
|
||||
|
||||
// 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
|
||||
validatorNumberSize = 1 // Fixed number of extra prefix bytes reserved for validator number after Luban
|
||||
validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength
|
||||
@ -35,6 +35,7 @@ type Extra struct {
|
||||
ExtraVanity string
|
||||
ValidatorSize uint8
|
||||
Validators validatorsAscending
|
||||
TurnLength *uint8
|
||||
*types.VoteAttestation
|
||||
ExtraSeal []byte
|
||||
}
|
||||
@ -113,6 +114,15 @@ func parseExtra(hexData string) (*Extra, error) {
|
||||
sort.Sort(extra.Validators)
|
||||
data = data[validatorBytesTotalLength-validatorNumberSize:]
|
||||
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
|
||||
@ -148,6 +158,10 @@ func prettyExtra(extra Extra) {
|
||||
}
|
||||
}
|
||||
|
||||
if extra.TurnLength != nil {
|
||||
fmt.Printf("TurnLength : %d\n", *extra.TurnLength)
|
||||
}
|
||||
|
||||
if extra.VoteAttestation != nil {
|
||||
fmt.Printf("Attestation :\n")
|
||||
fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count())
|
||||
|
@ -49,6 +49,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -216,6 +217,8 @@ type faucet struct {
|
||||
|
||||
bep2eInfos map[string]bep2eInfo
|
||||
bep2eAbi abi.ABI
|
||||
|
||||
limiter *IPRateLimiter
|
||||
}
|
||||
|
||||
// wsConn wraps a websocket connection with a write mutex as the underlying
|
||||
@ -235,6 +238,12 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Allow 1 request per minute with burst of 5, and cache up to 1000 IPs
|
||||
limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &faucet{
|
||||
config: genesis.Config,
|
||||
client: client,
|
||||
@ -245,6 +254,7 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
update: make(chan struct{}, 1),
|
||||
bep2eInfos: bep2eInfos,
|
||||
bep2eAbi: bep2eAbi,
|
||||
limiter: limiter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -272,6 +282,20 @@ func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// apiHandler handles requests for Ether grants and transaction statuses.
|
||||
func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ip := r.RemoteAddr
|
||||
if len(r.Header.Get("X-Forwarded-For")) > 0 {
|
||||
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
|
||||
if len(ips) > 0 {
|
||||
ip = strings.TrimSpace(ips[len(ips)-1])
|
||||
}
|
||||
}
|
||||
|
||||
if !f.limiter.GetLimiter(ip).Allow() {
|
||||
log.Warn("Too many requests from client: ", "client", ip)
|
||||
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
@ -625,19 +649,22 @@ func (f *faucet) loop() {
|
||||
balance := new(big.Int).Div(f.balance, ether)
|
||||
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
continue
|
||||
}
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
return // Exit the goroutine if the first send fails
|
||||
}
|
||||
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
@ -656,10 +683,12 @@ func (f *faucet) loop() {
|
||||
// Pending requests updated, stream to clients
|
||||
f.lock.RLock()
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
|
44
cmd/faucet/rate_limiter.go
Normal file
44
cmd/faucet/rate_limiter.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type IPRateLimiter struct {
|
||||
ips *lru.Cache // LRU cache to store IP addresses and their associated rate limiters
|
||||
r rate.Limit // the rate limit, e.g., 5 requests per second
|
||||
b int // the burst size, e.g., allowing a burst of 10 requests at once. The rate limiter gets into action
|
||||
// only after this number exceeds
|
||||
}
|
||||
|
||||
func NewIPRateLimiter(r rate.Limit, b int, size int) (*IPRateLimiter, error) {
|
||||
cache, err := lru.New(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := &IPRateLimiter{
|
||||
ips: cache,
|
||||
r: r,
|
||||
b: b,
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) addIP(ip string) *rate.Limiter {
|
||||
limiter := rate.NewLimiter(i.r, i.b)
|
||||
|
||||
i.ips.Add(ip, limiter)
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
|
||||
if limiter, exists := i.ips.Get(ip); exists {
|
||||
return limiter.(*rate.Limiter)
|
||||
}
|
||||
|
||||
return i.addIP(ip)
|
||||
}
|
@ -62,6 +62,7 @@ var (
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.CachePreimagesFlag,
|
||||
utils.OverridePassedForkTime,
|
||||
utils.OverrideBohr,
|
||||
utils.OverrideVerkle,
|
||||
utils.MultiDataBaseFlag,
|
||||
@ -253,6 +254,10 @@ func initGenesis(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
|
||||
var overrides core.ChainOverrides
|
||||
if ctx.IsSet(utils.OverridePassedForkTime.Name) {
|
||||
v := ctx.Uint64(utils.OverridePassedForkTime.Name)
|
||||
overrides.OverridePassedForkTime = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||
overrides.OverrideBohr = &v
|
||||
|
@ -185,6 +185,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
params.RialtoGenesisHash = common.HexToHash(v)
|
||||
}
|
||||
|
||||
if ctx.IsSet(utils.OverridePassedForkTime.Name) {
|
||||
v := ctx.Uint64(utils.OverridePassedForkTime.Name)
|
||||
cfg.Eth.OverridePassedForkTime = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||
cfg.Eth.OverrideBohr = &v
|
||||
@ -206,6 +210,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
if ctx.IsSet(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)
|
||||
|
||||
|
@ -72,12 +72,14 @@ var (
|
||||
utils.USBFlag,
|
||||
utils.SmartCardDaemonPathFlag,
|
||||
utils.RialtoHash,
|
||||
utils.OverridePassedForkTime,
|
||||
utils.OverrideBohr,
|
||||
utils.OverrideVerkle,
|
||||
utils.OverrideFullImmutabilityThreshold,
|
||||
utils.OverrideMinBlocksForBlobRequests,
|
||||
utils.OverrideDefaultExtraReserveForBlobRequests,
|
||||
utils.OverrideBreatheBlockInterval,
|
||||
utils.OverrideFixedTurnLength,
|
||||
utils.EnablePersonal,
|
||||
utils.TxPoolLocalsFlag,
|
||||
utils.TxPoolNoLocalsFlag,
|
||||
|
@ -33,7 +33,7 @@ node get_perf.js --rpc ${url} --startNum ${start} --endNum ${end}
|
||||
output as following
|
||||
```bash
|
||||
Get the performance between [ 19470 , 19670 )
|
||||
txCountPerBlock = 3142.81 txCountTotal = 628562 BlockCount = 200 avgBlockTime = 3.005 inturnBlocksRatio = 0.975
|
||||
txCountPerBlock = 3142.81 txCountTotal = 628562 BlockCount = 200 avgBlockTime = 3.005 inturnBlocksRatio = 0.975 justifiedBlocksRatio = 0.98
|
||||
txCountPerSecond = 1045.8602329450914 avgGasUsedPerBlock = 250.02062627 avgGasUsedPerSecond = 83.20153952412646
|
||||
```
|
||||
|
||||
|
@ -12,6 +12,9 @@ const main = async () => {
|
||||
let txCountTotal = 0;
|
||||
let gasUsedTotal = 0;
|
||||
let inturnBlocks = 0;
|
||||
let justifiedBlocks = 0;
|
||||
let turnLength = await provider.send("parlia_getTurnLength", [
|
||||
ethers.toQuantity(program.startNum)]);
|
||||
for (let i = program.startNum; i < program.endNum; i++) {
|
||||
let txCount = await provider.send("eth_getBlockTransactionCountByNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
@ -26,7 +29,15 @@ const main = async () => {
|
||||
inturnBlocks += 1
|
||||
}
|
||||
let timestamp = eval(eval(header.timestamp).toString(10))
|
||||
console.log("BlockNumber =", i, "mod =", i%4, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp)
|
||||
|
||||
let justifiedNumber = await provider.send("parlia_getJustifiedNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
if (justifiedNumber + 1 == i) {
|
||||
justifiedBlocks += 1
|
||||
} else {
|
||||
console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber)
|
||||
}
|
||||
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
|
||||
@ -41,13 +52,14 @@ const main = async () => {
|
||||
let timeCost = endTime - startTime
|
||||
let avgBlockTime = timeCost/blockCount
|
||||
let inturnBlocksRatio = inturnBlocks/blockCount
|
||||
let justifiedBlocksRatio = justifiedBlocks/blockCount
|
||||
let tps = txCountTotal/timeCost
|
||||
let M = 1000000
|
||||
let avgGasUsedPerBlock = gasUsedTotal/blockCount/M
|
||||
let avgGasUsedPerSecond = gasUsedTotal/timeCost/M
|
||||
|
||||
console.log("Get the performance between [", program.startNum, ",", program.endNum, ")");
|
||||
console.log("txCountPerBlock =", txCountPerBlock, "txCountTotal =", txCountTotal, "BlockCount =", blockCount, "avgBlockTime =", avgBlockTime, "inturnBlocksRatio =", inturnBlocksRatio);
|
||||
console.log("txCountPerBlock =", txCountPerBlock, "txCountTotal =", txCountTotal, "BlockCount =", blockCount, "avgBlockTime =", avgBlockTime, "inturnBlocksRatio =", inturnBlocksRatio, "justifiedBlocksRatio =", justifiedBlocksRatio);
|
||||
console.log("txCountPerSecond =", tps, "avgGasUsedPerBlock =", avgGasUsedPerBlock, "avgGasUsedPerSecond =", avgGasUsedPerSecond);
|
||||
};
|
||||
|
||||
|
164
cmd/jsutils/getchainstatus.js
Normal file
164
cmd/jsutils/getchainstatus.js
Normal 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);
|
||||
});
|
@ -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);
|
||||
});
|
@ -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);
|
||||
});
|
@ -305,6 +305,11 @@ var (
|
||||
Usage: "Manually specify the Rialto Genesis Hash, to trigger builtin network logic",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverridePassedForkTime = &cli.Uint64Flag{
|
||||
Name: "override.passedforktime",
|
||||
Usage: "Manually specify the hard fork timestamp except the last one, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideBohr = &cli.Uint64Flag{
|
||||
Name: "override.bohr",
|
||||
Usage: "Manually specify the Bohr fork timestamp, overriding the bundled setting",
|
||||
@ -339,6 +344,12 @@ var (
|
||||
Value: params.BreatheBlockInterval,
|
||||
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{
|
||||
Name: "syncmode",
|
||||
Usage: `Blockchain sync mode ("snap" or "full")`,
|
||||
|
@ -59,6 +59,9 @@ type ChainHeaderReader interface {
|
||||
// GetHighestVerifiedHeader retrieves the highest header verified.
|
||||
GetHighestVerifiedHeader() *types.Header
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the highest verified block.
|
||||
GetVerifiedBlockByHash(hash common.Hash) *types.Header
|
||||
|
||||
// ChasingHead return the best chain head of peers.
|
||||
ChasingHead() *types.Header
|
||||
}
|
||||
|
@ -2306,6 +2306,19 @@ const validatorSetABI = `
|
||||
],
|
||||
"stateMutability": "view"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getTurnLength",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "getValidators",
|
||||
|
@ -31,13 +31,7 @@ type API struct {
|
||||
|
||||
// GetSnapshot retrieves the state snapshot at a given block.
|
||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@ -56,13 +50,7 @@ func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
|
||||
|
||||
// GetValidators retrieves the list of validators at the specified block.
|
||||
func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@ -86,3 +74,65 @@ func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error)
|
||||
}
|
||||
return snap.validators(), nil
|
||||
}
|
||||
|
||||
func (api *API) GetJustifiedNumber(number *rpc.BlockNumber) (uint64, 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.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
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) {
|
||||
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.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
return snap.Attestation.SourceNumber, nil
|
||||
}
|
||||
|
||||
func (api *API) getHeader(number *rpc.BlockNumber) (header *types.Header) {
|
||||
currentHeader := api.chain.CurrentHeader()
|
||||
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = currentHeader // current if none requested
|
||||
} else if *number == rpc.SafeBlockNumber {
|
||||
justifiedNumber, _, err := api.parlia.GetJustifiedNumberAndHash(api.chain, []*types.Header{currentHeader})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
header = api.chain.GetHeaderByNumber(justifiedNumber)
|
||||
} else if *number == rpc.FinalizedBlockNumber {
|
||||
header = api.parlia.GetFinalizedHeader(api.chain, currentHeader)
|
||||
} else if *number == rpc.PendingBlockNumber {
|
||||
return nil // no pending blocks on bsc
|
||||
} else if *number == rpc.EarliestBlockNumber {
|
||||
header = api.chain.GetHeaderByNumber(0)
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
91
consensus/parlia/bohrFork.go
Normal file
91
consensus/parlia/bohrFork.go
Normal 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
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
@ -53,11 +54,13 @@ const (
|
||||
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
|
||||
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
|
||||
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.
|
||||
turnLengthSize = 1 // Fixed number of extra-data suffix bytes reserved for turnLength
|
||||
|
||||
validatorBytesLengthBeforeLuban = common.AddressLength
|
||||
validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength
|
||||
@ -126,6 +129,10 @@ var (
|
||||
// invalid list of validators (i.e. non divisible by 20 bytes).
|
||||
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 = errors.New("non-zero mix digest")
|
||||
|
||||
@ -136,6 +143,10 @@ var (
|
||||
// list of validators different than the one the local node calculated.
|
||||
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 = errors.New("invalid difficulty")
|
||||
|
||||
@ -369,6 +380,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.
|
||||
// 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 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 {
|
||||
if len(header.Extra) <= extraVanity+extraSeal {
|
||||
return nil
|
||||
@ -385,11 +397,15 @@ func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.Chain
|
||||
return nil
|
||||
}
|
||||
num := int(header.Extra[extraVanity])
|
||||
if num == 0 || len(header.Extra) <= extraVanity+extraSeal+num*validatorBytesLength {
|
||||
return nil
|
||||
}
|
||||
start := extraVanity + validatorNumberSize
|
||||
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]
|
||||
}
|
||||
|
||||
@ -408,11 +424,14 @@ func getVoteAttestationFromHeader(header *types.Header, chainConfig *params.Chai
|
||||
attestationBytes = header.Extra[extraVanity : len(header.Extra)-extraSeal]
|
||||
} else {
|
||||
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
|
||||
}
|
||||
start := extraVanity + validatorNumberSize + num*validatorBytesLength
|
||||
end := len(header.Extra) - extraSeal
|
||||
attestationBytes = header.Extra[start:end]
|
||||
}
|
||||
|
||||
@ -604,15 +623,11 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
case header.BlobGasUsed != nil:
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case header.WithdrawalsHash != nil:
|
||||
return fmt.Errorf("invalid WithdrawalsHash, have %#x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case !header.EmptyWithdrawalsHash():
|
||||
return errors.New("header has wrong WithdrawalsHash")
|
||||
}
|
||||
@ -621,6 +636,17 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
}
|
||||
}
|
||||
|
||||
bohr := chain.Config().IsBohr(header.Number, header.Time)
|
||||
if !bohr {
|
||||
if header.ParentBeaconRoot != nil {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
} else {
|
||||
if header.ParentBeaconRoot == nil || *header.ParentBeaconRoot != (common.Hash{}) {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot)
|
||||
}
|
||||
}
|
||||
|
||||
// All basic checks passed, verify cascading fields
|
||||
return p.verifyCascadingFields(chain, header, parents)
|
||||
}
|
||||
@ -713,13 +739,28 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
}
|
||||
}
|
||||
|
||||
// If we're at the genesis, snapshot the initial state.
|
||||
if number == 0 {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
// get checkpoint data
|
||||
hash := checkpoint.Hash()
|
||||
|
||||
// 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 {
|
||||
checkpoint = chain.GetHeaderByNumber(0)
|
||||
if checkpoint != nil {
|
||||
blockHash = 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
|
||||
validators, voteAddrs, err := parseValidators(checkpoint, p.chainConfig, p.config)
|
||||
if err != nil {
|
||||
@ -727,11 +768,27 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -888,6 +945,24 @@ func (p *Parlia) prepareValidators(header *types.Header) error {
|
||||
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 {
|
||||
if !p.chainConfig.IsLuban(header.Number) || header.Number.Uint64() < 2 {
|
||||
return nil
|
||||
@ -1019,6 +1094,9 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.prepareTurnLength(chain, header); err != nil {
|
||||
return err
|
||||
}
|
||||
// add extra seal space
|
||||
header.Extra = append(header.Extra, make([]byte, extraSeal)...)
|
||||
|
||||
@ -1069,6 +1147,30 @@ func (p *Parlia) verifyValidators(header *types.Header) error {
|
||||
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,
|
||||
cx core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction,
|
||||
usedGas *uint64, mining bool) error {
|
||||
@ -1163,6 +1265,10 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.verifyTurnLength(chain, header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cx := chainContext{Chain: chain, parlia: p}
|
||||
|
||||
parent := chain.GetHeaderByHash(header.ParentHash)
|
||||
@ -1189,7 +1295,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
||||
}
|
||||
}
|
||||
if header.Difficulty.Cmp(diffInTurn) != 0 {
|
||||
spoiledVal := snap.supposeValidator()
|
||||
spoiledVal := snap.inturnValidator()
|
||||
signedRecently := false
|
||||
if p.chainConfig.IsPlato(header.Number) {
|
||||
signedRecently = snap.SignRecently(spoiledVal)
|
||||
@ -1280,7 +1386,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
spoiledVal := snap.supposeValidator()
|
||||
spoiledVal := snap.inturnValidator()
|
||||
signedRecently := false
|
||||
if p.chainConfig.IsPlato(header.Number) {
|
||||
signedRecently = snap.SignRecently(spoiledVal)
|
||||
@ -1362,7 +1468,7 @@ func (p *Parlia) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *
|
||||
func (p *Parlia) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
|
||||
targetNumber := vote.Data.TargetNumber
|
||||
targetHash := vote.Data.TargetHash
|
||||
header := chain.GetHeaderByHash(targetHash)
|
||||
header := chain.GetVerifiedBlockByHash(targetHash)
|
||||
if header == nil {
|
||||
log.Warn("BlockHeader at current voteBlockNumber is nil", "targetNumber", targetNumber, "targetHash", targetHash)
|
||||
return errors.New("BlockHeader at current voteBlockNumber is nil")
|
||||
@ -1433,10 +1539,13 @@ func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOv
|
||||
delay = delay - *leftOver
|
||||
}
|
||||
|
||||
// The blocking time should be no more than half of period
|
||||
half := time.Duration(p.config.Period) * time.Second / 2
|
||||
if delay > half {
|
||||
delay = half
|
||||
// The blocking time should be no more than half of period when snap.TurnLength == 1
|
||||
timeForMining := time.Duration(p.config.Period) * time.Second / 2
|
||||
if !snap.lastBlockInOneTurn(header.Number.Uint64()) {
|
||||
timeForMining = time.Duration(p.config.Period) * time.Second * 2 / 3
|
||||
}
|
||||
if delay > timeForMining {
|
||||
delay = timeForMining
|
||||
}
|
||||
return &delay
|
||||
}
|
||||
@ -1594,11 +1703,35 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
|
||||
return new(big.Int).Set(diffNoTurn)
|
||||
}
|
||||
|
||||
func encodeSigHeaderWithoutVoteAttestation(w io.Writer, header *types.Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeaderWithoutVoteAttestation
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block without vote attestation prior to it being sealed.
|
||||
// So it's not the real hash of a block, just used as unique id to distinguish task
|
||||
func (p *Parlia) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
types.EncodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
encodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
@ -1904,42 +2037,40 @@ func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *t
|
||||
// =========================== utility function ==========================
|
||||
func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Address) uint64 {
|
||||
if snap.inturn(val) {
|
||||
log.Debug("backOffTime", "blockNumber", header.Number, "in turn validator", val)
|
||||
return 0
|
||||
} else {
|
||||
delay := initialBackOffTime
|
||||
validators := snap.validators()
|
||||
if p.chainConfig.IsPlanck(header.Number) {
|
||||
// reverse the key/value of snap.Recents to get recentsMap
|
||||
recentsMap := make(map[common.Address]uint64, len(snap.Recents))
|
||||
bound := uint64(0)
|
||||
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
|
||||
counts := snap.countRecents()
|
||||
for addr, seenTimes := range counts {
|
||||
log.Debug("backOffTime", "blockNumber", header.Number, "validator", addr, "seenTimes", seenTimes)
|
||||
}
|
||||
|
||||
// The backOffTime does not matter when a validator has signed recently.
|
||||
if _, ok := recentsMap[val]; ok {
|
||||
if snap.signRecentlyByCounts(val, counts) {
|
||||
return 0
|
||||
}
|
||||
|
||||
inTurnAddr := validators[(snap.Number+1)%uint64(len(validators))]
|
||||
if _, ok := recentsMap[inTurnAddr]; ok {
|
||||
inTurnAddr := snap.inturnValidator()
|
||||
if snap.signRecentlyByCounts(inTurnAddr, counts) {
|
||||
log.Debug("in turn validator has recently signed, skip initialBackOffTime",
|
||||
"inTurnAddr", inTurnAddr)
|
||||
delay = 0
|
||||
}
|
||||
|
||||
// Exclude the recently signed validators
|
||||
// Exclude the recently signed validators and the in turn validator
|
||||
temp := make([]common.Address, 0, len(validators))
|
||||
for _, addr := range validators {
|
||||
if _, ok := recentsMap[addr]; ok {
|
||||
if snap.signRecentlyByCounts(addr, counts) {
|
||||
continue
|
||||
}
|
||||
if p.chainConfig.IsBohr(header.Number, header.Time) {
|
||||
if addr == inTurnAddr {
|
||||
continue
|
||||
}
|
||||
}
|
||||
temp = append(temp, addr)
|
||||
}
|
||||
validators = temp
|
||||
@ -1957,7 +2088,11 @@ func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Ad
|
||||
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)
|
||||
n := len(validators)
|
||||
backOffSteps := make([]uint64, 0, n)
|
||||
|
@ -22,22 +22,44 @@ func TestImpactOfValidatorOutOfService(t *testing.T) {
|
||||
testCases := []struct {
|
||||
totalValidators int
|
||||
downValidators int
|
||||
turnLength int
|
||||
}{
|
||||
{3, 1},
|
||||
{5, 2},
|
||||
{10, 1},
|
||||
{10, 4},
|
||||
{21, 1},
|
||||
{21, 3},
|
||||
{21, 5},
|
||||
{21, 10},
|
||||
{3, 1, 1},
|
||||
{5, 2, 1},
|
||||
{10, 1, 2},
|
||||
{10, 4, 2},
|
||||
{21, 1, 3},
|
||||
{21, 3, 3},
|
||||
{21, 5, 4},
|
||||
{21, 10, 5},
|
||||
}
|
||||
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
|
||||
recoverBlocks := 10000
|
||||
recents := make(map[uint64]int)
|
||||
@ -55,12 +77,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
delete(validators, down[i])
|
||||
}
|
||||
isRecentSign := func(idx int) bool {
|
||||
for _, signIdx := range recents {
|
||||
if signIdx == idx {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return signRecently(idx, recents, turnLength)
|
||||
}
|
||||
isInService := func(idx int) bool {
|
||||
return validators[idx]
|
||||
@ -68,10 +85,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
|
||||
downDelay := uint64(0)
|
||||
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)
|
||||
}
|
||||
proposer := h % totalValidators
|
||||
proposer := inturnValidator(totalValidators, turnLength, h)
|
||||
if !isInService(proposer) || isRecentSign(proposer) {
|
||||
candidates := make(map[int]bool, totalValidators/2)
|
||||
for v := range validators {
|
||||
@ -99,10 +116,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
recoverDelay := uint64(0)
|
||||
lastseen := downBlocks
|
||||
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)
|
||||
}
|
||||
proposer := h % totalValidators
|
||||
proposer := inturnValidator(totalValidators, turnLength, h)
|
||||
if !isInService(proposer) || isRecentSign(proposer) {
|
||||
lastseen = h
|
||||
candidates := make(map[int]bool, totalValidators/2)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
@ -43,6 +44,7 @@ type Snapshot struct {
|
||||
|
||||
Number uint64 `json:"number"` // Block number 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
|
||||
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
|
||||
@ -72,6 +74,7 @@ func newSnapshot(
|
||||
sigCache: sigCache,
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
TurnLength: defaultTurnLength,
|
||||
Recents: make(map[uint64]common.Address),
|
||||
RecentForkHashes: make(map[uint64]string),
|
||||
Validators: make(map[common.Address]*ValidatorInfo),
|
||||
@ -114,6 +117,10 @@ func loadSnapshot(config *params.ParliaConfig, sigCache *lru.ARCCache, db ethdb.
|
||||
if err := json.Unmarshal(blob, snap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if snap.TurnLength == 0 { // no TurnLength field in old snapshots
|
||||
snap.TurnLength = defaultTurnLength
|
||||
}
|
||||
|
||||
snap.config = config
|
||||
snap.sigCache = sigCache
|
||||
snap.ethAPI = ethAPI
|
||||
@ -138,6 +145,7 @@ func (s *Snapshot) copy() *Snapshot {
|
||||
sigCache: s.sigCache,
|
||||
Number: s.Number,
|
||||
Hash: s.Hash,
|
||||
TurnLength: s.TurnLength,
|
||||
Validators: make(map[common.Address]*ValidatorInfo),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
RecentForkHashes: make(map[uint64]string),
|
||||
@ -210,17 +218,45 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Snapshot) SignRecently(validator common.Address) bool {
|
||||
for seen, recent := range s.Recents {
|
||||
if recent == validator {
|
||||
if limit := uint64(len(s.Validators)/2 + 1); s.Number+1 < limit || seen > s.Number+1-limit {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if seen <= leftHistoryBound || recent == (common.Address{}) /*when seen == `epochKey`*/ {
|
||||
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 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) {
|
||||
// Allow passing in no headers for cleaner code
|
||||
if len(headers) == 0 {
|
||||
@ -247,10 +283,10 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
for _, header := range headers {
|
||||
number := header.Number.Uint64()
|
||||
// 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)
|
||||
}
|
||||
if limit := uint64(len(snap.Validators)); number >= limit {
|
||||
if limit := snap.versionHistoryCheckLen(); number >= limit {
|
||||
delete(snap.RecentForkHashes, number-limit)
|
||||
}
|
||||
// Resolve the authorization key and check against signers
|
||||
@ -261,19 +297,47 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
if _, ok := snap.Validators[validator]; !ok {
|
||||
return nil, errUnauthorizedValidator(validator.String())
|
||||
}
|
||||
for _, recent := range snap.Recents {
|
||||
if recent == validator {
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
if snap.SignRecently(validator) {
|
||||
return nil, errRecentlySigned
|
||||
}
|
||||
} else {
|
||||
for _, recent := range snap.Recents {
|
||||
if recent == validator {
|
||||
return nil, errRecentlySigned
|
||||
}
|
||||
}
|
||||
}
|
||||
snap.Recents[number] = validator
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
// change validator set
|
||||
if number > 0 && number%s.config.Epoch == uint64(len(snap.Validators)/2) {
|
||||
checkpointHeader := FindAncientHeader(header, uint64(len(snap.Validators)/2), chain, parents)
|
||||
if number > 0 && number%s.config.Epoch == snap.minerHistoryCheckLen() {
|
||||
epochKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// after switching the validator set, snap.Validators may become larger,
|
||||
// then the unexpected second switch will happen, just skip it.
|
||||
if _, ok := snap.Recents[epochKey]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
checkpointHeader := FindAncientHeader(header, snap.minerHistoryCheckLen(), chain, parents)
|
||||
if checkpointHeader == nil {
|
||||
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
|
||||
newValArr, voteAddrs, err := parseValidators(checkpointHeader, chainConfig, s.config)
|
||||
if err != nil {
|
||||
@ -289,18 +353,18 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
}
|
||||
}
|
||||
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))
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// BEP-404: Clear Miner History when Switching Validators Set
|
||||
snap.Recents = make(map[uint64]common.Address)
|
||||
snap.Recents[epochKey] = common.Address{}
|
||||
log.Debug("Recents are cleared up", "blockNumber", number)
|
||||
} else {
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
snap.Validators = newVals
|
||||
@ -310,11 +374,10 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
snap.Validators[val].Index = idx + 1 // offset by 1
|
||||
}
|
||||
}
|
||||
for i := snap.versionHistoryCheckLen(); i < oldVersionsLen; i++ {
|
||||
delete(snap.RecentForkHashes, number-i)
|
||||
}
|
||||
}
|
||||
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
}
|
||||
snap.Number += uint64(len(headers))
|
||||
snap.Hash = headers[len(headers)-1].Hash()
|
||||
@ -331,17 +394,20 @@ func (s *Snapshot) validators() []common.Address {
|
||||
return validators
|
||||
}
|
||||
|
||||
// inturn returns if a validator at a given block height is in-turn or not.
|
||||
func (s *Snapshot) inturn(validator common.Address) bool {
|
||||
validators := s.validators()
|
||||
offset := (s.Number + 1) % uint64(len(validators))
|
||||
return validators[offset] == validator
|
||||
// lastBlockInOneTurn returns if the block at height `blockNumber` is the last block in current turn.
|
||||
func (s *Snapshot) lastBlockInOneTurn(blockNumber uint64) bool {
|
||||
return (blockNumber+1)%uint64(s.TurnLength) == 0
|
||||
}
|
||||
|
||||
// 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 {
|
||||
validators := s.validators()
|
||||
offset := (s.Number + 1) % uint64(len(validators))
|
||||
offset := (s.Number + 1) / uint64(s.TurnLength) % uint64(len(validators))
|
||||
return validators[offset]
|
||||
}
|
||||
|
||||
@ -379,12 +445,6 @@ func (s *Snapshot) indexOfVal(validator common.Address) int {
|
||||
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) {
|
||||
validatorsBytes := getValidatorBytesFromHeader(header, chainConfig, parliaConfig)
|
||||
if len(validatorsBytes) == 0 {
|
||||
@ -410,6 +470,24 @@ func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parl
|
||||
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 {
|
||||
ancient := header
|
||||
for i := uint64(1); i <= ite; i++ {
|
||||
|
@ -259,23 +259,25 @@ type BlockChain struct {
|
||||
triesInMemory uint64
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
chainBlockFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
finalizedHeaderFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
chainBlockFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
finalizedHeaderFeed event.Feed
|
||||
highestVerifiedBlockFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
|
||||
// This mutex synchronizes chain write operations.
|
||||
// Readers don't need to take it, they can just read the database.
|
||||
chainmu *syncx.ClosableMutex
|
||||
|
||||
highestVerifiedHeader atomic.Pointer[types.Header]
|
||||
highestVerifiedBlock atomic.Pointer[types.Header]
|
||||
currentBlock atomic.Pointer[types.Header] // Current head of the chain
|
||||
currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync
|
||||
currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block
|
||||
@ -400,6 +402,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
}
|
||||
|
||||
bc.highestVerifiedHeader.Store(nil)
|
||||
bc.highestVerifiedBlock.Store(nil)
|
||||
bc.currentBlock.Store(nil)
|
||||
bc.currentSnapBlock.Store(nil)
|
||||
bc.chasingHead.Store(nil)
|
||||
@ -1390,7 +1393,7 @@ func (bc *BlockChain) Stop() {
|
||||
if !bc.cacheConfig.TrieDirtyDisabled {
|
||||
triedb := bc.triedb
|
||||
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 {
|
||||
recent := bc.GetBlockByNumber(number - offset)
|
||||
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
|
||||
@ -1828,7 +1831,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||
|
||||
// Flush limits are not considered for the first TriesInMemory blocks.
|
||||
current := block.NumberU64()
|
||||
if current <= TriesInMemory {
|
||||
if current <= bc.TriesInMemory() {
|
||||
return nil
|
||||
}
|
||||
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
||||
@ -1926,14 +1929,19 @@ func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types
|
||||
// writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead.
|
||||
// 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) (status WriteStatus, err error) {
|
||||
if err := bc.writeBlockWithState(block, receipts, state); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
currentBlock := bc.CurrentBlock()
|
||||
reorg, err := bc.forker.ReorgNeededWithFastFinality(currentBlock, block.Header())
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if reorg {
|
||||
bc.highestVerifiedBlock.Store(types.CopyHeader(block.Header()))
|
||||
bc.highestVerifiedBlockFeed.Send(HighestVerifiedBlockEvent{Header: block.Header()})
|
||||
}
|
||||
|
||||
if err := bc.writeBlockWithState(block, receipts, state); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if reorg {
|
||||
// Reorganise the chain if the parent is not the head block
|
||||
if block.ParentHash() != currentBlock.Hash() {
|
||||
|
@ -98,6 +98,15 @@ func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the header of a verified block, it may be only in memory.
|
||||
func (bc *BlockChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
highestVerifiedBlock := bc.highestVerifiedBlock.Load()
|
||||
if highestVerifiedBlock != nil && highestVerifiedBlock.Hash() == hash {
|
||||
return highestVerifiedBlock
|
||||
}
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
@ -486,6 +495,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
|
||||
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeHighestVerifiedBlockEvent registers a subscription of HighestVerifiedBlockEvent.
|
||||
func (bc *BlockChain) SubscribeHighestVerifiedHeaderEvent(ch chan<- HighestVerifiedBlockEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.highestVerifiedBlockFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeChainBlockEvent registers a subscription of ChainBlockEvent.
|
||||
func (bc *BlockChain) SubscribeChainBlockEvent(ch chan<- ChainHeadEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.chainBlockFeed.Subscribe(ch))
|
||||
|
@ -486,7 +486,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
|
||||
if cm.config.Parlia != nil {
|
||||
header.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
if cm.config.Parlia == nil {
|
||||
if cm.config.Parlia == nil || cm.config.IsBohr(header.Number, header.Time) {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
}
|
||||
@ -621,6 +621,10 @@ func (cm *chainMaker) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (cm *chainMaker) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return cm.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (cm *chainMaker) ChasingHead() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
@ -365,6 +365,10 @@ func (r *mockDAHeaderReader) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func createMockDATx(config *params.ChainConfig, sidecar *types.BlobTxSidecar) *types.Transaction {
|
||||
if sidecar == nil {
|
||||
tx := &types.DynamicFeeTx{
|
||||
|
@ -50,3 +50,5 @@ type ChainSideEvent struct {
|
||||
}
|
||||
|
||||
type ChainHeadEvent struct{ Block *types.Block }
|
||||
|
||||
type HighestVerifiedBlockEvent struct{ Header *types.Header }
|
||||
|
@ -121,9 +121,12 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b
|
||||
if f.preserve != nil {
|
||||
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern)
|
||||
}
|
||||
doubleSign := (extern.Coinbase == current.Coinbase)
|
||||
reorg = !currentPreserve && (externPreserve ||
|
||||
extern.Time < current.Time ||
|
||||
extern.Time == current.Time && f.rand.Float64() < 0.5)
|
||||
extern.Time == current.Time &&
|
||||
((doubleSign && extern.Hash().Cmp(current.Hash()) < 0) ||
|
||||
(!doubleSign && f.rand.Float64() < 0.5)))
|
||||
}
|
||||
return reorg, nil
|
||||
}
|
||||
|
@ -216,8 +216,9 @@ func (e *GenesisMismatchError) Error() string {
|
||||
// ChainOverrides contains the changes to chain config
|
||||
// Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes.
|
||||
type ChainOverrides struct {
|
||||
OverrideBohr *uint64
|
||||
OverrideVerkle *uint64
|
||||
OverridePassedForkTime *uint64
|
||||
OverrideBohr *uint64
|
||||
OverrideVerkle *uint64
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
@ -243,6 +244,15 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||
}
|
||||
applyOverrides := func(config *params.ChainConfig) {
|
||||
if config != nil {
|
||||
if overrides != nil && overrides.OverridePassedForkTime != nil {
|
||||
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
|
||||
}
|
||||
if overrides != nil && overrides.OverrideBohr != nil {
|
||||
config.BohrTime = overrides.OverrideBohr
|
||||
}
|
||||
@ -444,7 +454,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
if conf.Parlia == nil {
|
||||
if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) {
|
||||
head.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
|
||||
|
@ -436,6 +436,10 @@ func (hc *HeaderChain) GetHighestVerifiedHeader() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) ChasingHead() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
@ -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
|
||||
// contract. This method is exported to be used in tests.
|
||||
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
|
||||
// the new root
|
||||
msg := &Message{
|
||||
|
1
core/systemcontracts/bohr/chapel/StakeHubContract
Normal file
1
core/systemcontracts/bohr/chapel/StakeHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bohr/chapel/ValidatorContract
Normal file
1
core/systemcontracts/bohr/chapel/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bohr/mainnet/StakeHubContract
Normal file
1
core/systemcontracts/bohr/mainnet/StakeHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bohr/mainnet/ValidatorContract
Normal file
1
core/systemcontracts/bohr/mainnet/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bohr/rialto/StakeHubContract
Normal file
1
core/systemcontracts/bohr/rialto/StakeHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bohr/rialto/ValidatorContract
Normal file
1
core/systemcontracts/bohr/rialto/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
27
core/systemcontracts/bohr/types.go
Normal file
27
core/systemcontracts/bohr/types.go
Normal file
@ -0,0 +1,27 @@
|
||||
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
|
||||
)
|
||||
|
||||
// contract codes for Rialto upgrade
|
||||
var (
|
||||
//go:embed rialto/ValidatorContract
|
||||
RialtoValidatorContract string
|
||||
//go:embed rialto/StakeHubContract
|
||||
RialtoStakeHubContract string
|
||||
)
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"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/euler"
|
||||
"github.com/ethereum/go-ethereum/core/systemcontracts/feynman"
|
||||
@ -78,6 +79,8 @@ var (
|
||||
feynmanFixUpgrade = make(map[string]*Upgrade)
|
||||
|
||||
haberFixUpgrade = make(map[string]*Upgrade)
|
||||
|
||||
bohrUpgrade = make(map[string]*Upgrade)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -736,6 +739,54 @@ 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
bohrUpgrade[chapelNet] = &Upgrade{
|
||||
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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
bohrUpgrade[rialtoNet] = &Upgrade{
|
||||
UpgradeName: "bohr",
|
||||
Configs: []*UpgradeConfig{
|
||||
{
|
||||
ContractAddr: common.HexToAddress(ValidatorContract),
|
||||
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
|
||||
Code: bohr.RialtoValidatorContract,
|
||||
},
|
||||
{
|
||||
ContractAddr: common.HexToAddress(StakeHubContract),
|
||||
CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/398c9364aad5261c1ecd90ac3ab2df89b65c45e3",
|
||||
Code: bohr.RialtoStakeHubContract,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb *state.StateDB) {
|
||||
@ -816,6 +867,10 @@ func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.I
|
||||
applySystemContractUpgrade(haberFixUpgrade[network], blockNumber, statedb, logger)
|
||||
}
|
||||
|
||||
if config.IsOnBohr(blockNumber, lastBlockTime, blockTime) {
|
||||
applySystemContractUpgrade(bohrUpgrade[network], blockNumber, statedb, logger)
|
||||
}
|
||||
|
||||
/*
|
||||
apply other upgrades
|
||||
*/
|
||||
|
@ -673,10 +673,7 @@ type DiffAccountsInBlock struct {
|
||||
Transactions []DiffAccountsInTx
|
||||
}
|
||||
|
||||
var (
|
||||
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
|
||||
)
|
||||
var extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func SealHash(header *Header, chainId *big.Int) (hash common.Hash) {
|
||||
@ -687,48 +684,51 @@ func SealHash(header *Header, chainId *big.Int) (hash common.Hash) {
|
||||
}
|
||||
|
||||
func EncodeSigHeader(w io.Writer, header *Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeSigHeaderWithoutVoteAttestation(w io.Writer, header *Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeaderWithoutVoteAttestation
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
var err error
|
||||
if header.ParentBeaconRoot != nil && *header.ParentBeaconRoot == (common.Hash{}) {
|
||||
err = rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
header.BaseFee,
|
||||
header.WithdrawalsHash,
|
||||
header.BlobGasUsed,
|
||||
header.ExcessBlobGas,
|
||||
header.ParentBeaconRoot,
|
||||
})
|
||||
} else {
|
||||
err = rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package vote
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"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
|
||||
|
||||
var diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures
|
||||
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.
|
||||
type Backend interface {
|
||||
@ -33,8 +40,8 @@ type VoteManager struct {
|
||||
|
||||
chain *core.BlockChain
|
||||
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
highestVerifiedBlockCh chan core.HighestVerifiedBlockEvent
|
||||
highestVerifiedBlockSub event.Subscription
|
||||
|
||||
// used for backup validators to sync votes from corresponding mining validator
|
||||
syncVoteCh chan core.NewVoteEvent
|
||||
@ -49,12 +56,12 @@ type VoteManager struct {
|
||||
|
||||
func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journalPath, blsPasswordPath, blsWalletPath string, engine consensus.PoSA) (*VoteManager, error) {
|
||||
voteManager := &VoteManager{
|
||||
eth: eth,
|
||||
chain: chain,
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
syncVoteCh: make(chan core.NewVoteEvent, voteBufferForPut),
|
||||
pool: pool,
|
||||
engine: engine,
|
||||
eth: eth,
|
||||
chain: chain,
|
||||
highestVerifiedBlockCh: make(chan core.HighestVerifiedBlockEvent, highestVerifiedBlockChanSize),
|
||||
syncVoteCh: make(chan core.NewVoteEvent, voteBufferForPut),
|
||||
pool: pool,
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
// Create voteSigner.
|
||||
@ -74,7 +81,7 @@ func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journal
|
||||
voteManager.journal = voteJournal
|
||||
|
||||
// Subscribe to chain head event.
|
||||
voteManager.chainHeadSub = voteManager.chain.SubscribeChainHeadEvent(voteManager.chainHeadCh)
|
||||
voteManager.highestVerifiedBlockSub = voteManager.chain.SubscribeHighestVerifiedHeaderEvent(voteManager.highestVerifiedBlockCh)
|
||||
voteManager.syncVoteSub = voteManager.pool.SubscribeNewVoteEvent(voteManager.syncVoteCh)
|
||||
|
||||
go voteManager.loop()
|
||||
@ -84,7 +91,7 @@ func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journal
|
||||
|
||||
func (voteManager *VoteManager) loop() {
|
||||
log.Debug("vote manager routine loop started")
|
||||
defer voteManager.chainHeadSub.Unsubscribe()
|
||||
defer voteManager.highestVerifiedBlockSub.Unsubscribe()
|
||||
defer voteManager.syncVoteSub.Unsubscribe()
|
||||
|
||||
events := voteManager.eth.EventMux().Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
|
||||
@ -119,7 +126,7 @@ func (voteManager *VoteManager) loop() {
|
||||
log.Debug("downloader is in DoneEvent mode, set the startVote flag to true")
|
||||
startVote = true
|
||||
}
|
||||
case cHead := <-voteManager.chainHeadCh:
|
||||
case cHead := <-voteManager.highestVerifiedBlockCh:
|
||||
if !startVote {
|
||||
log.Debug("startVote flag is false, continue")
|
||||
continue
|
||||
@ -135,12 +142,12 @@ func (voteManager *VoteManager) loop() {
|
||||
continue
|
||||
}
|
||||
|
||||
if cHead.Block == nil {
|
||||
log.Debug("cHead.Block is nil, continue")
|
||||
if cHead.Header == nil {
|
||||
log.Debug("cHead.Header is nil, continue")
|
||||
continue
|
||||
}
|
||||
|
||||
curHead := cHead.Block.Header()
|
||||
curHead := cHead.Header
|
||||
if p, ok := voteManager.engine.(*parlia.Parlia); ok {
|
||||
nextBlockMinedTime := time.Unix(int64((curHead.Time + p.Period())), 0)
|
||||
timeForBroadcast := 50 * time.Millisecond // enough to broadcast a vote
|
||||
@ -155,7 +162,7 @@ func (voteManager *VoteManager) loop() {
|
||||
func(bLSPublicKey *types.BLSPublicKey) bool {
|
||||
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
|
||||
}
|
||||
|
||||
@ -202,6 +209,36 @@ func (voteManager *VoteManager) loop() {
|
||||
voteManager.pool.PutVote(voteMessage)
|
||||
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:
|
||||
voteMessage := event.Vote
|
||||
if voteManager.eth.IsMining() || !bytes.Equal(voteManager.signer.PubKey[:], voteMessage.VoteAddress[:]) {
|
||||
@ -217,7 +254,7 @@ func (voteManager *VoteManager) loop() {
|
||||
case <-voteManager.syncVoteSub.Err():
|
||||
log.Debug("voteManager subscribed votes failed")
|
||||
return
|
||||
case <-voteManager.chainHeadSub.Err():
|
||||
case <-voteManager.highestVerifiedBlockSub.Err():
|
||||
log.Debug("voteManager subscribed chainHead failed")
|
||||
return
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ const (
|
||||
lowerLimitOfVoteBlockNumber = 256
|
||||
upperLimitOfVoteBlockNumber = 11 // refer to fetcher.maxUncleDist
|
||||
|
||||
chainHeadChanSize = 10 // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||
highestVerifiedBlockChanSize = 10 // highestVerifiedBlockChanSize is the size of channel listening to HighestVerifiedBlockEvent.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -57,8 +57,8 @@ type VotePool struct {
|
||||
curVotesPq *votesPriorityQueue
|
||||
futureVotesPq *votesPriorityQueue
|
||||
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
highestVerifiedBlockCh chan core.HighestVerifiedBlockEvent
|
||||
highestVerifiedBlockSub event.Subscription
|
||||
|
||||
votesCh chan *types.VoteEnvelope
|
||||
|
||||
@ -69,19 +69,19 @@ type votesPriorityQueue []*types.VoteData
|
||||
|
||||
func NewVotePool(chain *core.BlockChain, engine consensus.PoSA) *VotePool {
|
||||
votePool := &VotePool{
|
||||
chain: chain,
|
||||
receivedVotes: mapset.NewSet[common.Hash](),
|
||||
curVotes: make(map[common.Hash]*VoteBox),
|
||||
futureVotes: make(map[common.Hash]*VoteBox),
|
||||
curVotesPq: &votesPriorityQueue{},
|
||||
futureVotesPq: &votesPriorityQueue{},
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
votesCh: make(chan *types.VoteEnvelope, voteBufferForPut),
|
||||
engine: engine,
|
||||
chain: chain,
|
||||
receivedVotes: mapset.NewSet[common.Hash](),
|
||||
curVotes: make(map[common.Hash]*VoteBox),
|
||||
futureVotes: make(map[common.Hash]*VoteBox),
|
||||
curVotesPq: &votesPriorityQueue{},
|
||||
futureVotesPq: &votesPriorityQueue{},
|
||||
highestVerifiedBlockCh: make(chan core.HighestVerifiedBlockEvent, highestVerifiedBlockChanSize),
|
||||
votesCh: make(chan *types.VoteEnvelope, voteBufferForPut),
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
// Subscribe events from blockchain and start the main event loop.
|
||||
votePool.chainHeadSub = votePool.chain.SubscribeChainHeadEvent(votePool.chainHeadCh)
|
||||
votePool.highestVerifiedBlockSub = votePool.chain.SubscribeHighestVerifiedHeaderEvent(votePool.highestVerifiedBlockCh)
|
||||
|
||||
go votePool.loop()
|
||||
return votePool
|
||||
@ -89,18 +89,18 @@ func NewVotePool(chain *core.BlockChain, engine consensus.PoSA) *VotePool {
|
||||
|
||||
// loop is the vote pool's main even loop, waiting for and reacting to outside blockchain events and votes channel event.
|
||||
func (pool *VotePool) loop() {
|
||||
defer pool.chainHeadSub.Unsubscribe()
|
||||
defer pool.highestVerifiedBlockSub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
// Handle ChainHeadEvent.
|
||||
case ev := <-pool.chainHeadCh:
|
||||
if ev.Block != nil {
|
||||
latestBlockNumber := ev.Block.NumberU64()
|
||||
case ev := <-pool.highestVerifiedBlockCh:
|
||||
if ev.Header != nil {
|
||||
latestBlockNumber := ev.Header.Number.Uint64()
|
||||
pool.prune(latestBlockNumber)
|
||||
pool.transferVotesFromFutureToCur(ev.Block.Header())
|
||||
pool.transferVotesFromFutureToCur(ev.Header)
|
||||
}
|
||||
case <-pool.chainHeadSub.Err():
|
||||
case <-pool.highestVerifiedBlockSub.Err():
|
||||
return
|
||||
|
||||
// Handle votes channel and put the vote into vote pool.
|
||||
@ -135,7 +135,7 @@ func (pool *VotePool) putIntoVotePool(vote *types.VoteEnvelope) bool {
|
||||
var votesPq *votesPriorityQueue
|
||||
isFutureVote := false
|
||||
|
||||
voteBlock := pool.chain.GetHeaderByHash(targetHash)
|
||||
voteBlock := pool.chain.GetVerifiedBlockByHash(targetHash)
|
||||
if voteBlock == nil {
|
||||
votes = pool.futureVotes
|
||||
votesPq = pool.futureVotesPq
|
||||
@ -226,7 +226,7 @@ func (pool *VotePool) transferVotesFromFutureToCur(latestBlockHeader *types.Head
|
||||
futurePqBuffer := make([]*types.VoteData, 0)
|
||||
for futurePq.Len() > 0 && futurePq.Peek().TargetNumber <= latestBlockNumber {
|
||||
blockHash := futurePq.Peek().TargetHash
|
||||
header := pool.chain.GetHeaderByHash(blockHash)
|
||||
header := pool.chain.GetVerifiedBlockByHash(blockHash)
|
||||
if header == nil {
|
||||
// Put into pq buffer used for later put again into futurePq
|
||||
futurePqBuffer = append(futurePqBuffer, heap.Pop(futurePq).(*types.VoteData))
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/parlia"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@ -440,6 +441,16 @@ func (b *EthAPIBackend) Engine() consensus.Engine {
|
||||
return b.eth.engine
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) CurrentTurnLength() (turnLength uint8, err error) {
|
||||
if p, ok := b.eth.engine.(*parlia.Parlia); ok {
|
||||
service := p.APIs(b.Chain())[0].Service
|
||||
currentHead := rpc.LatestBlockNumber
|
||||
return service.(*parlia.API).GetTurnLength(¤tHead)
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) CurrentHeader() *types.Header {
|
||||
return b.eth.blockchain.CurrentHeader()
|
||||
}
|
||||
|
@ -185,6 +185,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||
}
|
||||
// Override the chain config with provided settings.
|
||||
var overrides core.ChainOverrides
|
||||
if config.OverridePassedForkTime != nil {
|
||||
chainConfig.ShanghaiTime = config.OverridePassedForkTime
|
||||
chainConfig.KeplerTime = config.OverridePassedForkTime
|
||||
chainConfig.FeynmanTime = config.OverridePassedForkTime
|
||||
chainConfig.FeynmanFixTime = config.OverridePassedForkTime
|
||||
chainConfig.CancunTime = config.OverridePassedForkTime
|
||||
chainConfig.HaberTime = config.OverridePassedForkTime
|
||||
chainConfig.HaberFixTime = config.OverridePassedForkTime
|
||||
overrides.OverridePassedForkTime = config.OverridePassedForkTime
|
||||
}
|
||||
if config.OverrideBohr != nil {
|
||||
chainConfig.BohrTime = config.OverrideBohr
|
||||
overrides.OverrideBohr = config.OverrideBohr
|
||||
|
@ -188,6 +188,9 @@ type Config struct {
|
||||
// send-transaction variants. The unit is ether.
|
||||
RPCTxFeeCap float64
|
||||
|
||||
// OverridePassedForkTime
|
||||
OverridePassedForkTime *uint64 `toml:",omitempty"`
|
||||
|
||||
// OverrideBohr (TODO: remove after the fork)
|
||||
OverrideBohr *uint64 `toml:",omitempty"`
|
||||
|
||||
|
@ -70,6 +70,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||
RPCGasCap uint64
|
||||
RPCEVMTimeout time.Duration
|
||||
RPCTxFeeCap float64
|
||||
OverridePassedForkTime *uint64 `toml:",omitempty"`
|
||||
OverrideBohr *uint64 `toml:",omitempty"`
|
||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||
BlobExtraReserve uint64
|
||||
@ -128,6 +129,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||
enc.RPCGasCap = c.RPCGasCap
|
||||
enc.RPCEVMTimeout = c.RPCEVMTimeout
|
||||
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
||||
enc.OverridePassedForkTime = c.OverridePassedForkTime
|
||||
enc.OverrideBohr = c.OverrideBohr
|
||||
enc.OverrideVerkle = c.OverrideVerkle
|
||||
enc.BlobExtraReserve = c.BlobExtraReserve
|
||||
@ -190,6 +192,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||
RPCGasCap *uint64
|
||||
RPCEVMTimeout *time.Duration
|
||||
RPCTxFeeCap *float64
|
||||
OverridePassedForkTime *uint64 `toml:",omitempty"`
|
||||
OverrideBohr *uint64 `toml:",omitempty"`
|
||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||
BlobExtraReserve *uint64
|
||||
@ -357,6 +360,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||
if dec.RPCTxFeeCap != nil {
|
||||
c.RPCTxFeeCap = *dec.RPCTxFeeCap
|
||||
}
|
||||
if dec.OverridePassedForkTime != nil {
|
||||
c.OverridePassedForkTime = dec.OverridePassedForkTime
|
||||
}
|
||||
if dec.OverrideBohr != nil {
|
||||
c.OverrideBohr = dec.OverrideBohr
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ func TestInvalidLogFilterCreation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilterUninstall tests invalid getLogs requests
|
||||
// TestInvalidGetLogsRequest tests invalid getLogs requests
|
||||
func TestInvalidGetLogsRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -136,7 +136,7 @@ func (p *Peer) dispatchRequest(req *Request) error {
|
||||
}
|
||||
}
|
||||
|
||||
// dispatchRequest fulfils a pending request and delivers it to the requested
|
||||
// dispatchResponse fulfils a pending request and delivers it to the requested
|
||||
// sink.
|
||||
func (p *Peer) dispatchResponse(res *Response, metadata func() interface{}) error {
|
||||
resOp := &response{
|
||||
|
@ -361,7 +361,7 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
|
||||
return json.tx, err
|
||||
}
|
||||
|
||||
// TransactionInBlock returns a single transaction at index in the given block.
|
||||
// TransactionsInBlock returns a single transaction at index in the given block.
|
||||
func (ec *Client) TransactionsInBlock(ctx context.Context, number *big.Int) ([]*types.Transaction, error) {
|
||||
var rpcTxs []*rpcTransaction
|
||||
err := ec.c.CallContext(ctx, &rpcTxs, "eth_getTransactionsByBlockNumber", toBlockNumArg(number))
|
||||
@ -376,7 +376,7 @@ func (ec *Client) TransactionsInBlock(ctx context.Context, number *big.Int) ([]*
|
||||
return txs, err
|
||||
}
|
||||
|
||||
// TransactionInBlock returns a single transaction at index in the given block.
|
||||
// TransactionRecipientsInBlock returns a single transaction at index in the given block.
|
||||
func (ec *Client) TransactionRecipientsInBlock(ctx context.Context, number *big.Int) ([]*types.Receipt, error) {
|
||||
var rs []*types.Receipt
|
||||
err := ec.c.CallContext(ctx, &rs, "eth_getTransactionReceiptsByBlockNumber", toBlockNumArg(number))
|
||||
|
1
go.sum
1
go.sum
@ -326,7 +326,6 @@ github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R
|
||||
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
|
@ -870,7 +870,10 @@ func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFin
|
||||
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized)
|
||||
}
|
||||
|
||||
var err error
|
||||
currentTurnLength, err := s.b.CurrentTurnLength()
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
}
|
||||
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
@ -879,7 +882,7 @@ func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFin
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
}
|
||||
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized)
|
||||
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength))
|
||||
|
||||
return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber))
|
||||
}
|
||||
@ -894,7 +897,10 @@ func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFina
|
||||
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized)
|
||||
}
|
||||
|
||||
var err error
|
||||
currentTurnLength, err := s.b.CurrentTurnLength()
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
}
|
||||
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
@ -903,7 +909,7 @@ func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFina
|
||||
if err != nil { // impossible
|
||||
return nil, err
|
||||
}
|
||||
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized)
|
||||
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength))
|
||||
|
||||
return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx)
|
||||
}
|
||||
|
@ -631,8 +631,9 @@ func (b testBackend) TxPoolContentFrom(addr common.Address) ([]*types.Transactio
|
||||
func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event.Subscription {
|
||||
panic("implement me")
|
||||
}
|
||||
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
|
||||
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
|
||||
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
|
||||
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
|
||||
func (b testBackend) CurrentTurnLength() (uint8, error) { return 1, nil }
|
||||
func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
@ -89,6 +89,8 @@ type Backend interface {
|
||||
|
||||
ChainConfig() *params.ChainConfig
|
||||
Engine() consensus.Engine
|
||||
// CurrentTurnLength return the turnLength at the latest block
|
||||
CurrentTurnLength() (uint8, error)
|
||||
|
||||
// This is copied from filters.Backend
|
||||
// eth/filters needs to be initialized from this backend type, so methods needed by
|
||||
|
@ -416,6 +416,8 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
|
||||
|
||||
func (b *backendMock) Engine() consensus.Engine { return nil }
|
||||
|
||||
func (b *backendMock) CurrentTurnLength() (uint8, error) { return 1, nil }
|
||||
|
||||
func (b *backendMock) MevRunning() bool { return false }
|
||||
func (b *backendMock) HasBuilder(builder common.Address) bool { return false }
|
||||
func (b *backendMock) MevParams() *types.MevParams {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/bidutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@ -82,6 +83,7 @@ type bidSimulator struct {
|
||||
delayLeftOver time.Duration
|
||||
minGasPrice *big.Int
|
||||
chain *core.BlockChain
|
||||
txpool *txpool.TxPool
|
||||
chainConfig *params.ChainConfig
|
||||
engine consensus.Engine
|
||||
bidWorker bidWorker
|
||||
@ -118,7 +120,7 @@ func newBidSimulator(
|
||||
config *MevConfig,
|
||||
delayLeftOver time.Duration,
|
||||
minGasPrice *big.Int,
|
||||
chain *core.BlockChain,
|
||||
eth Backend,
|
||||
chainConfig *params.ChainConfig,
|
||||
engine consensus.Engine,
|
||||
bidWorker bidWorker,
|
||||
@ -127,7 +129,8 @@ func newBidSimulator(
|
||||
config: config,
|
||||
delayLeftOver: delayLeftOver,
|
||||
minGasPrice: minGasPrice,
|
||||
chain: chain,
|
||||
chain: eth.BlockChain(),
|
||||
txpool: eth.TxPool(),
|
||||
chainConfig: chainConfig,
|
||||
engine: engine,
|
||||
bidWorker: bidWorker,
|
||||
@ -141,7 +144,7 @@ func newBidSimulator(
|
||||
simulatingBid: make(map[common.Hash]*BidRuntime),
|
||||
}
|
||||
|
||||
b.chainHeadSub = chain.SubscribeChainHeadEvent(b.chainHeadCh)
|
||||
b.chainHeadSub = b.chain.SubscribeChainHeadEvent(b.chainHeadCh)
|
||||
|
||||
if config.Enabled {
|
||||
b.bidReceiving.Store(true)
|
||||
@ -409,7 +412,7 @@ func (b *bidSimulator) clearLoop() {
|
||||
}
|
||||
delete(b.bestBid, parentHash)
|
||||
for k, v := range b.bestBid {
|
||||
if v.bid.BlockNumber <= blockNumber-core.TriesInMemory {
|
||||
if v.bid.BlockNumber <= blockNumber-b.chain.TriesInMemory() {
|
||||
v.env.discard()
|
||||
delete(b.bestBid, k)
|
||||
}
|
||||
@ -418,7 +421,7 @@ func (b *bidSimulator) clearLoop() {
|
||||
|
||||
b.simBidMu.Lock()
|
||||
for k, v := range b.simulatingBid {
|
||||
if v.bid.BlockNumber <= blockNumber-core.TriesInMemory {
|
||||
if v.bid.BlockNumber <= blockNumber-b.chain.TriesInMemory() {
|
||||
v.env.discard()
|
||||
delete(b.simulatingBid, k)
|
||||
}
|
||||
@ -625,16 +628,39 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
|
||||
// check if bid gas price is lower than min gas price
|
||||
{
|
||||
bidGasUsed := uint64(0)
|
||||
bidGasFee := bidRuntime.env.state.GetBalance(consensus.SystemAddress)
|
||||
bidGasFee := big.NewInt(0)
|
||||
|
||||
for _, receipt := range bidRuntime.env.receipts {
|
||||
bidGasUsed += receipt.GasUsed
|
||||
for i, receipt := range bidRuntime.env.receipts {
|
||||
tx := bidRuntime.env.txs[i]
|
||||
if !b.txpool.Has(tx.Hash()) {
|
||||
bidGasUsed += receipt.GasUsed
|
||||
effectiveTip, er := tx.EffectiveGasTip(bidRuntime.env.header.BaseFee)
|
||||
if er != nil {
|
||||
err = errors.New("failed to calculate effective tip")
|
||||
return
|
||||
}
|
||||
|
||||
if bidRuntime.env.header.BaseFee != nil {
|
||||
effectiveTip.Add(effectiveTip, bidRuntime.env.header.BaseFee)
|
||||
}
|
||||
|
||||
gasFee := new(big.Int).Mul(effectiveTip, new(big.Int).SetUint64(receipt.GasUsed))
|
||||
bidGasFee.Add(bidGasFee, gasFee)
|
||||
|
||||
if tx.Type() == types.BlobTxType {
|
||||
blobFee := new(big.Int).Mul(receipt.BlobGasPrice, new(big.Int).SetUint64(receipt.BlobGasUsed))
|
||||
bidGasFee.Add(bidGasFee, blobFee)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bidGasPrice := new(big.Int).Div(bidGasFee.ToBig(), new(big.Int).SetUint64(bidGasUsed))
|
||||
if bidGasPrice.Cmp(b.minGasPrice) < 0 {
|
||||
err = errors.New("bid gas price is lower than min gas price")
|
||||
return
|
||||
// if bid txs are all from mempool, do not check gas price
|
||||
if bidGasUsed != 0 {
|
||||
bidGasPrice := new(big.Int).Div(bidGasFee, new(big.Int).SetUint64(bidGasUsed))
|
||||
if bidGasPrice.Cmp(b.minGasPrice) < 0 {
|
||||
err = fmt.Errorf("bid gas price is lower than min gas price, bid:%v, min:%v", bidGasPrice, b.minGasPrice)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
|
||||
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, false),
|
||||
}
|
||||
|
||||
miner.bidSimulator = newBidSimulator(&config.Mev, config.DelayLeftOver, config.GasPrice, eth.BlockChain(), chainConfig, engine, miner.worker)
|
||||
miner.bidSimulator = newBidSimulator(&config.Mev, config.DelayLeftOver, config.GasPrice, eth, chainConfig, engine, miner.worker)
|
||||
miner.worker.setBestBidFetcher(miner.bidSimulator)
|
||||
|
||||
miner.wg.Add(1)
|
||||
|
@ -1032,6 +1032,8 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
||||
}
|
||||
if w.chainConfig.Parlia == nil {
|
||||
header.ParentBeaconRoot = genParams.beaconRoot
|
||||
} else if w.chainConfig.IsBohr(header.Number, header.Time) {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
}
|
||||
// Could potentially happen if starting to mine in an odd state.
|
||||
|
@ -195,7 +195,7 @@ var (
|
||||
CancunTime: newUint64(1713330442), // 2024-04-17 05:07:22 AM UTC
|
||||
HaberTime: newUint64(1716962820), // 2024-05-29 06:07:00 AM UTC
|
||||
HaberFixTime: newUint64(1719986788), // 2024-07-03 06:06:28 AM UTC
|
||||
BohrTime: nil,
|
||||
BohrTime: newUint64(1724116996), // 2024-08-20 01:23:16 AM UTC
|
||||
|
||||
Parlia: &ParliaConfig{
|
||||
Period: 3,
|
||||
|
@ -192,6 +192,11 @@ var (
|
||||
MinBlocksForBlobRequests uint64 = 524288 // it keeps blob data available for ~18.2 days in local, ref: https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP-336.md#51-parameters.
|
||||
DefaultExtraReserveForBlobRequests uint64 = 1 * (24 * 3600) / 3 // it adds more time for expired blobs for some request cases, like expiry blob when remote peer is syncing, default 1 day.
|
||||
BreatheBlockInterval uint64 = 86400 // Controls the interval for updateValidatorSetV2
|
||||
// used for testing:
|
||||
// [1,9] except 2 --> used as turn length directly
|
||||
// 2 --> use random values to test switching turn length
|
||||
// 0 and other values --> get turn length from contract
|
||||
FixedTurnLength uint64 = 0
|
||||
)
|
||||
|
||||
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 4 // Minor version component of the current release
|
||||
VersionPatch = 12 // Patch version component of the current release
|
||||
VersionPatch = 13 // Patch version component of the current release
|
||||
VersionMeta = "" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
@ -1,8 +1,19 @@
|
||||
From 32329440626abd6e9668c2d5bd2e7b719e951e01 Mon Sep 17 00:00:00 2001
|
||||
From: NathanBSC <Nathan.l@nodereal.io>
|
||||
Date: Wed, 31 Jul 2024 15:01:28 +0800
|
||||
Subject: [PATCH] diff go ethereum
|
||||
|
||||
---
|
||||
core/vm/contracts.go | 10 ----------
|
||||
core/vm/jump_table.go | 2 +-
|
||||
params/protocol_params.go | 2 +-
|
||||
3 files changed, 2 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
|
||||
index 5988bb15f..c92cbf542 100644
|
||||
index 38a6cac24..7eb29c3ed 100644
|
||||
--- a/core/vm/contracts.go
|
||||
+++ b/core/vm/contracts.go
|
||||
@@ -83,9 +83,6 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
|
||||
@@ -84,9 +84,6 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
|
||||
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
|
||||
common.BytesToAddress([]byte{9}): &blake2F{},
|
||||
@ -12,7 +23,7 @@ index 5988bb15f..c92cbf542 100644
|
||||
}
|
||||
|
||||
var PrecompiledContractsNano = map[common.Address]PrecompiledContract{
|
||||
@@ -238,13 +235,6 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
|
||||
@@ -239,13 +236,6 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
|
||||
common.BytesToAddress([]byte{9}): &blake2F{},
|
||||
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},
|
||||
@ -25,7 +36,7 @@ index 5988bb15f..c92cbf542 100644
|
||||
- common.BytesToAddress([]byte{105}): &secp256k1SignatureRecover{},
|
||||
}
|
||||
|
||||
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
|
||||
// PrecompiledContractsHaber contains the default set of pre-compiled Ethereum
|
||||
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
|
||||
index 70c543f14..65716f944 100644
|
||||
--- a/core/vm/jump_table.go
|
||||
@ -40,7 +51,7 @@ index 70c543f14..65716f944 100644
|
||||
enable3860(&instructionSet) // Limit and meter initcode
|
||||
|
||||
diff --git a/params/protocol_params.go b/params/protocol_params.go
|
||||
index b84fa148f..97bf6c4d2 100644
|
||||
index 65b2d942c..bb085512f 100644
|
||||
--- a/params/protocol_params.go
|
||||
+++ b/params/protocol_params.go
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
@ -52,3 +63,6 @@ index b84fa148f..97bf6c4d2 100644
|
||||
MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
|
||||
MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
|
||||
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
@ -11,4 +11,5 @@ echo "PASS",$PASS,"FAIL",$FAIL
|
||||
if [ $FAIL -ne 0 ]
|
||||
then
|
||||
cat fail.log
|
||||
exit 1
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user