From 922c7ed22d9ef8e9616d4d9e31d41fb7861d8974 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Mon, 29 Apr 2024 18:33:02 +0800 Subject: [PATCH 01/18] feat: make validators to produce consecutive blocks defined by BEP-341 --- cmd/extradump/extradump_test.go | 90 +++++++++++- cmd/extradump/main.go | 16 +- cmd/geth/config.go | 3 + cmd/geth/main.go | 1 + cmd/utils/flags.go | 6 + consensus/beacon/consensus.go | 5 + consensus/clique/clique.go | 5 + consensus/consensus.go | 3 + consensus/ethash/consensus.go | 5 + consensus/parlia/abi.go | 13 ++ consensus/parlia/bohrFork.go | 61 ++++++++ consensus/parlia/parlia.go | 135 ++++++++++++++--- consensus/parlia/snapshot.go | 137 +++++++++++++----- .../bohr/chapel/ValidatorContract | 0 .../bohr/mainnet/ValidatorContract | 0 core/systemcontracts/bohr/types.go | 15 ++ core/systemcontracts/upgrade.go | 6 + core/vote/vote_manager.go | 39 ++++- internal/ethapi/api.go | 14 +- params/protocol_params.go | 1 + 20 files changed, 490 insertions(+), 65 deletions(-) create mode 100644 consensus/parlia/bohrFork.go create mode 100644 core/systemcontracts/bohr/chapel/ValidatorContract create mode 100644 core/systemcontracts/bohr/mainnet/ValidatorContract create mode 100644 core/systemcontracts/bohr/types.go diff --git a/cmd/extradump/extradump_test.go b/cmd/extradump/extradump_test.go index f203d322e6..1c41fb7ec9 100644 --- a/cmd/extradump/extradump_test.go +++ b/cmd/extradump/extradump_test.go @@ -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 Term---|---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.TurnTerm + var want = uint8(4) + if *have != want { + t.Fatalf("extra.TurnTerm 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,51 @@ 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 := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b878f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed28407080048b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200" + 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) + } + } + } + + // case 6, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Term---|---Vote Attestation---|---Extra Seal---| { - extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b878f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed284070808b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200" + extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b87804f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed28407080048b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200" extra, err := parseExtra(extraData) assert.NoError(t, err) { @@ -104,5 +181,12 @@ func TestExtraParse(t *testing.T) { t.Fatalf("extra.Data.TargetNumber mismatch, have %d, want %d", have, want) } } + { + var have = extra.TurnTerm + var want = uint8(4) + if *have != want { + t.Fatalf("extra.TurnTerm mismatch, have %d, want %d", *have, want) + } + } } } diff --git a/cmd/extradump/main.go b/cmd/extradump/main.go index bb06735147..aaec5ea80a 100644 --- a/cmd/extradump/main.go +++ b/cmd/extradump/main.go @@ -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 Term (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 + TurnTerm *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 TurnTerm + if dataLength > 0 { + if data[0] != '\xf8' { + extra.TurnTerm = &data[0] + data = data[1:] + dataLength = len(data) + } + } } // parse Vote Attestation @@ -148,6 +158,10 @@ func prettyExtra(extra Extra) { } } + if extra.TurnTerm != nil { + fmt.Printf("TurnTerm : %d\n", *extra.TurnTerm) + } + if extra.VoteAttestation != nil { fmt.Printf("Attestation :\n") fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count()) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index e535dc8155..7ad20539e0 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -214,6 +214,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.OverrideUseRandTurnTerm.Name) { + params.UseRandTurnTerm = ctx.Bool(utils.OverrideUseRandTurnTerm.Name) + } backend, eth := utils.RegisterEthService(stack, &cfg.Eth) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 9763794f7e..172752da3e 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -80,6 +80,7 @@ var ( utils.OverrideMinBlocksForBlobRequests, utils.OverrideDefaultExtraReserveForBlobRequests, utils.OverrideBreatheBlockInterval, + utils.OverrideUseRandTurnTerm, utils.EnablePersonal, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8707840692..c929a0bae2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -349,6 +349,12 @@ var ( Value: params.BreatheBlockInterval, Category: flags.EthCategory, } + OverrideUseRandTurnTerm = &cli.BoolFlag{ + Name: "override.useRandTurnTerm", + Usage: "It use random values for turn terms instead of reading from the contract, only for testing purpose", + Value: params.UseRandTurnTerm, + Category: flags.EthCategory, + } SyncModeFlag = &flags.TextMarshalerFlag{ Name: "syncmode", Usage: `Blockchain sync mode ("snap" or "full")`, diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 9e1c39b99a..ecabe15959 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -333,6 +333,11 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [ return abort, results } +// CurrentTurnTerm return the turnTerm at the latest block +func (beacon *Beacon) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { + return nil, errors.New("not implemented") +} + // NextInTurnValidator return the next in-turn validator for header func (beacon *Beacon) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index ddacbc2e6e..cc83ef1786 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -511,6 +511,11 @@ func (c *Clique) verifySeal(snap *Snapshot, header *types.Header, parents []*typ return nil } +// CurrentTurnTerm return the turnTerm at the latest block +func (c *Clique) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { + return nil, errors.New("not implemented") +} + // NextInTurnValidator return the next in-turn validator for header func (c *Clique) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/consensus.go b/consensus/consensus.go index 7c65acb359..fb4cd63a23 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -97,6 +97,9 @@ type Engine interface { // rules of a given engine. VerifyUncles(chain ChainReader, block *types.Block) error + // CurrentTurnTerm return the turnTerm at the latest block + CurrentTurnTerm(chain ChainHeaderReader) (turnTerm *uint64, err error) + // NextInTurnValidator return the next in-turn validator for header NextInTurnValidator(chain ChainHeaderReader, header *types.Header) (common.Address, error) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index db730fab29..c9234e2ce7 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -489,6 +489,11 @@ var FrontierDifficultyCalculator = calcDifficultyFrontier var HomesteadDifficultyCalculator = calcDifficultyHomestead var DynamicDifficultyCalculator = makeDifficultyCalculator +// CurrentTurnTerm return the turnTerm at the latest block +func (ethash *Ethash) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { + return nil, errors.New("not implemented") +} + // NextInTurnValidator return the next in-turn validator for header func (ethash *Ethash) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/parlia/abi.go b/consensus/parlia/abi.go index 0d9ab54cd5..401db48911 100644 --- a/consensus/parlia/abi.go +++ b/consensus/parlia/abi.go @@ -2306,6 +2306,19 @@ const validatorSetABI = ` ], "stateMutability": "view" }, + { + "inputs": [], + "name": "getTurnTerm", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "type": "function", "name": "getValidators", diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go new file mode 100644 index 0000000000..2b712b3216 --- /dev/null +++ b/consensus/parlia/bohrFork.go @@ -0,0 +1,61 @@ +package parlia + +import ( + "context" + "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/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) getTurnTerm(header *types.Header) (turnTerm *big.Int, err error) { + if params.UseRandTurnTerm { + return p.getRandTurnTerm(header) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + method := "getTurnTerm" + 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 getTurnTerm", "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(&turnTerm, method, result); err != nil { + return nil, err + } + + return turnTerm, nil +} + +// getRandTurnTerm returns a random valid value, used to test switching turn terms +func (p *Parlia) getRandTurnTerm(header *types.Header) (turnTerm *big.Int, err error) { + turnTerms := [8]uint8{1, 3, 4, 5, 6, 7, 8, 9} + r := mrand.New(mrand.NewSource(int64(header.Time))) + termIndex := int(r.Int31n(int32(len(turnTerms)))) + return big.NewInt(int64(turnTerms[termIndex])), nil +} diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 69b82d408c..6f99dec037 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -54,10 +54,12 @@ const ( 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 + defaultTurnTerm = uint64(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. + turnTermSize = 1 // Fixed number of extra-data suffix bytes reserved for turnTerm validatorBytesLengthBeforeLuban = common.AddressLength validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength @@ -136,6 +138,10 @@ var ( // list of validators different than the one the local node calculated. errMismatchingEpochValidators = errors.New("mismatching validator list on epoch block") + // errMismatchingEpochTurnTerm is returned if a sprint block contains a + // turn term different than the one the local node calculated. + errMismatchingEpochTurnTerm = errors.New("mismatching turn term on epoch block") + // errInvalidDifficulty is returned if the difficulty of a block is missing. errInvalidDifficulty = errors.New("invalid difficulty") @@ -365,6 +371,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 Term (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 @@ -381,11 +388,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 += turnTermSize + } + if num == 0 || len(header.Extra) < extraMinLen { + return nil + } return header.Extra[start:end] } @@ -404,11 +415,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 { - return nil, nil - } start := extraVanity + validatorNumberSize + num*validatorBytesLength + if chainConfig.IsBohr(header.Number, header.Time) { + start += turnTermSize + } end := len(header.Extra) - extraSeal + if end <= start { + return nil, nil + } attestationBytes = header.Extra[start:end] } @@ -884,6 +898,35 @@ func (p *Parlia) prepareValidators(header *types.Header) error { return nil } +func (p *Parlia) prepareTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { + if header.Number.Uint64()%p.config.Epoch != 0 || + !p.chainConfig.IsBohr(header.Number, header.Time) { + return nil + } + + parent := chain.GetHeaderByHash(header.ParentHash) + if parent == nil { + return errors.New("parent not found") + } + + if p.chainConfig.IsBohr(parent.Number, parent.Time) { + turnTerm, err := p.getTurnTerm(parent) + if err != nil { + return err + } + if turnTerm == nil { + return errors.New("unexpected error when getTurnTerm") + } + header.Extra = append(header.Extra, byte(int(turnTerm.Int64()))) + log.Debug("prepareTurnTerm", "turnTerm", turnTerm.Int64()) + } else { + log.Debug("prepareTurnTerm", "turnTerm", "defaultTurnTerm") + header.Extra = append(header.Extra, byte(int(defaultTurnTerm))) + } + + 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 @@ -967,6 +1010,16 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head return nil } +// CurrentTurnTerm return the turnTerm at the latest block +func (p *Parlia) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { + currentHeader := chain.CurrentHeader() + snap, err := p.snapshot(chain, currentHeader.Number.Uint64(), currentHeader.Hash(), nil) + if err != nil { + return nil, err + } + return &snap.TurnTerm, nil +} + // NextInTurnValidator return the next in-turn validator for header func (p *Parlia) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil) @@ -1015,6 +1068,9 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header return err } + if err := p.prepareTurnTerm(chain, header); err != nil { + return err + } // add extra seal space header.Extra = append(header.Extra, make([]byte, extraSeal)...) @@ -1065,6 +1121,42 @@ func (p *Parlia) verifyValidators(header *types.Header) error { return nil } +func (p *Parlia) verifyTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { + if header.Number.Uint64()%p.config.Epoch != 0 || + !p.chainConfig.IsBohr(header.Number, header.Time) { + return nil + } + + turnTermFromHeader, err := parseTurnTerm(header, p.chainConfig, p.config) + if err != nil { + return err + } + if turnTermFromHeader != nil { + parent := chain.GetHeaderByHash(header.ParentHash) + if parent == nil { + return errors.New("parent not found") + } + + if p.chainConfig.IsBohr(parent.Number, parent.Time) { + turnTermFromContract, err := p.getTurnTerm(parent) + if err != nil { + return err + } + if turnTermFromContract != nil && uint8(turnTermFromContract.Int64()) == *turnTermFromHeader { + log.Debug("verifyTurnTerm", "turnTerm", turnTermFromContract.Int64()) + return nil + } + } else { + if uint8(defaultTurnTerm) == *turnTermFromHeader { + log.Debug("verifyTurnTerm", "turnTerm", "defaultTurnTerm") + return nil + } + } + } + + return errMismatchingEpochTurnTerm +} + 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 { @@ -1159,6 +1251,10 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade return err } + if err := p.verifyTurnTerm(chain, header); err != nil { + return err + } + cx := chainContext{Chain: chain, parlia: p} parent := chain.GetHeaderByHash(header.ParentHash) @@ -1185,7 +1281,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) @@ -1276,7 +1372,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) @@ -1900,31 +1996,24 @@ 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 @@ -1933,7 +2022,7 @@ func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Ad // Exclude the recently signed validators temp := make([]common.Address, 0, len(validators)) for _, addr := range validators { - if _, ok := recentsMap[addr]; ok { + if snap.signRecentlyByCounts(addr, counts) { continue } temp = append(temp, addr) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 0da0929e7c..86793e8bc4 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -43,6 +43,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 + TurnTerm uint64 `json:"turn_term"` // 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 +73,7 @@ func newSnapshot( sigCache: sigCache, Number: number, Hash: hash, + TurnTerm: defaultTurnTerm, Recents: make(map[uint64]common.Address), RecentForkHashes: make(map[uint64]string), Validators: make(map[common.Address]*ValidatorInfo), @@ -114,6 +116,10 @@ func loadSnapshot(config *params.ParliaConfig, sigCache *lru.ARCCache, db ethdb. if err := json.Unmarshal(blob, snap); err != nil { return nil, err } + if snap.TurnTerm == 0 { // no TurnTerm field in old snapshots + snap.TurnTerm = defaultTurnTerm + } + snap.config = config snap.sigCache = sigCache snap.ethAPI = ethAPI @@ -138,6 +144,7 @@ func (s *Snapshot) copy() *Snapshot { sigCache: s.sigCache, Number: s.Number, Hash: s.Hash, + TurnTerm: s.TurnTerm, Validators: make(map[common.Address]*ValidatorInfo), Recents: make(map[uint64]common.Address), RecentForkHashes: make(map[uint64]string), @@ -210,17 +217,45 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C } } -func (s *Snapshot) SignRecently(validator common.Address) bool { +func (s *Snapshot) versionHistoryCheckLen() uint64 { + return uint64(len(s.Validators)) * s.TurnTerm +} + +func (s *Snapshot) minerHistoryCheckLen() uint64 { + return (uint64(len(s.Validators)) / 2) * s.TurnTerm +} + +func (s *Snapshot) countRecents() map[common.Address]uint64 { + leftHistoryBound := uint64(0) // the bound is excluded + checkHistoryLength := s.minerHistoryCheckLen() + if s.Number > checkHistoryLength { + leftHistoryBound = s.Number - checkHistoryLength + } + counts := make(map[common.Address]uint64, len(s.Validators)) 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 - } + if seen <= leftHistoryBound || recent == (common.Address{}) { + continue } + counts[recent] += 1 } + return counts +} + +func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint64) bool { + if seenTimes, ok := counts[validator]; ok && seenTimes >= s.TurnTerm { + if seenTimes > s.TurnTerm { + 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 +282,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 { // `+1` for genesis block which has no miner 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 +296,36 @@ 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 // 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() { + checkpointHeader := FindAncientHeader(header, snap.minerHistoryCheckLen(), chain, parents) if checkpointHeader == nil { return nil, consensus.ErrUnknownAncestor } + oldVersionsLen := snap.versionHistoryCheckLen() + // get turnTerm from headers and use that for new turnTerm + turnTerm, err := parseTurnTerm(checkpointHeader, chainConfig, s.config) + if err != nil { + return nil, err + } + if turnTerm != nil { + snap.TurnTerm = uint64(*turnTerm) + log.Debug("validator set switch", "turnTerm", *turnTerm) + } + // get validators from headers and use that for new validator set newValArr, voteAddrs, err := parseValidators(checkpointHeader, chainConfig, s.config) if err != nil { @@ -289,18 +341,25 @@ 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)) + if chainConfig.IsBohr(header.Number, header.Time) { + getClearKey := func(blockNumber uint64) uint64 { + return 1<<63 | (blockNumber / s.config.Epoch) // impossible used as a block number } - } - 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)) + epochClearKey := getClearKey(number) + // in a epoch, after the first validator set switch, minerHistoryCheckLen() may become larger, + // so the unexpected second switch will happen, don't clear up the `Recents` in this kind of scene. + if _, ok := snap.Recents[epochClearKey]; !ok { + snap.Recents = make(map[uint64]common.Address) // without this logic, there will be several off-turn blocks when do validator set switch + snap.Recents[epochClearKey] = 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,10 +369,12 @@ 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)) @@ -333,15 +394,13 @@ func (s *Snapshot) validators() []common.Address { // 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 + return s.inturnValidator() == validator } // inturnValidator returns the validator at a given block height. func (s *Snapshot) inturnValidator() common.Address { validators := s.validators() - offset := (s.Number + 1) % uint64(len(validators)) + offset := (s.Number + 1) / s.TurnTerm % uint64(len(validators)) return validators[offset] } @@ -379,12 +438,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 +463,24 @@ func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parl return cnsAddrs, voteAddrs, nil } +func parseTurnTerm(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, errors.New("invalid turnTerm") + } + num := int(header.Extra[extraVanity]) + pos := extraVanity + validatorNumberSize + num*validatorBytesLength + if len(header.Extra) <= pos { + return nil, errors.New("invalid turnTerm") + } + turnterm := header.Extra[pos] + return &turnterm, 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++ { diff --git a/core/systemcontracts/bohr/chapel/ValidatorContract b/core/systemcontracts/bohr/chapel/ValidatorContract new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/systemcontracts/bohr/mainnet/ValidatorContract b/core/systemcontracts/bohr/mainnet/ValidatorContract new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/systemcontracts/bohr/types.go b/core/systemcontracts/bohr/types.go new file mode 100644 index 0000000000..77e3e836dc --- /dev/null +++ b/core/systemcontracts/bohr/types.go @@ -0,0 +1,15 @@ +package bohr + +import _ "embed" + +// contract codes for Mainnet upgrade +var ( + //go:embed mainnet/ValidatorContract + MainnetValidatorContract string +) + +// contract codes for Chapel upgrade +var ( + //go:embed chapel/ValidatorContract + ChapelValidatorContract string +) diff --git a/core/systemcontracts/upgrade.go b/core/systemcontracts/upgrade.go index 341fe7e512..c2e73855a3 100644 --- a/core/systemcontracts/upgrade.go +++ b/core/systemcontracts/upgrade.go @@ -78,6 +78,8 @@ var ( feynmanFixUpgrade = make(map[string]*Upgrade) haberFixUpgrade = make(map[string]*Upgrade) + + bohrUpgrade = make(map[string]*Upgrade) ) func init() { @@ -816,6 +818,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 */ diff --git a/core/vote/vote_manager.go b/core/vote/vote_manager.go index a7eaf10b09..8f6bf1fc33 100644 --- a/core/vote/vote_manager.go +++ b/core/vote/vote_manager.go @@ -3,6 +3,7 @@ package vote import ( "bytes" "fmt" + "math/big" "sync" "github.com/ethereum/go-ethereum/common" @@ -17,7 +18,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 { @@ -144,7 +151,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 } @@ -191,6 +198,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[:]) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e31cee15ec..188f99fb3a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -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 + currentTurnTerm, err := s.b.Engine().CurrentTurnTerm(s.b.Chain()) + 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((*currentTurnTerm))) 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 + currentTurnTerm, err := s.b.Engine().CurrentTurnTerm(s.b.Chain()) + 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((*currentTurnTerm))) return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx) } diff --git a/params/protocol_params.go b/params/protocol_params.go index 2ecb92bc61..a6c08a9e42 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -192,6 +192,7 @@ 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 + UseRandTurnTerm bool = false // Use random values to test switching turn terms ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations From e02d6ac68c0a722be24f9a5baf2bc314c2d608a1 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Mon, 3 Jun 2024 11:17:43 +0800 Subject: [PATCH 02/18] internal/ethapi: move CurrentTurnTerm() into Backend from Engine --- consensus/beacon/consensus.go | 5 ----- consensus/clique/clique.go | 5 ----- consensus/consensus.go | 3 --- consensus/ethash/consensus.go | 5 ----- consensus/parlia/parlia.go | 10 ---------- consensus/parlia/snapshot.go | 6 ++---- eth/api_backend.go | 15 +++++++++++++++ go.mod | 2 +- internal/ethapi/api.go | 8 ++++---- internal/ethapi/api_test.go | 1 + internal/ethapi/backend.go | 2 ++ internal/ethapi/transaction_args_test.go | 2 ++ 12 files changed, 27 insertions(+), 37 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index ecabe15959..9e1c39b99a 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -333,11 +333,6 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [ return abort, results } -// CurrentTurnTerm return the turnTerm at the latest block -func (beacon *Beacon) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { - return nil, errors.New("not implemented") -} - // NextInTurnValidator return the next in-turn validator for header func (beacon *Beacon) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index cc83ef1786..ddacbc2e6e 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -511,11 +511,6 @@ func (c *Clique) verifySeal(snap *Snapshot, header *types.Header, parents []*typ return nil } -// CurrentTurnTerm return the turnTerm at the latest block -func (c *Clique) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { - return nil, errors.New("not implemented") -} - // NextInTurnValidator return the next in-turn validator for header func (c *Clique) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/consensus.go b/consensus/consensus.go index fb4cd63a23..7c65acb359 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -97,9 +97,6 @@ type Engine interface { // rules of a given engine. VerifyUncles(chain ChainReader, block *types.Block) error - // CurrentTurnTerm return the turnTerm at the latest block - CurrentTurnTerm(chain ChainHeaderReader) (turnTerm *uint64, err error) - // NextInTurnValidator return the next in-turn validator for header NextInTurnValidator(chain ChainHeaderReader, header *types.Header) (common.Address, error) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c9234e2ce7..db730fab29 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -489,11 +489,6 @@ var FrontierDifficultyCalculator = calcDifficultyFrontier var HomesteadDifficultyCalculator = calcDifficultyHomestead var DynamicDifficultyCalculator = makeDifficultyCalculator -// CurrentTurnTerm return the turnTerm at the latest block -func (ethash *Ethash) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { - return nil, errors.New("not implemented") -} - // NextInTurnValidator return the next in-turn validator for header func (ethash *Ethash) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { return common.Address{}, errors.New("not implemented") diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 6f99dec037..191861815f 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1010,16 +1010,6 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head return nil } -// CurrentTurnTerm return the turnTerm at the latest block -func (p *Parlia) CurrentTurnTerm(chain consensus.ChainHeaderReader) (turnTerm *uint64, err error) { - currentHeader := chain.CurrentHeader() - snap, err := p.snapshot(chain, currentHeader.Number.Uint64(), currentHeader.Hash(), nil) - if err != nil { - return nil, err - } - return &snap.TurnTerm, nil -} - // NextInTurnValidator return the next in-turn validator for header func (p *Parlia) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) { snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 86793e8bc4..7119b210d3 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "sort" lru "github.com/hashicorp/golang-lru" @@ -342,10 +343,7 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } } if chainConfig.IsBohr(header.Number, header.Time) { - getClearKey := func(blockNumber uint64) uint64 { - return 1<<63 | (blockNumber / s.config.Epoch) // impossible used as a block number - } - epochClearKey := getClearKey(number) + epochClearKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number // in a epoch, after the first validator set switch, minerHistoryCheckLen() may become larger, // so the unexpected second switch will happen, don't clear up the `Recents` in this kind of scene. if _, ok := snap.Recents[epochClearKey]; !ok { diff --git a/eth/api_backend.go b/eth/api_backend.go index ae698ade36..6a564b3a9c 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -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,20 @@ func (b *EthAPIBackend) Engine() consensus.Engine { return b.eth.engine } +func (b *EthAPIBackend) CurrentTurnTerm() (turnTerm uint64, err error) { + if p, ok := b.eth.engine.(*parlia.Parlia); ok { + service := p.APIs(b.Chain())[0].Service + snap, err := service.(*parlia.API).GetSnapshot(nil) + if err != nil { + return 0, err + } + + return snap.TurnTerm, nil + } + + return 1, nil +} + func (b *EthAPIBackend) CurrentHeader() *types.Header { return b.eth.blockchain.CurrentHeader() } diff --git a/go.mod b/go.mod index 904ce53692..cc80ed9739 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/cespare/cp v1.1.1 github.com/cloudflare/cloudflare-go v0.79.0 - github.com/cometbft/cometbft v0.37.0 github.com/cockroachdb/pebble v1.1.0 + github.com/cometbft/cometbft v0.37.0 github.com/consensys/gnark-crypto v0.12.1 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/crate-crypto/go-kzg-4844 v0.7.0 diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 188f99fb3a..1f476cde23 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -870,7 +870,7 @@ func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFin return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized) } - currentTurnTerm, err := s.b.Engine().CurrentTurnTerm(s.b.Chain()) + currentTurnTerm, err := s.b.CurrentTurnTerm() if err != nil { // impossible return nil, err } @@ -882,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*int64((*currentTurnTerm))) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64((currentTurnTerm))) return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber)) } @@ -897,7 +897,7 @@ func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFina return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized) } - currentTurnTerm, err := s.b.Engine().CurrentTurnTerm(s.b.Chain()) + currentTurnTerm, err := s.b.CurrentTurnTerm() if err != nil { // impossible return nil, err } @@ -909,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*int64((*currentTurnTerm))) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64((currentTurnTerm))) return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 8764f51fb6..5bd6e7ee2e 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -633,6 +633,7 @@ func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event. } func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() } +func (b testBackend) CurrentTurnTerm() (uint64, error) { return 1, nil } func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { panic("implement me") } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index fd778a3f69..a9becc5356 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -89,6 +89,8 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine + // CurrentTurnTerm return the turnTerm at the latest block + CurrentTurnTerm() (uint64, error) // This is copied from filters.Backend // eth/filters needs to be initialized from this backend type, so methods needed by diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 07a1c57a40..70ae3f14e5 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -416,6 +416,8 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } +func (b *backendMock) CurrentTurnTerm() (uint64, 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 { From 3d1c4e341c5111bcd127e03f925a6795542082ca Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Tue, 4 Jun 2024 19:22:47 +0800 Subject: [PATCH 03/18] consensus/parlia: explain turnterm in comments --- consensus/parlia/snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 7119b210d3..250e722799 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -44,7 +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 - TurnTerm uint64 `json:"turn_term"` // The consecutive number of blocks a validator receives priority for block production + TurnTerm uint64 `json:"turn_term"` // Term 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 From 429e2fd92347059cedb31238dd1694964c45712c Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Wed, 5 Jun 2024 17:51:49 +0800 Subject: [PATCH 04/18] consensus/parlia: define errInvalidTurnTerm --- consensus/parlia/parlia.go | 4 ++++ consensus/parlia/snapshot.go | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 191861815f..68e56c3d50 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -128,6 +128,10 @@ var ( // invalid list of validators (i.e. non divisible by 20 bytes). errInvalidSpanValidators = errors.New("invalid validator list on sprint end block") + // errInvalidTurnTerm is returned if a block contains an + // invalid term of turn (i.e. no data left after parsing validators). + errInvalidTurnTerm = errors.New("invalid turnTerm") + // errInvalidMixDigest is returned if a block's mix digest is non-zero. errInvalidMixDigest = errors.New("non-zero mix digest") diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 250e722799..2bfb9ef38c 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -468,12 +468,12 @@ func parseTurnTerm(header *types.Header, chainConfig *params.ChainConfig, parlia } if len(header.Extra) <= extraVanity+extraSeal { - return nil, errors.New("invalid turnTerm") + return nil, errInvalidSpanValidators } num := int(header.Extra[extraVanity]) pos := extraVanity + validatorNumberSize + num*validatorBytesLength if len(header.Extra) <= pos { - return nil, errors.New("invalid turnTerm") + return nil, errInvalidTurnTerm } turnterm := header.Extra[pos] return &turnterm, nil From cf0e65776c9b4ee864048f1240d19653f8025feb Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 6 Jun 2024 11:22:52 +0800 Subject: [PATCH 05/18] consensu/parlia: optimize prepareTurnTerm and verifyTurnTerm --- consensus/parlia/bohrFork.go | 35 +++++++++++++++++++++++-- consensus/parlia/parlia.go | 50 ++++++++---------------------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go index 2b712b3216..714b57afd4 100644 --- a/consensus/parlia/bohrFork.go +++ b/consensus/parlia/bohrFork.go @@ -2,12 +2,14 @@ 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" @@ -16,9 +18,38 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -func (p *Parlia) getTurnTerm(header *types.Header) (turnTerm *big.Int, err error) { +func (p *Parlia) getTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) (*uint8, error) { + if header.Number.Uint64()%p.config.Epoch != 0 || + !p.chainConfig.IsBohr(header.Number, header.Time) { + return nil, nil + } + + parent := chain.GetHeaderByHash(header.ParentHash) + if parent == nil { + return nil, errors.New("parent not found") + } + + var turnTerm uint8 + if p.chainConfig.IsBohr(parent.Number, parent.Time) { + turnTermFromContract, err := p.getTurnTermFromContract(parent) + if err != nil { + return nil, err + } + if turnTermFromContract == nil { + return nil, errors.New("unexpected error when getTurnTermFromContract") + } + turnTerm = uint8(turnTermFromContract.Int64()) + } else { + turnTerm = uint8(defaultTurnTerm) + } + log.Debug("getTurnTerm", "turnTerm", turnTerm) + + return &turnTerm, nil +} + +func (p *Parlia) getTurnTermFromContract(header *types.Header) (turnTerm *big.Int, err error) { if params.UseRandTurnTerm { - return p.getRandTurnTerm(header) + return p.getRandTurnTerm(header) // used as a mock to get turnTerm from the contract } ctx, cancel := context.WithCancel(context.Background()) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 68e56c3d50..10d614e97f 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -903,29 +903,13 @@ func (p *Parlia) prepareValidators(header *types.Header) error { } func (p *Parlia) prepareTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { - if header.Number.Uint64()%p.config.Epoch != 0 || - !p.chainConfig.IsBohr(header.Number, header.Time) { - return nil + turnTerm, err := p.getTurnTerm(chain, header) + if err != nil { + return err } - parent := chain.GetHeaderByHash(header.ParentHash) - if parent == nil { - return errors.New("parent not found") - } - - if p.chainConfig.IsBohr(parent.Number, parent.Time) { - turnTerm, err := p.getTurnTerm(parent) - if err != nil { - return err - } - if turnTerm == nil { - return errors.New("unexpected error when getTurnTerm") - } - header.Extra = append(header.Extra, byte(int(turnTerm.Int64()))) - log.Debug("prepareTurnTerm", "turnTerm", turnTerm.Int64()) - } else { - log.Debug("prepareTurnTerm", "turnTerm", "defaultTurnTerm") - header.Extra = append(header.Extra, byte(int(defaultTurnTerm))) + if turnTerm != nil { + header.Extra = append(header.Extra, *turnTerm) } return nil @@ -1126,25 +1110,13 @@ func (p *Parlia) verifyTurnTerm(chain consensus.ChainHeaderReader, header *types return err } if turnTermFromHeader != nil { - parent := chain.GetHeaderByHash(header.ParentHash) - if parent == nil { - return errors.New("parent not found") + turnTerm, err := p.getTurnTerm(chain, header) + if err != nil { + return err } - - if p.chainConfig.IsBohr(parent.Number, parent.Time) { - turnTermFromContract, err := p.getTurnTerm(parent) - if err != nil { - return err - } - if turnTermFromContract != nil && uint8(turnTermFromContract.Int64()) == *turnTermFromHeader { - log.Debug("verifyTurnTerm", "turnTerm", turnTermFromContract.Int64()) - return nil - } - } else { - if uint8(defaultTurnTerm) == *turnTermFromHeader { - log.Debug("verifyTurnTerm", "turnTerm", "defaultTurnTerm") - return nil - } + if turnTerm != nil && *turnTerm == *turnTermFromHeader { + log.Debug("verifyTurnTerm", "turnTerm", *turnTerm) + return nil } } From 933bd4b626684c1cac2f1b167956cac5ee0c7a7c Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Tue, 11 Jun 2024 09:37:07 +0800 Subject: [PATCH 06/18] consensus/parlia: change func minerHistoryCheckLen --- consensus/parlia/snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 2bfb9ef38c..070a819607 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -223,7 +223,7 @@ func (s *Snapshot) versionHistoryCheckLen() uint64 { } func (s *Snapshot) minerHistoryCheckLen() uint64 { - return (uint64(len(s.Validators)) / 2) * s.TurnTerm + return (uint64(len(s.Validators))/2+1)*s.TurnTerm - 1 } func (s *Snapshot) countRecents() map[common.Address]uint64 { From d458a0a2ada3610ad3b1a50f19e899bc8b911a72 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Wed, 12 Jun 2024 14:34:18 +0800 Subject: [PATCH 07/18] cmd/geth: use OverrideFixedTurnTerm to replace OverrideUseRandTurnTerm --- cmd/geth/config.go | 4 ++-- cmd/geth/main.go | 2 +- cmd/utils/flags.go | 8 ++++---- consensus/parlia/bohrFork.go | 8 ++++++-- params/protocol_params.go | 6 +++++- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 7ad20539e0..85222709aa 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -214,8 +214,8 @@ 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.OverrideUseRandTurnTerm.Name) { - params.UseRandTurnTerm = ctx.Bool(utils.OverrideUseRandTurnTerm.Name) + if ctx.IsSet(utils.OverrideFixedTurnTerm.Name) { + params.FixedTurnTerm = ctx.Uint64(utils.OverrideFixedTurnTerm.Name) } backend, eth := utils.RegisterEthService(stack, &cfg.Eth) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 172752da3e..b6a098d058 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -80,7 +80,7 @@ var ( utils.OverrideMinBlocksForBlobRequests, utils.OverrideDefaultExtraReserveForBlobRequests, utils.OverrideBreatheBlockInterval, - utils.OverrideUseRandTurnTerm, + utils.OverrideFixedTurnTerm, utils.EnablePersonal, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c929a0bae2..fcaa3860b2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -349,10 +349,10 @@ var ( Value: params.BreatheBlockInterval, Category: flags.EthCategory, } - OverrideUseRandTurnTerm = &cli.BoolFlag{ - Name: "override.useRandTurnTerm", - Usage: "It use random values for turn terms instead of reading from the contract, only for testing purpose", - Value: params.UseRandTurnTerm, + OverrideFixedTurnTerm = &cli.Uint64Flag{ + Name: "override.fixedturnterm", + Usage: "It use fixed or random values for turn terms instead of reading from the contract, only for testing purpose", + Value: params.FixedTurnTerm, Category: flags.EthCategory, } SyncModeFlag = &flags.TextMarshalerFlag{ diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go index 714b57afd4..36a851222e 100644 --- a/consensus/parlia/bohrFork.go +++ b/consensus/parlia/bohrFork.go @@ -48,8 +48,12 @@ func (p *Parlia) getTurnTerm(chain consensus.ChainHeaderReader, header *types.He } func (p *Parlia) getTurnTermFromContract(header *types.Header) (turnTerm *big.Int, err error) { - if params.UseRandTurnTerm { - return p.getRandTurnTerm(header) // used as a mock to get turnTerm from the contract + // mock to get turnTerm from the contract + if params.FixedTurnTerm >= 1 && params.FixedTurnTerm <= 9 { + if params.FixedTurnTerm == 2 { + return p.getRandTurnTerm(header) + } + return big.NewInt(int64(params.FixedTurnTerm)), nil } ctx, cancel := context.WithCancel(context.Background()) diff --git a/params/protocol_params.go b/params/protocol_params.go index a6c08a9e42..5651590cb0 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -192,7 +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 - UseRandTurnTerm bool = false // Use random values to test switching turn terms + // used for testing: + // [1,9] except 2 --> used as turn term directly + // 2 --> use random values to test switching turn terms + // 0 and other values --> get turn term from contract + FixedTurnTerm uint64 = 0 ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations From 7e3a660bcaa10f9106f3a15076a4faf6afd267bd Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 13 Jun 2024 13:47:27 +0800 Subject: [PATCH 08/18] consensus/parlia: change type to uint8 from uint64 for turnTerm --- consensus/parlia/bohrFork.go | 5 ----- consensus/parlia/parlia.go | 7 ++++++- consensus/parlia/snapshot.go | 16 ++++++++-------- eth/api_backend.go | 2 +- internal/ethapi/api.go | 4 ++-- internal/ethapi/api_test.go | 2 +- internal/ethapi/backend.go | 2 +- internal/ethapi/transaction_args_test.go | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go index 36a851222e..48395096bf 100644 --- a/consensus/parlia/bohrFork.go +++ b/consensus/parlia/bohrFork.go @@ -19,11 +19,6 @@ import ( ) func (p *Parlia) getTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) (*uint8, error) { - if header.Number.Uint64()%p.config.Epoch != 0 || - !p.chainConfig.IsBohr(header.Number, header.Time) { - return nil, nil - } - parent := chain.GetHeaderByHash(header.ParentHash) if parent == nil { return nil, errors.New("parent not found") diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 10d614e97f..6f283cf82c 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -54,7 +54,7 @@ const ( 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 - defaultTurnTerm = uint64(1) // Default consecutive number of blocks a validator receives priority for block production + defaultTurnTerm = 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 @@ -903,6 +903,11 @@ func (p *Parlia) prepareValidators(header *types.Header) error { } func (p *Parlia) prepareTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { + if header.Number.Uint64()%p.config.Epoch != 0 || + !p.chainConfig.IsBohr(header.Number, header.Time) { + return nil + } + turnTerm, err := p.getTurnTerm(chain, header) if err != nil { return err diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 070a819607..88bf0f1b8c 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -44,7 +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 - TurnTerm uint64 `json:"turn_term"` // Term of `turn`, meaning the consecutive number of blocks a validator receives priority for block production + TurnTerm uint8 `json:"turn_term"` // Term 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 @@ -219,20 +219,20 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C } func (s *Snapshot) versionHistoryCheckLen() uint64 { - return uint64(len(s.Validators)) * s.TurnTerm + return uint64(len(s.Validators)) * uint64(s.TurnTerm) } func (s *Snapshot) minerHistoryCheckLen() uint64 { - return (uint64(len(s.Validators))/2+1)*s.TurnTerm - 1 + return (uint64(len(s.Validators))/2+1)*uint64(s.TurnTerm) - 1 } -func (s *Snapshot) countRecents() map[common.Address]uint64 { +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]uint64, len(s.Validators)) + counts := make(map[common.Address]uint8, len(s.Validators)) for seen, recent := range s.Recents { if seen <= leftHistoryBound || recent == (common.Address{}) { continue @@ -242,7 +242,7 @@ func (s *Snapshot) countRecents() map[common.Address]uint64 { return counts } -func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint64) bool { +func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint8) bool { if seenTimes, ok := counts[validator]; ok && seenTimes >= s.TurnTerm { if seenTimes > s.TurnTerm { log.Warn("produce more blocks than expected!", "validator", validator, "seenTimes", seenTimes) @@ -323,7 +323,7 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea return nil, err } if turnTerm != nil { - snap.TurnTerm = uint64(*turnTerm) + snap.TurnTerm = *turnTerm log.Debug("validator set switch", "turnTerm", *turnTerm) } @@ -398,7 +398,7 @@ func (s *Snapshot) inturn(validator common.Address) bool { // inturnValidator returns the validator at a given block height. func (s *Snapshot) inturnValidator() common.Address { validators := s.validators() - offset := (s.Number + 1) / s.TurnTerm % uint64(len(validators)) + offset := (s.Number + 1) / uint64(s.TurnTerm) % uint64(len(validators)) return validators[offset] } diff --git a/eth/api_backend.go b/eth/api_backend.go index 6a564b3a9c..7f9f0087cb 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -441,7 +441,7 @@ func (b *EthAPIBackend) Engine() consensus.Engine { return b.eth.engine } -func (b *EthAPIBackend) CurrentTurnTerm() (turnTerm uint64, err error) { +func (b *EthAPIBackend) CurrentTurnTerm() (turnTerm uint8, err error) { if p, ok := b.eth.engine.(*parlia.Parlia); ok { service := p.APIs(b.Chain())[0].Service snap, err := service.(*parlia.API).GetSnapshot(nil) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1f476cde23..f1bd8fd446 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -882,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*int64((currentTurnTerm))) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnTerm)) return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber)) } @@ -909,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*int64((currentTurnTerm))) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnTerm)) return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 5bd6e7ee2e..b2846d0fd5 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -633,7 +633,7 @@ func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event. } func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() } -func (b testBackend) CurrentTurnTerm() (uint64, error) { return 1, nil } +func (b testBackend) CurrentTurnTerm() (uint8, error) { return 1, nil } func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { panic("implement me") } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index a9becc5356..1de5418806 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -90,7 +90,7 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine // CurrentTurnTerm return the turnTerm at the latest block - CurrentTurnTerm() (uint64, error) + CurrentTurnTerm() (uint8, error) // This is copied from filters.Backend // eth/filters needs to be initialized from this backend type, so methods needed by diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 70ae3f14e5..cd241bc888 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -416,7 +416,7 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } -func (b *backendMock) CurrentTurnTerm() (uint64, error) { return 1, nil } +func (b *backendMock) CurrentTurnTerm() (uint8, error) { return 1, nil } func (b *backendMock) MevRunning() bool { return false } func (b *backendMock) HasBuilder(builder common.Address) bool { return false } From 899bfad1ec13e70fb3081e78a50a303ac2fdfa7f Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 13 Jun 2024 15:19:41 +0800 Subject: [PATCH 09/18] consensus/parlia: adapt TestImpactOfValidatorOutOfService --- consensus/parlia/parlia_test.go | 57 +++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/consensus/parlia/parlia_test.go b/consensus/parlia/parlia_test.go index b03f747537..1c5309517d 100644 --- a/consensus/parlia/parlia_test.go +++ b/consensus/parlia/parlia_test.go @@ -22,22 +22,44 @@ func TestImpactOfValidatorOutOfService(t *testing.T) { testCases := []struct { totalValidators int downValidators int + turnTerm 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.turnTerm) } } -func simulateValidatorOutOfService(totalValidators int, downValidators int) { +// refer Snapshot.SignRecently +func signRecently(idx int, recents map[uint64]int, turnTerm int) bool { + recentSignTimes := 0 + for _, signIdx := range recents { + if signIdx == idx { + recentSignTimes += 1 + } + } + return recentSignTimes >= turnTerm +} + +// refer Snapshot.minerHistoryCheckLen +func minerHistoryCheckLen(totalValidators int, turnTerm int) uint64 { + return uint64(totalValidators/2+1)*uint64(turnTerm) - 1 +} + +// refer Snapshot.inturnValidator +func inturnValidator(totalValidators int, turnTerm int, height int) int { + return height / turnTerm % totalValidators +} + +func simulateValidatorOutOfService(totalValidators int, downValidators int, turnTerm 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, turnTerm) } 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, turnTerm) + 1; uint64(h) >= limit { delete(recents, uint64(h)-limit) } - proposer := h % totalValidators + proposer := inturnValidator(totalValidators, turnTerm, 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, turnTerm) + 1; uint64(h) >= limit { delete(recents, uint64(h)-limit) } - proposer := h % totalValidators + proposer := inturnValidator(totalValidators, turnTerm, h) if !isInService(proposer) || isRecentSign(proposer) { lastseen = h candidates := make(map[int]bool, totalValidators/2) From 64bb887d0d8a508d0a73930f53458687fe9be0b2 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 13 Jun 2024 18:29:55 +0800 Subject: [PATCH 10/18] consensus/parlia: fix go lint --- consensus/parlia/bohrFork.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go index 48395096bf..8531dc26fa 100644 --- a/consensus/parlia/bohrFork.go +++ b/consensus/parlia/bohrFork.go @@ -35,7 +35,7 @@ func (p *Parlia) getTurnTerm(chain consensus.ChainHeaderReader, header *types.He } turnTerm = uint8(turnTermFromContract.Int64()) } else { - turnTerm = uint8(defaultTurnTerm) + turnTerm = defaultTurnTerm } log.Debug("getTurnTerm", "turnTerm", turnTerm) From a68b85a893dc051cc3da391981a1985619de41d2 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 27 Jun 2024 18:02:49 +0800 Subject: [PATCH 11/18] consensus/parlia: more delay when mining blocks --- consensus/parlia/parlia.go | 17 +++++++++++++---- consensus/parlia/snapshot.go | 5 +++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 6f283cf82c..b653502d41 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1496,10 +1496,19 @@ 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.TurnTerm == 1 + timeForMining := time.Duration(p.config.Period) * time.Second / 2 + if snap.TurnTerm > 1 { + if snap.lastBlockInOneTurn(header.Number.Uint64()) { + // To ensure that the next validator produces and broadcasts blocks in a timely manner, + // set the value of timeForMining to a small amount + timeForMining = 500 * time.Millisecond + } else { + timeForMining = time.Duration(p.config.Period)*time.Second - 200*time.Millisecond + } + } + if delay > timeForMining { + delay = timeForMining } return &delay } diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 88bf0f1b8c..3730976f25 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -390,6 +390,11 @@ func (s *Snapshot) validators() []common.Address { return validators } +// 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.TurnTerm) == 0 +} + // 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 From 4d2d72021ce2bfef19124b23f808158cf70d02d4 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Thu, 27 Jun 2024 18:52:24 +0800 Subject: [PATCH 12/18] feat: rename turnTerm to turnLength --- cmd/extradump/extradump_test.go | 12 +++--- cmd/extradump/main.go | 12 +++--- cmd/geth/config.go | 4 +- cmd/geth/main.go | 2 +- cmd/utils/flags.go | 8 ++-- consensus/parlia/abi.go | 2 +- consensus/parlia/bohrFork.go | 48 +++++++++++----------- consensus/parlia/parlia.go | 52 ++++++++++++------------ consensus/parlia/parlia_test.go | 28 ++++++------- consensus/parlia/snapshot.go | 40 +++++++++--------- eth/api_backend.go | 4 +- internal/ethapi/api.go | 8 ++-- internal/ethapi/api_test.go | 6 +-- internal/ethapi/backend.go | 4 +- internal/ethapi/transaction_args_test.go | 2 +- params/protocol_params.go | 8 ++-- 16 files changed, 120 insertions(+), 120 deletions(-) diff --git a/cmd/extradump/extradump_test.go b/cmd/extradump/extradump_test.go index 1c41fb7ec9..6f9289fc41 100644 --- a/cmd/extradump/extradump_test.go +++ b/cmd/extradump/extradump_test.go @@ -43,7 +43,7 @@ func TestExtraParse(t *testing.T) { } } - // case 3, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Term---|---Empty---|---Extra Seal---| + // case 3, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Length---|---Empty---|---Extra Seal---| { extraData := "0xd983010209846765746889676f312e31392e3131856c696e75780000a6bf97c1152465176c461afb316ebc773c61faee85a6515daa8a923564c6ffd37fb2fe9f118ef88092e8762c7addb526ab7eb1e772baef85181f892c731be0c1891a50e6b06262c816295e26495cef6f69dfa69911d9d8e4f3bbadb89b977cf58294f7239d515e15b24cfeb82494056cf691eaf729b165f32c9757c429dba5051155903067e56ebe3698678e912d4c407bbe49438ed859fe965b140dcf1aab71a993c1f7f6929d1fe2a17b4e14614ef9fc5bdc713d6631d675403fbeefac55611bf612700b1b65f4744861b80b0f7d6ab03f349bbafec1551819b8be1efea2fc46ca749aa184248a459464eec1a21e7fc7b71a053d9644e9bb8da4853b8f872cd7c1d6b324bf1922829830646ceadfb658d3de009a61dd481a114a2e761c554b641742c973867899d300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069c77a677c40c7fbea129d4b171a39b7a8ddabfab2317f59d86abfaf690850223d90e9e7593d91a29331dfc2f84d5adecc75fc39ecab4632c1b4400a3dd1e1298835bcca70f657164e5b75689b64b7fd1fa275f334f28e1896a26afa1295da81418593bd12814463d9f6e45c36a0e47eb4cd3e5b6af29c41e2a3a5636430155a466e216585af3ba772b61c6014342d914470ec7ac2975be345796c2b81db0422a5fd08e40db1fc2368d2245e4b18b1d0b85c921aaaafd2e341760e29fc613edd39f71254614e2055c3287a517ae2f5b9e386cd1b50a4550696d957cb4900f03ab84f83ff2df44193496793b847f64e9d6db1b3953682bb95edd096eb1e69bbd357c200992ca78050d0cbe180cfaa018e8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec73a8a257074e82b881cfa06ef3eb4efeca060c2531359abd0eab8af1e3edfa2025fca464ac9c3fd123f6c24a0d78869485a6f79b60359f141df90a0c745125b131caaffd12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b218c5d6af1f979ac42bc68d98a5a0d796c6ab01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4dd66d7c2c7e57f628210187192fb89d4b99dd4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be807dddb074639cd9fa61b47676c064fc50d62cb1f2c71577def3144fabeb75a8a1c8cb5b51d1d1b4a05eec67988b8685008baa17459ec425dbaebc852f496dc92196cdcc8e6d00c17eb431350c6c50d8b8f05176b90b11b3a3d4feb825ae9702711566df5dbf38e82add4dd1b573b95d2466fa6501ccb81e9d26a352b96150ccbf7b697fd0a419d1d6bf74282782b0b3eb1413c901d6ecf02e8e28000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2d3a739effcd3a99387d015e260eefac72ebea1956c470ddff48cb49300200b5f83497f3a3ccb3aeb83c5edd9818569038e61d197184f4aa6939ea5e9911e3e98ac6d21e9ae3261a475a27bb1028f140bc2a7c843318afd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea0a6e3c511bbd10f4519ece37dc24887e11b55db2d4c6283c44a1c7bd503aaba7666e9f0c830e0ff016c1c750a5e48757a713d0836b1cabfd5c281b1de3b77d1c192183ee226379db83cffc681495730c11fdde79ba4c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef0274e31810c9df02f98fafde0f841f4e66a1cd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e99f701bb14cb7dfb68b90bd3e6d1ca656964630de71beffc7f33f7f08ec99d336ec51ad9fad0ac84ae77ca2e8ad9512acc56e0d7c93f3c2ce7de1b69149a5a400" extra, err := parseExtra(extraData) @@ -70,10 +70,10 @@ func TestExtraParse(t *testing.T) { } } { - var have = extra.TurnTerm + var have = extra.TurnLength var want = uint8(4) if *have != want { - t.Fatalf("extra.TurnTerm mismatch, have %d, want %d", *have, want) + t.Fatalf("extra.TurnLength mismatch, have %d, want %d", *have, want) } } } @@ -141,7 +141,7 @@ func TestExtraParse(t *testing.T) { } } - // case 6, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Term---|---Vote Attestation---|---Extra Seal---| + // case 6, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Turn Length---|---Vote Attestation---|---Extra Seal---| { extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b87804f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed28407080048b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200" extra, err := parseExtra(extraData) @@ -182,10 +182,10 @@ func TestExtraParse(t *testing.T) { } } { - var have = extra.TurnTerm + var have = extra.TurnLength var want = uint8(4) if *have != want { - t.Fatalf("extra.TurnTerm mismatch, have %d, want %d", *have, want) + t.Fatalf("extra.TurnLength mismatch, have %d, want %d", *have, want) } } } diff --git a/cmd/extradump/main.go b/cmd/extradump/main.go index aaec5ea80a..49fbc73b8f 100644 --- a/cmd/extradump/main.go +++ b/cmd/extradump/main.go @@ -24,7 +24,7 @@ const ( BLSPublicKeyLength = 48 // follow order in extra field - // |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Turn Term (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,7 +35,7 @@ type Extra struct { ExtraVanity string ValidatorSize uint8 Validators validatorsAscending - TurnTerm *uint8 + TurnLength *uint8 *types.VoteAttestation ExtraSeal []byte } @@ -115,10 +115,10 @@ func parseExtra(hexData string) (*Extra, error) { data = data[validatorBytesTotalLength-validatorNumberSize:] dataLength = len(data) - // parse TurnTerm + // parse TurnLength if dataLength > 0 { if data[0] != '\xf8' { - extra.TurnTerm = &data[0] + extra.TurnLength = &data[0] data = data[1:] dataLength = len(data) } @@ -158,8 +158,8 @@ func prettyExtra(extra Extra) { } } - if extra.TurnTerm != nil { - fmt.Printf("TurnTerm : %d\n", *extra.TurnTerm) + if extra.TurnLength != nil { + fmt.Printf("TurnLength : %d\n", *extra.TurnLength) } if extra.VoteAttestation != nil { diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 85222709aa..c2120bb903 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -214,8 +214,8 @@ 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.OverrideFixedTurnTerm.Name) { - params.FixedTurnTerm = ctx.Uint64(utils.OverrideFixedTurnTerm.Name) + if ctx.IsSet(utils.OverrideFixedTurnLength.Name) { + params.FixedTurnLength = ctx.Uint64(utils.OverrideFixedTurnLength.Name) } backend, eth := utils.RegisterEthService(stack, &cfg.Eth) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index b6a098d058..7c119f92eb 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -80,7 +80,7 @@ var ( utils.OverrideMinBlocksForBlobRequests, utils.OverrideDefaultExtraReserveForBlobRequests, utils.OverrideBreatheBlockInterval, - utils.OverrideFixedTurnTerm, + utils.OverrideFixedTurnLength, utils.EnablePersonal, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fcaa3860b2..aed444e738 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -349,10 +349,10 @@ var ( Value: params.BreatheBlockInterval, Category: flags.EthCategory, } - OverrideFixedTurnTerm = &cli.Uint64Flag{ - Name: "override.fixedturnterm", - Usage: "It use fixed or random values for turn terms instead of reading from the contract, only for testing purpose", - Value: params.FixedTurnTerm, + 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{ diff --git a/consensus/parlia/abi.go b/consensus/parlia/abi.go index 401db48911..804dcdf7e0 100644 --- a/consensus/parlia/abi.go +++ b/consensus/parlia/abi.go @@ -2308,7 +2308,7 @@ const validatorSetABI = ` }, { "inputs": [], - "name": "getTurnTerm", + "name": "getTurnLength", "outputs": [ { "internalType": "uint256", diff --git a/consensus/parlia/bohrFork.go b/consensus/parlia/bohrFork.go index 8531dc26fa..7cfe3c2f18 100644 --- a/consensus/parlia/bohrFork.go +++ b/consensus/parlia/bohrFork.go @@ -18,49 +18,49 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -func (p *Parlia) getTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) (*uint8, error) { +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 turnTerm uint8 + var turnLength uint8 if p.chainConfig.IsBohr(parent.Number, parent.Time) { - turnTermFromContract, err := p.getTurnTermFromContract(parent) + turnLengthFromContract, err := p.getTurnLengthFromContract(parent) if err != nil { return nil, err } - if turnTermFromContract == nil { - return nil, errors.New("unexpected error when getTurnTermFromContract") + if turnLengthFromContract == nil { + return nil, errors.New("unexpected error when getTurnLengthFromContract") } - turnTerm = uint8(turnTermFromContract.Int64()) + turnLength = uint8(turnLengthFromContract.Int64()) } else { - turnTerm = defaultTurnTerm + turnLength = defaultTurnLength } - log.Debug("getTurnTerm", "turnTerm", turnTerm) + log.Debug("getTurnLength", "turnLength", turnLength) - return &turnTerm, nil + return &turnLength, nil } -func (p *Parlia) getTurnTermFromContract(header *types.Header) (turnTerm *big.Int, err error) { - // mock to get turnTerm from the contract - if params.FixedTurnTerm >= 1 && params.FixedTurnTerm <= 9 { - if params.FixedTurnTerm == 2 { - return p.getRandTurnTerm(header) +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.FixedTurnTerm)), nil + return big.NewInt(int64(params.FixedTurnLength)), nil } ctx, cancel := context.WithCancel(context.Background()) defer cancel() - method := "getTurnTerm" + 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 getTurnTerm", "error", err) + log.Error("Unable to pack tx for getTurnLength", "error", err) return nil, err } msgData := (hexutil.Bytes)(data) @@ -75,17 +75,17 @@ func (p *Parlia) getTurnTermFromContract(header *types.Header) (turnTerm *big.In return nil, err } - if err := p.validatorSetABI.UnpackIntoInterface(&turnTerm, method, result); err != nil { + if err := p.validatorSetABI.UnpackIntoInterface(&turnLength, method, result); err != nil { return nil, err } - return turnTerm, nil + return turnLength, nil } -// getRandTurnTerm returns a random valid value, used to test switching turn terms -func (p *Parlia) getRandTurnTerm(header *types.Header) (turnTerm *big.Int, err error) { - turnTerms := [8]uint8{1, 3, 4, 5, 6, 7, 8, 9} +// 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))) - termIndex := int(r.Int31n(int32(len(turnTerms)))) - return big.NewInt(int64(turnTerms[termIndex])), nil + lengthIndex := int(r.Int31n(int32(len(turnLengths)))) + return big.NewInt(int64(turnLengths[lengthIndex])), nil } diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index b653502d41..5bce04f3f6 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -54,12 +54,12 @@ const ( 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 - defaultTurnTerm = uint8(1) // Default consecutive number of blocks a validator receives priority for block production + 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. - turnTermSize = 1 // Fixed number of extra-data suffix bytes reserved for turnTerm + turnLengthSize = 1 // Fixed number of extra-data suffix bytes reserved for turnLength validatorBytesLengthBeforeLuban = common.AddressLength validatorBytesLength = common.AddressLength + types.BLSPublicKeyLength @@ -128,9 +128,9 @@ var ( // invalid list of validators (i.e. non divisible by 20 bytes). errInvalidSpanValidators = errors.New("invalid validator list on sprint end block") - // errInvalidTurnTerm is returned if a block contains an - // invalid term of turn (i.e. no data left after parsing validators). - errInvalidTurnTerm = errors.New("invalid turnTerm") + // 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") @@ -142,9 +142,9 @@ var ( // list of validators different than the one the local node calculated. errMismatchingEpochValidators = errors.New("mismatching validator list on epoch block") - // errMismatchingEpochTurnTerm is returned if a sprint block contains a - // turn term different than the one the local node calculated. - errMismatchingEpochTurnTerm = errors.New("mismatching turn term 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") @@ -375,7 +375,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 Term (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 @@ -396,7 +396,7 @@ func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.Chain end := start + num*validatorBytesLength extraMinLen := end + extraSeal if chainConfig.IsBohr(header.Number, header.Time) { - extraMinLen += turnTermSize + extraMinLen += turnLengthSize } if num == 0 || len(header.Extra) < extraMinLen { return nil @@ -421,7 +421,7 @@ func getVoteAttestationFromHeader(header *types.Header, chainConfig *params.Chai num := int(header.Extra[extraVanity]) start := extraVanity + validatorNumberSize + num*validatorBytesLength if chainConfig.IsBohr(header.Number, header.Time) { - start += turnTermSize + start += turnLengthSize } end := len(header.Extra) - extraSeal if end <= start { @@ -902,19 +902,19 @@ func (p *Parlia) prepareValidators(header *types.Header) error { return nil } -func (p *Parlia) prepareTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { +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 } - turnTerm, err := p.getTurnTerm(chain, header) + turnLength, err := p.getTurnLength(chain, header) if err != nil { return err } - if turnTerm != nil { - header.Extra = append(header.Extra, *turnTerm) + if turnLength != nil { + header.Extra = append(header.Extra, *turnLength) } return nil @@ -1051,7 +1051,7 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header return err } - if err := p.prepareTurnTerm(chain, header); err != nil { + if err := p.prepareTurnLength(chain, header); err != nil { return err } // add extra seal space @@ -1104,28 +1104,28 @@ func (p *Parlia) verifyValidators(header *types.Header) error { return nil } -func (p *Parlia) verifyTurnTerm(chain consensus.ChainHeaderReader, header *types.Header) error { +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 } - turnTermFromHeader, err := parseTurnTerm(header, p.chainConfig, p.config) + turnLengthFromHeader, err := parseTurnLength(header, p.chainConfig, p.config) if err != nil { return err } - if turnTermFromHeader != nil { - turnTerm, err := p.getTurnTerm(chain, header) + if turnLengthFromHeader != nil { + turnLength, err := p.getTurnLength(chain, header) if err != nil { return err } - if turnTerm != nil && *turnTerm == *turnTermFromHeader { - log.Debug("verifyTurnTerm", "turnTerm", *turnTerm) + if turnLength != nil && *turnLength == *turnLengthFromHeader { + log.Debug("verifyTurnLength", "turnLength", *turnLength) return nil } } - return errMismatchingEpochTurnTerm + return errMismatchingEpochTurnLength } func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header, @@ -1222,7 +1222,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade return err } - if err := p.verifyTurnTerm(chain, header); err != nil { + if err := p.verifyTurnLength(chain, header); err != nil { return err } @@ -1496,9 +1496,9 @@ 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 when snap.TurnTerm == 1 + // 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.TurnTerm > 1 { + if snap.TurnLength > 1 { if snap.lastBlockInOneTurn(header.Number.Uint64()) { // To ensure that the next validator produces and broadcasts blocks in a timely manner, // set the value of timeForMining to a small amount diff --git a/consensus/parlia/parlia_test.go b/consensus/parlia/parlia_test.go index 1c5309517d..afd30b86e8 100644 --- a/consensus/parlia/parlia_test.go +++ b/consensus/parlia/parlia_test.go @@ -22,7 +22,7 @@ func TestImpactOfValidatorOutOfService(t *testing.T) { testCases := []struct { totalValidators int downValidators int - turnTerm int + turnLength int }{ {3, 1, 1}, {5, 2, 1}, @@ -34,32 +34,32 @@ func TestImpactOfValidatorOutOfService(t *testing.T) { {21, 10, 5}, } for _, tc := range testCases { - simulateValidatorOutOfService(tc.totalValidators, tc.downValidators, tc.turnTerm) + simulateValidatorOutOfService(tc.totalValidators, tc.downValidators, tc.turnLength) } } // refer Snapshot.SignRecently -func signRecently(idx int, recents map[uint64]int, turnTerm int) bool { +func signRecently(idx int, recents map[uint64]int, turnLength int) bool { recentSignTimes := 0 for _, signIdx := range recents { if signIdx == idx { recentSignTimes += 1 } } - return recentSignTimes >= turnTerm + return recentSignTimes >= turnLength } // refer Snapshot.minerHistoryCheckLen -func minerHistoryCheckLen(totalValidators int, turnTerm int) uint64 { - return uint64(totalValidators/2+1)*uint64(turnTerm) - 1 +func minerHistoryCheckLen(totalValidators int, turnLength int) uint64 { + return uint64(totalValidators/2+1)*uint64(turnLength) - 1 } // refer Snapshot.inturnValidator -func inturnValidator(totalValidators int, turnTerm int, height int) int { - return height / turnTerm % totalValidators +func inturnValidator(totalValidators int, turnLength int, height int) int { + return height / turnLength % totalValidators } -func simulateValidatorOutOfService(totalValidators int, downValidators int, turnTerm int) { +func simulateValidatorOutOfService(totalValidators int, downValidators int, turnLength int) { downBlocks := 10000 recoverBlocks := 10000 recents := make(map[uint64]int) @@ -77,7 +77,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int, turn delete(validators, down[i]) } isRecentSign := func(idx int) bool { - return signRecently(idx, recents, turnTerm) + return signRecently(idx, recents, turnLength) } isInService := func(idx int) bool { return validators[idx] @@ -85,10 +85,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int, turn downDelay := uint64(0) for h := 1; h <= downBlocks; h++ { - if limit := minerHistoryCheckLen(totalValidators, turnTerm) + 1; uint64(h) >= limit { + if limit := minerHistoryCheckLen(totalValidators, turnLength) + 1; uint64(h) >= limit { delete(recents, uint64(h)-limit) } - proposer := inturnValidator(totalValidators, turnTerm, h) + proposer := inturnValidator(totalValidators, turnLength, h) if !isInService(proposer) || isRecentSign(proposer) { candidates := make(map[int]bool, totalValidators/2) for v := range validators { @@ -116,10 +116,10 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int, turn recoverDelay := uint64(0) lastseen := downBlocks for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ { - if limit := minerHistoryCheckLen(totalValidators, turnTerm) + 1; uint64(h) >= limit { + if limit := minerHistoryCheckLen(totalValidators, turnLength) + 1; uint64(h) >= limit { delete(recents, uint64(h)-limit) } - proposer := inturnValidator(totalValidators, turnTerm, h) + proposer := inturnValidator(totalValidators, turnLength, h) if !isInService(proposer) || isRecentSign(proposer) { lastseen = h candidates := make(map[int]bool, totalValidators/2) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 3730976f25..278c0275b7 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -44,7 +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 - TurnTerm uint8 `json:"turn_term"` // Term of `turn`, meaning the consecutive number of blocks a validator receives priority for block production + 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 @@ -74,7 +74,7 @@ func newSnapshot( sigCache: sigCache, Number: number, Hash: hash, - TurnTerm: defaultTurnTerm, + TurnLength: defaultTurnLength, Recents: make(map[uint64]common.Address), RecentForkHashes: make(map[uint64]string), Validators: make(map[common.Address]*ValidatorInfo), @@ -117,8 +117,8 @@ func loadSnapshot(config *params.ParliaConfig, sigCache *lru.ARCCache, db ethdb. if err := json.Unmarshal(blob, snap); err != nil { return nil, err } - if snap.TurnTerm == 0 { // no TurnTerm field in old snapshots - snap.TurnTerm = defaultTurnTerm + if snap.TurnLength == 0 { // no TurnLength field in old snapshots + snap.TurnLength = defaultTurnLength } snap.config = config @@ -145,7 +145,7 @@ func (s *Snapshot) copy() *Snapshot { sigCache: s.sigCache, Number: s.Number, Hash: s.Hash, - TurnTerm: s.TurnTerm, + TurnLength: s.TurnLength, Validators: make(map[common.Address]*ValidatorInfo), Recents: make(map[uint64]common.Address), RecentForkHashes: make(map[uint64]string), @@ -219,11 +219,11 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C } func (s *Snapshot) versionHistoryCheckLen() uint64 { - return uint64(len(s.Validators)) * uint64(s.TurnTerm) + return uint64(len(s.Validators)) * uint64(s.TurnLength) } func (s *Snapshot) minerHistoryCheckLen() uint64 { - return (uint64(len(s.Validators))/2+1)*uint64(s.TurnTerm) - 1 + return (uint64(len(s.Validators))/2+1)*uint64(s.TurnLength) - 1 } func (s *Snapshot) countRecents() map[common.Address]uint8 { @@ -243,8 +243,8 @@ func (s *Snapshot) countRecents() map[common.Address]uint8 { } func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint8) bool { - if seenTimes, ok := counts[validator]; ok && seenTimes >= s.TurnTerm { - if seenTimes > s.TurnTerm { + 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 @@ -317,14 +317,14 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } oldVersionsLen := snap.versionHistoryCheckLen() - // get turnTerm from headers and use that for new turnTerm - turnTerm, err := parseTurnTerm(checkpointHeader, chainConfig, s.config) + // get turnLength from headers and use that for new turnLength + turnLength, err := parseTurnLength(checkpointHeader, chainConfig, s.config) if err != nil { return nil, err } - if turnTerm != nil { - snap.TurnTerm = *turnTerm - log.Debug("validator set switch", "turnTerm", *turnTerm) + if turnLength != nil { + snap.TurnLength = *turnLength + log.Debug("validator set switch", "turnLength", *turnLength) } // get validators from headers and use that for new validator set @@ -392,7 +392,7 @@ func (s *Snapshot) validators() []common.Address { // 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.TurnTerm) == 0 + return (blockNumber+1)%uint64(s.TurnLength) == 0 } // inturn returns if a validator at a given block height is in-turn or not. @@ -403,7 +403,7 @@ func (s *Snapshot) inturn(validator common.Address) bool { // inturnValidator returns the validator at a given block height. func (s *Snapshot) inturnValidator() common.Address { validators := s.validators() - offset := (s.Number + 1) / uint64(s.TurnTerm) % uint64(len(validators)) + offset := (s.Number + 1) / uint64(s.TurnLength) % uint64(len(validators)) return validators[offset] } @@ -466,7 +466,7 @@ func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parl return cnsAddrs, voteAddrs, nil } -func parseTurnTerm(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) (*uint8, error) { +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 @@ -478,10 +478,10 @@ func parseTurnTerm(header *types.Header, chainConfig *params.ChainConfig, parlia num := int(header.Extra[extraVanity]) pos := extraVanity + validatorNumberSize + num*validatorBytesLength if len(header.Extra) <= pos { - return nil, errInvalidTurnTerm + return nil, errInvalidTurnLength } - turnterm := header.Extra[pos] - return &turnterm, nil + turnLength := header.Extra[pos] + return &turnLength, nil } func FindAncientHeader(header *types.Header, ite uint64, chain consensus.ChainHeaderReader, candidateParents []*types.Header) *types.Header { diff --git a/eth/api_backend.go b/eth/api_backend.go index 7f9f0087cb..34ad883647 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -441,7 +441,7 @@ func (b *EthAPIBackend) Engine() consensus.Engine { return b.eth.engine } -func (b *EthAPIBackend) CurrentTurnTerm() (turnTerm uint8, err error) { +func (b *EthAPIBackend) CurrentTurnLength() (turnLength uint8, err error) { if p, ok := b.eth.engine.(*parlia.Parlia); ok { service := p.APIs(b.Chain())[0].Service snap, err := service.(*parlia.API).GetSnapshot(nil) @@ -449,7 +449,7 @@ func (b *EthAPIBackend) CurrentTurnTerm() (turnTerm uint8, err error) { return 0, err } - return snap.TurnTerm, nil + return snap.TurnLength, nil } return 1, nil diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f1bd8fd446..1c81ba544c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -870,7 +870,7 @@ func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFin return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized) } - currentTurnTerm, err := s.b.CurrentTurnTerm() + currentTurnLength, err := s.b.CurrentTurnLength() if err != nil { // impossible return nil, err } @@ -882,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*int64(currentTurnTerm)) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength)) return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber)) } @@ -897,7 +897,7 @@ func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFina return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized) } - currentTurnTerm, err := s.b.CurrentTurnTerm() + currentTurnLength, err := s.b.CurrentTurnLength() if err != nil { // impossible return nil, err } @@ -909,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*int64(currentTurnTerm)) + finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength)) return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index b2846d0fd5..ac467c352a 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -631,9 +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) CurrentTurnTerm() (uint8, error) { return 1, nil } +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") } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 1de5418806..79492cda85 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -89,8 +89,8 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine - // CurrentTurnTerm return the turnTerm at the latest block - CurrentTurnTerm() (uint8, error) + // 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 diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index cd241bc888..17bbc49c5a 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -416,7 +416,7 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } -func (b *backendMock) CurrentTurnTerm() (uint8, error) { return 1, 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 } diff --git a/params/protocol_params.go b/params/protocol_params.go index 5651590cb0..65b2d942c1 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -193,10 +193,10 @@ var ( 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 term directly - // 2 --> use random values to test switching turn terms - // 0 and other values --> get turn term from contract - FixedTurnTerm uint64 = 0 + // [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 From 5be96a550550ceb2fa8f938093e237e0aac0e3a4 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Fri, 28 Jun 2024 09:37:26 +0800 Subject: [PATCH 13/18] consensus/parlia: modify delay --- consensus/parlia/parlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 5bce04f3f6..38f8888881 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1504,7 +1504,7 @@ func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOv // set the value of timeForMining to a small amount timeForMining = 500 * time.Millisecond } else { - timeForMining = time.Duration(p.config.Period)*time.Second - 200*time.Millisecond + timeForMining = time.Duration(p.config.Period) * time.Second } } if delay > timeForMining { From f513d207ce9c133bad1a47c69660d50965fb9bf9 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Mon, 8 Jul 2024 12:20:18 +0800 Subject: [PATCH 14/18] consensus/parlia: apply BEP-404 --- consensus/parlia/snapshot.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 278c0275b7..b9a81991e6 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -309,8 +309,19 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } } 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 == 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 @@ -343,14 +354,10 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } } if chainConfig.IsBohr(header.Number, header.Time) { - epochClearKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number - // in a epoch, after the first validator set switch, minerHistoryCheckLen() may become larger, - // so the unexpected second switch will happen, don't clear up the `Recents` in this kind of scene. - if _, ok := snap.Recents[epochClearKey]; !ok { - snap.Recents = make(map[uint64]common.Address) // without this logic, there will be several off-turn blocks when do validator set switch - snap.Recents[epochClearKey] = common.Address{} - log.Debug("Recents are cleared up", "blockNumber", number) - } + // 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 @@ -371,9 +378,6 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea 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() From 6589d44ff729e9221cdef61158c05c6ba53d6dce Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Fri, 12 Jul 2024 16:36:22 +0800 Subject: [PATCH 15/18] consensus/parlia: leave buffer time for import and vote --- consensus/parlia/parlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 38f8888881..05c46fef06 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1504,7 +1504,7 @@ func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOv // set the value of timeForMining to a small amount timeForMining = 500 * time.Millisecond } else { - timeForMining = time.Duration(p.config.Period) * time.Second + timeForMining = time.Duration(p.config.Period) * time.Second * 2 / 3 } } if delay > timeForMining { From bd82176c3a33eea10316a536ddc64c8426ee94c2 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Fri, 19 Jul 2024 14:04:05 +0800 Subject: [PATCH 16/18] consensus/parlia: change the rand seed for backOffTime --- consensus/parlia/parlia.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 05c46fef06..6700215294 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -2022,7 +2022,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) From 38e091c5b06f8277d2c9f3b54e4a060b9f339868 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Mon, 22 Jul 2024 14:06:24 +0800 Subject: [PATCH 17/18] consensus/parlia: add GetTurnLength for Parlia Engine --- cmd/jsutils/get_perf.js | 4 +++- consensus/parlia/api.go | 13 +++++++++++++ eth/api_backend.go | 8 ++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd/jsutils/get_perf.js b/cmd/jsutils/get_perf.js index da0aff0e7d..cec035f09f 100644 --- a/cmd/jsutils/get_perf.js +++ b/cmd/jsutils/get_perf.js @@ -13,6 +13,8 @@ const main = async () => { 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)]); @@ -35,7 +37,7 @@ const main = async () => { } else { console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber) } - console.log("BlockNumber =", i, "mod =", i%4, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp) + console.log("BlockNumber =", i, "mod =", i%turnLength, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp) } let blockCount = program.endNum - program.startNum diff --git a/consensus/parlia/api.go b/consensus/parlia/api.go index 0527a1c6a0..5929e94d1e 100644 --- a/consensus/parlia/api.go +++ b/consensus/parlia/api.go @@ -88,6 +88,19 @@ func (api *API) GetJustifiedNumber(number *rpc.BlockNumber) (uint64, error) { 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 diff --git a/eth/api_backend.go b/eth/api_backend.go index 34ad883647..d72711929d 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -444,12 +444,8 @@ func (b *EthAPIBackend) Engine() consensus.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 - snap, err := service.(*parlia.API).GetSnapshot(nil) - if err != nil { - return 0, err - } - - return snap.TurnLength, nil + currentHead := rpc.LatestBlockNumber + return service.(*parlia.API).GetTurnLength(¤tHead) } return 1, nil From 11488c3d87e02cb6b2673d10ea98a1da4f7486d3 Mon Sep 17 00:00:00 2001 From: NathanBSC Date: Tue, 23 Jul 2024 09:49:02 +0800 Subject: [PATCH 18/18] consensus/parlia: update some comments --- consensus/parlia/snapshot.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index b9a81991e6..339736771d 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -234,7 +234,7 @@ func (s *Snapshot) countRecents() map[common.Address]uint8 { } counts := make(map[common.Address]uint8, len(s.Validators)) for seen, recent := range s.Recents { - if seen <= leftHistoryBound || recent == (common.Address{}) { + if seen <= leftHistoryBound || recent == (common.Address{}) /*when seen == `epochKey`*/ { continue } counts[recent] += 1 @@ -283,7 +283,7 @@ 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 := snap.minerHistoryCheckLen() + 1; number >= limit { // `+1` for genesis block which has no miner + if limit := snap.minerHistoryCheckLen() + 1; number >= limit { delete(snap.Recents, number-limit) } if limit := snap.versionHistoryCheckLen(); number >= limit { @@ -404,7 +404,7 @@ func (s *Snapshot) inturn(validator common.Address) bool { return s.inturnValidator() == validator } -// inturnValidator returns the validator at a given block height. +// inturnValidator returns the validator for the following block height. func (s *Snapshot) inturnValidator() common.Address { validators := s.validators() offset := (s.Number + 1) / uint64(s.TurnLength) % uint64(len(validators))