Compare commits

..

10 Commits

16 changed files with 428 additions and 37 deletions

View File

@@ -1,4 +1,16 @@
# Changelog # Changelog
## v1.2.10
FEATURE
* [\#1780](https://github.com/bnb-chain/bsc/pull/1780) log: reduce logs when receiving too much votes from a peer
* [\#1788](https://github.com/bnb-chain/bsc/pull/1788) metrics: add txpool config into metrics server
* [\#1789](https://github.com/bnb-chain/bsc/pull/1789) rpc: add GetFinalizedHeader/Block to simplify using the fast finality feature
* [\#1791](https://github.com/bnb-chain/bsc/pull/1791) finality: add more check to ensure result of assembleVoteAttestation
BUGFIX
* [\#1773](https://github.com/bnb-chain/bsc/pull/1773) discov: do not filter out bootnodes
* [\#1778](https://github.com/bnb-chain/bsc/pull/1778) vote: backup validator sync votes from corresponding mining validator
* [\#1784](https://github.com/bnb-chain/bsc/pull/1784) fix: exclude same votes when doing malicious voting check
## v1.2.9 ## v1.2.9
FEATURE FEATURE
* [\#1775](https://github.com/bnb-chain/bsc/pull/1775) upgrade: several hardfork block height on mainnet: Plato, Hertz(Berlin, London) * [\#1775](https://github.com/bnb-chain/bsc/pull/1775) upgrade: several hardfork block height on mainnet: Plato, Hertz(Berlin, London)

View File

@@ -0,0 +1,108 @@
package main
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
)
func TestExtraParse(t *testing.T) {
// case 1, |---Extra Vanity---|---Empty---|---Empty---|---Extra Seal---|
{
extraData := "0xd983010209846765746889676f312e31392e3131856c696e75780000a6bf97c1e99f701bb14cb7dfb68b90bd3e6d1ca656964630de71beffc7f33f7f08ec99d336ec51ad9fad0ac84ae77ca2e8ad9512acc56e0d7c93f3c2ce7de1b69149a5a400"
_, err := parseExtra(extraData)
assert.NoError(t, err)
}
// case 2, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Empty---|---Extra Seal---|
{
extraData := "0xd983010209846765746889676f312e31392e3131856c696e75780000a6bf97c1152465176c461afb316ebc773c61faee85a6515daa8a923564c6ffd37fb2fe9f118ef88092e8762c7addb526ab7eb1e772baef85181f892c731be0c1891a50e6b06262c816295e26495cef6f69dfa69911d9d8e4f3bbadb89b977cf58294f7239d515e15b24cfeb82494056cf691eaf729b165f32c9757c429dba5051155903067e56ebe3698678e912d4c407bbe49438ed859fe965b140dcf1aab71a993c1f7f6929d1fe2a17b4e14614ef9fc5bdc713d6631d675403fbeefac55611bf612700b1b65f4744861b80b0f7d6ab03f349bbafec1551819b8be1efea2fc46ca749aa184248a459464eec1a21e7fc7b71a053d9644e9bb8da4853b8f872cd7c1d6b324bf1922829830646ceadfb658d3de009a61dd481a114a2e761c554b641742c973867899d300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069c77a677c40c7fbea129d4b171a39b7a8ddabfab2317f59d86abfaf690850223d90e9e7593d91a29331dfc2f84d5adecc75fc39ecab4632c1b4400a3dd1e1298835bcca70f657164e5b75689b64b7fd1fa275f334f28e1896a26afa1295da81418593bd12814463d9f6e45c36a0e47eb4cd3e5b6af29c41e2a3a5636430155a466e216585af3ba772b61c6014342d914470ec7ac2975be345796c2b81db0422a5fd08e40db1fc2368d2245e4b18b1d0b85c921aaaafd2e341760e29fc613edd39f71254614e2055c3287a517ae2f5b9e386cd1b50a4550696d957cb4900f03ab84f83ff2df44193496793b847f64e9d6db1b3953682bb95edd096eb1e69bbd357c200992ca78050d0cbe180cfaa018e8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec73a8a257074e82b881cfa06ef3eb4efeca060c2531359abd0eab8af1e3edfa2025fca464ac9c3fd123f6c24a0d78869485a6f79b60359f141df90a0c745125b131caaffd12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b218c5d6af1f979ac42bc68d98a5a0d796c6ab01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4dd66d7c2c7e57f628210187192fb89d4b99dd4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be807dddb074639cd9fa61b47676c064fc50d62cb1f2c71577def3144fabeb75a8a1c8cb5b51d1d1b4a05eec67988b8685008baa17459ec425dbaebc852f496dc92196cdcc8e6d00c17eb431350c6c50d8b8f05176b90b11b3a3d4feb825ae9702711566df5dbf38e82add4dd1b573b95d2466fa6501ccb81e9d26a352b96150ccbf7b697fd0a419d1d6bf74282782b0b3eb1413c901d6ecf02e8e28000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2d3a739effcd3a99387d015e260eefac72ebea1956c470ddff48cb49300200b5f83497f3a3ccb3aeb83c5edd9818569038e61d197184f4aa6939ea5e9911e3e98ac6d21e9ae3261a475a27bb1028f140bc2a7c843318afd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea0a6e3c511bbd10f4519ece37dc24887e11b55db2d4c6283c44a1c7bd503aaba7666e9f0c830e0ff016c1c750a5e48757a713d0836b1cabfd5c281b1de3b77d1c192183ee226379db83cffc681495730c11fdde79ba4c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef0274e31810c9df02f98fafde0f841f4e66a1cd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e99f701bb14cb7dfb68b90bd3e6d1ca656964630de71beffc7f33f7f08ec99d336ec51ad9fad0ac84ae77ca2e8ad9512acc56e0d7c93f3c2ce7de1b69149a5a400"
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)
}
}
}
// case 3, |---Extra Vanity---|---Empty---|---Vote Attestation---|---Extra Seal---|
{
extraData := "0xd883010205846765746888676f312e32302e35856c696e75780000002995c52af8b5830563efb86089cf168dcf4c5d3cb057926628ad1bf0f03ea67eef1458485578a4f8489afa8a853ecc7af45e2d145c21b70641c4b29f0febd2dd2c61fa1ba174be3fd47f1f5fa2ab9b5c318563d8b70ca58d0d51e79ee32b2fb721649e2cb9d36538361fba11f84c8401d14bb7a0fa67ddb3ba654d6006bf788710032247aa4d1be0707273e696b422b3ff72e9798401d14bbaa01225f505f5a0e1aefadcd2913b7aac9009fe4fb3d1bf57399e0b9dce5947f94280fe6d3647276c4127f437af59eb7c7985b2ae1ebe432619860695cb6106b80cc66c735bc1709afd11f233a2c97409d38ebaf7178aa53e895aea2fe0a229f71ec601"
extra, err := parseExtra(extraData)
assert.NoError(t, err)
{
var have = common.Bytes2Hex(extra.Data.TargetHash[:])
var want = "1225f505f5a0e1aefadcd2913b7aac9009fe4fb3d1bf57399e0b9dce5947f942"
if have != want {
t.Fatalf("extra.Data.TargetHash mismatch, have %s, want %s", have, want)
}
}
{
var have = extra.Data.TargetNumber
var want = uint64(30493626)
if have != want {
t.Fatalf("extra.Data.TargetNumber mismatch, have %d, want %d", have, want)
}
}
}
// case 4, |---Extra Vanity---|---Validators Number and Validators Bytes---|---Vote Attestation---|---Extra Seal---|
{
extraData := "0xd883010209846765746888676f312e31392e38856c696e7578000000dc55905c071284214b9b9c85549ab3d2b972df0deef66ac2c98e82934ca974fdcd97f3309de967d3c9c43fa711a8d673af5d75465844bf8969c8d1948d903748ac7b8b1720fa64e50c35552c16704d214347f29fa77f77da6d75d7c752b742ad4855bae330426b823e742da31f816cc83bc16d69a9134be0cfb4a1d17ec34f1b5b32d5c20440b8536b1e88f0f247788386d0ed6c748e03a53160b4b30ed3748cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000980a75ecd1309ea12fa2ed87a8744fbfc9b863d589037a9ace3b590165ea1c0c5ac72bf600b7c88c1e435f41932c1132aae1bfa0bb68e46b96ccb12c3415e4d82af717d8a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b973c2d38487e58fd6e145491b110080fb14ac915a0411fc78f19e09a399ddee0d20c63a75d8f930f1694544ad2dc01bb71b214cb885500844365e95cd9942c7276e7fd8a2750ec6dded3dcdc2f351782310b0eadc077db59abca0f0cd26776e2e7acb9f3bce40b1fa5221fd1561226c6263cc5ff474cf03cceff28abc65c9cbae594f725c80e12d96c9b86c3400e529bfe184056e257c07940bb664636f689e8d2027c834681f8f878b73445261034e946bb2d901b4b878f8b27bb8608c11016739b3f8a19e54ab8c7abacd936cfeba200f3645a98b65adb0dd3692b69ce0b3ae10e7176b9a4b0d83f04065b1042b4bcb646a34b75c550f92fc34b8b2b1db0fa0d3172db23ba92727c80bcd306320d0ff411bf858525fde13bc8e0370f84c8401e9c2e6a0820dc11d63176a0eb1b828bc5376867b275579112b7013358da40317e7bab6e98401e9c2e7a00edc71ce80105a3220a87bea2792fa340d66c59002f02b0a09349ed1ed284070808b972fac2b9077a4dcb6fc37093799a652858016c99142b227500c844fa97ec22e3f9d3b1e982f14bcd999a7453e89ce5ef5c55f1c7f8f74ba904186cd67828200"
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)
}
}
}
}

162
cmd/extradump/main.go Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2023 The bsc Authors
// This file is part of bsc.
package main
import (
"bytes"
"encoding/hex"
"flag"
"fmt"
"os"
"sort"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/willf/bitset"
)
// follow define in parlia
const (
AddressLength = 20
BLSPublicKeyLength = 48
// follow order in extra field
// |---Extra Vanity---|---Validators Number and Validators Bytes (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
extraSealLength = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
)
type Extra struct {
ExtraVanity string
ValidatorSize uint8
Validators validatorsAscending
*types.VoteAttestation
ExtraSeal []byte
}
type ValidatorInfo struct {
common.Address
types.BLSPublicKey
VoteIncluded bool
}
// validatorsAscending implements the sort interface to allow sorting a list of ValidatorInfo
type validatorsAscending []ValidatorInfo
func (s validatorsAscending) Len() int { return len(s) }
func (s validatorsAscending) Less(i, j int) bool {
return bytes.Compare(s[i].Address[:], s[j].Address[:]) < 0
}
func (s validatorsAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func init() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[extraHexData]")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, `
Dumps extra info from the given hex data, only support extra after luban upgrade.`)
}
}
func main() {
flag.Parse()
extraHexData := os.Args[1]
if extra, err := parseExtra(extraHexData); err == nil {
fmt.Println("extra parsed successly")
prettyExtra(*extra)
} else {
fmt.Println("extra parsed failed", "err", err)
}
}
// parseExtra parse hex data into type Extra
func parseExtra(hexData string) (*Extra, error) {
// decode hex into bytes
data, err := hex.DecodeString(strings.TrimPrefix(hexData, "0x"))
if err != nil {
return nil, fmt.Errorf("invalid hex data")
}
// parse ExtraVanity and ExtraSeal
dataLength := len(data)
var extra Extra
if dataLength < extraVanityLength+extraSealLength {
fmt.Println("length less than min required")
}
extra.ExtraVanity = string(data[:extraVanityLength])
extra.ExtraSeal = data[dataLength-extraSealLength:]
data = data[extraVanityLength : dataLength-extraSealLength]
dataLength = len(data)
// parse Validators and Vote Attestation
if dataLength > 0 {
// parse Validators
if data[0] != '\xf8' { // rlp format of attestation begin with 'f8'
validatorNum := int(data[0])
validatorBytesTotalLength := validatorNumberSize + validatorNum*validatorBytesLength
if dataLength < validatorBytesTotalLength {
return nil, fmt.Errorf("parse validators failed")
}
extra.ValidatorSize = uint8(validatorNum)
data = data[validatorNumberSize:]
for i := 0; i < validatorNum; i++ {
var validatorInfo ValidatorInfo
validatorInfo.Address = common.BytesToAddress(data[i*validatorBytesLength : i*validatorBytesLength+common.AddressLength])
copy(validatorInfo.BLSPublicKey[:], data[i*validatorBytesLength+common.AddressLength:(i+1)*validatorBytesLength])
extra.Validators = append(extra.Validators, validatorInfo)
}
sort.Sort(extra.Validators)
data = data[validatorBytesTotalLength-validatorNumberSize:]
dataLength = len(data)
}
// parse Vote Attestation
if dataLength > 0 {
if err := rlp.Decode(bytes.NewReader(data), &extra.VoteAttestation); err != nil {
return nil, fmt.Errorf("parse voteAttestation failed")
}
if extra.ValidatorSize > 0 {
validatorsBitSet := bitset.From([]uint64{uint64(extra.VoteAddressSet)})
for i := 0; i < int(extra.ValidatorSize); i++ {
if validatorsBitSet.Test(uint(i)) {
extra.Validators[i].VoteIncluded = true
}
}
}
}
}
return &extra, nil
}
// prettyExtra print Extra with a pretty format
func prettyExtra(extra Extra) {
fmt.Printf("ExtraVanity : %s\n", extra.ExtraVanity)
if extra.ValidatorSize > 0 {
fmt.Printf("ValidatorSize : %d\n", extra.ValidatorSize)
for i := 0; i < int(extra.ValidatorSize); i++ {
fmt.Printf("Validator %d\n", i+1)
fmt.Printf("\tAddress : %s\n", common.Bytes2Hex(extra.Validators[i].Address[:]))
fmt.Printf("\tVoteKey : %s\n", common.Bytes2Hex(extra.Validators[i].BLSPublicKey[:]))
fmt.Printf("\tVoteIncluded : %t\n", extra.Validators[i].VoteIncluded)
}
}
if extra.VoteAttestation != nil {
fmt.Printf("Attestation :\n")
fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count())
fmt.Printf("\tAggSignature : %s\n", common.Bytes2Hex(extra.AggSignature[:]))
fmt.Printf("\tVoteData :\n")
fmt.Printf("\t\tSourceNumber : %d\n", extra.Data.SourceNumber)
fmt.Printf("\t\tSourceHash : %s\n", common.Bytes2Hex(extra.Data.SourceHash[:]))
fmt.Printf("\t\tTargetNumber : %d\n", extra.Data.TargetNumber)
fmt.Printf("\t\tTargetHash : %s\n", common.Bytes2Hex(extra.Data.TargetHash[:]))
}
fmt.Printf("ExtraSeal : %s\n", common.Bytes2Hex(extra.ExtraSeal))
}

View File

@@ -178,6 +178,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
utils.SetupMetrics(ctx, utils.SetupMetrics(ctx,
utils.EnableBuildInfo(gitCommit, gitDate), utils.EnableBuildInfo(gitCommit, gitDate),
utils.EnableMinerInfo(ctx, cfg.Eth.Miner), utils.EnableMinerInfo(ctx, cfg.Eth.Miner),
utils.EnableNodeInfo(cfg.Eth.TxPool),
) )
return stack, backend return stack, backend
} }

View File

@@ -1972,6 +1972,21 @@ func EnableMinerInfo(ctx *cli.Context, minerConfig miner.Config) SetupMetricsOpt
} }
} }
func EnableNodeInfo(poolConfig core.TxPoolConfig) SetupMetricsOption {
return func() {
// register node info into metrics
metrics.NewRegisteredLabel("node-info", nil).Mark(map[string]interface{}{
"PriceLimit": poolConfig.PriceLimit,
"PriceBump": poolConfig.PriceBump,
"AccountSlots": poolConfig.AccountSlots,
"GlobalSlots": poolConfig.GlobalSlots,
"AccountQueue": poolConfig.AccountQueue,
"GlobalQueue": poolConfig.GlobalQueue,
"Lifetime": poolConfig.Lifetime,
})
}
}
func SetupMetrics(ctx *cli.Context, options ...SetupMetricsOption) { func SetupMetrics(ctx *cli.Context, options ...SetupMetricsOption) {
if metrics.Enabled { if metrics.Enabled {
log.Info("Enabling metrics collection") log.Info("Enabling metrics collection")

View File

@@ -882,6 +882,11 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head
attestation.VoteAddressSet |= 1 << (valInfo.Index - 1) //Index is offset by 1 attestation.VoteAddressSet |= 1 << (valInfo.Index - 1) //Index is offset by 1
} }
} }
validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)})
if validatorsBitSet.Count() < uint(len(signatures)) {
log.Warn(fmt.Sprintf("assembleVoteAttestation, check VoteAddress Set failed, expected:%d, real:%d", len(signatures), validatorsBitSet.Count()))
return fmt.Errorf("invalid attestation, check VoteAddress Set failed")
}
// Append attestation to header extra field. // Append attestation to header extra field.
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -1758,10 +1763,11 @@ func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *t
return nil return nil
} }
if snap.Attestation != nil { if snap.Attestation == nil {
return chain.GetHeader(snap.Attestation.SourceHash, snap.Attestation.SourceNumber) return chain.GetHeaderByNumber(0) // keep consistent with GetJustifiedNumberAndHash
} }
return nil
return chain.GetHeader(snap.Attestation.SourceHash, snap.Attestation.SourceNumber)
} }
// =========================== utility function ========================== // =========================== utility function ==========================

View File

@@ -58,6 +58,7 @@ func (m *MaliciousVoteMonitor) ConflictDetect(newVote *types.VoteEnvelope, pendi
if !(blockNumber+maliciousVoteSlashScope > pendingBlockNumber) { if !(blockNumber+maliciousVoteSlashScope > pendingBlockNumber) {
blockNumber = pendingBlockNumber - maliciousVoteSlashScope + 1 blockNumber = pendingBlockNumber - maliciousVoteSlashScope + 1
} }
newVoteHash := newVote.Data.Hash()
for ; blockNumber <= pendingBlockNumber+upperLimitOfVoteBlockNumber; blockNumber++ { for ; blockNumber <= pendingBlockNumber+upperLimitOfVoteBlockNumber; blockNumber++ {
if voteDataBuffer.Contains(blockNumber) { if voteDataBuffer.Contains(blockNumber) {
voteEnvelope, ok := voteDataBuffer.Get(blockNumber) voteEnvelope, ok := voteDataBuffer.Get(blockNumber)
@@ -66,7 +67,7 @@ func (m *MaliciousVoteMonitor) ConflictDetect(newVote *types.VoteEnvelope, pendi
continue continue
} }
maliciousVote := false maliciousVote := false
if blockNumber == targetNumber { if blockNumber == targetNumber && voteEnvelope.(*types.VoteEnvelope).Data.Hash() != newVoteHash {
violateRule1Counter.Inc(1) violateRule1Counter.Inc(1)
maliciousVote = true maliciousVote = true
} else if (blockNumber < targetNumber && voteEnvelope.(*types.VoteEnvelope).Data.SourceNumber > sourceNumber) || } else if (blockNumber < targetNumber && voteEnvelope.(*types.VoteEnvelope).Data.SourceNumber > sourceNumber) ||

View File

@@ -22,9 +22,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1, TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes(("01"))),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -34,9 +34,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1, TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -54,9 +54,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1, TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes("01")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -65,9 +65,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1, TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -85,9 +85,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 1, TargetNumber: pendingBlockNumber - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes("01")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -96,9 +96,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: uint64(0), SourceNumber: uint64(0),
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 1, TargetNumber: pendingBlockNumber - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -116,9 +116,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 4, SourceNumber: pendingBlockNumber - 4,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 1, TargetNumber: pendingBlockNumber - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes("01")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -127,9 +127,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 2, SourceNumber: pendingBlockNumber - 2,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 3, TargetNumber: pendingBlockNumber - 3,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -147,9 +147,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 2, SourceNumber: pendingBlockNumber - 2,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 3, TargetNumber: pendingBlockNumber - 3,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes("01")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -158,9 +158,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 4, SourceNumber: pendingBlockNumber - 4,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 1, TargetNumber: pendingBlockNumber - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -178,9 +178,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 4, SourceNumber: pendingBlockNumber - 4,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 3, TargetNumber: pendingBlockNumber - 3,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))), TargetHash: common.BytesToHash(common.Hex2Bytes("01")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
@@ -189,9 +189,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 3, SourceNumber: pendingBlockNumber - 3,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 2, TargetNumber: pendingBlockNumber - 2,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
@@ -200,9 +200,9 @@ func TestMaliciousVoteMonitor(t *testing.T) {
Signature: types.BLSSignature{}, Signature: types.BLSSignature{},
Data: &types.VoteData{ Data: &types.VoteData{
SourceNumber: pendingBlockNumber - 2, SourceNumber: pendingBlockNumber - 2,
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))), SourceHash: common.BytesToHash(common.Hex2Bytes("00")),
TargetNumber: pendingBlockNumber - 1, TargetNumber: pendingBlockNumber - 1,
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))), TargetHash: common.BytesToHash(common.Hex2Bytes("02")),
}, },
} }
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote3, pendingBlockNumber)) assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote3, pendingBlockNumber))

View File

@@ -32,6 +32,10 @@ type VoteManager struct {
chainHeadCh chan core.ChainHeadEvent chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription chainHeadSub event.Subscription
// used for backup validators to sync votes from corresponding mining validator
syncVoteCh chan core.NewVoteEvent
syncVoteSub event.Subscription
pool *VotePool pool *VotePool
signer *VoteSigner signer *VoteSigner
journal *VoteJournal journal *VoteJournal
@@ -46,9 +50,9 @@ func NewVoteManager(eth Backend, chainconfig *params.ChainConfig, chain *core.Bl
chain: chain, chain: chain,
chainconfig: chainconfig, chainconfig: chainconfig,
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
syncVoteCh: make(chan core.NewVoteEvent, voteBufferForPut),
pool: pool, pool: pool,
engine: engine, engine: engine,
} }
// Create voteSigner. // Create voteSigner.
@@ -69,6 +73,7 @@ func NewVoteManager(eth Backend, chainconfig *params.ChainConfig, chain *core.Bl
// Subscribe to chain head event. // Subscribe to chain head event.
voteManager.chainHeadSub = voteManager.chain.SubscribeChainHeadEvent(voteManager.chainHeadCh) voteManager.chainHeadSub = voteManager.chain.SubscribeChainHeadEvent(voteManager.chainHeadCh)
voteManager.syncVoteSub = voteManager.pool.SubscribeNewVoteEvent(voteManager.syncVoteCh)
go voteManager.loop() go voteManager.loop()
@@ -77,6 +82,9 @@ func NewVoteManager(eth Backend, chainconfig *params.ChainConfig, chain *core.Bl
func (voteManager *VoteManager) loop() { func (voteManager *VoteManager) loop() {
log.Debug("vote manager routine loop started") log.Debug("vote manager routine loop started")
defer voteManager.chainHeadSub.Unsubscribe()
defer voteManager.syncVoteSub.Unsubscribe()
events := voteManager.eth.EventMux().Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) events := voteManager.eth.EventMux().Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
defer func() { defer func() {
log.Debug("vote manager loop defer func occur") log.Debug("vote manager loop defer func occur")
@@ -164,6 +172,21 @@ func (voteManager *VoteManager) loop() {
voteManager.pool.PutVote(voteMessage) voteManager.pool.PutVote(voteMessage)
votesManagerCounter.Inc(1) votesManagerCounter.Inc(1)
} }
case event := <-voteManager.syncVoteCh:
voteMessage := event.Vote
if voteManager.eth.IsMining() || !voteManager.signer.UsingKey(&voteMessage.VoteAddress) {
continue
}
if err := voteManager.journal.WriteVote(voteMessage); err != nil {
log.Error("Failed to write vote into journal", "err", err)
voteJournalErrorCounter.Inc(1)
continue
}
log.Debug("vote manager synced vote", "votedBlockNumber", voteMessage.Data.TargetNumber, "votedBlockHash", voteMessage.Data.TargetHash, "voteMessageHash", voteMessage.Hash())
votesManagerCounter.Inc(1)
case <-voteManager.syncVoteSub.Err():
log.Debug("voteManager subscribed votes failed")
return
case <-voteManager.chainHeadSub.Err(): case <-voteManager.chainHeadSub.Err():
log.Debug("voteManager subscribed chainHead failed") log.Debug("voteManager subscribed chainHead failed")
return return

View File

@@ -92,6 +92,8 @@ func NewVotePool(chainconfig *params.ChainConfig, chain *core.BlockChain, engine
// loop is the vote pool's main even loop, waiting for and reacting to outside blockchain events and votes channel event. // loop is the vote pool's main even loop, waiting for and reacting to outside blockchain events and votes channel event.
func (pool *VotePool) loop() { func (pool *VotePool) loop() {
defer pool.chainHeadSub.Unsubscribe()
for { for {
select { select {
// Handle ChainHeadEvent. // Handle ChainHeadEvent.

View File

@@ -1,6 +1,7 @@
package vote package vote
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@@ -104,3 +105,7 @@ func (signer *VoteSigner) SignVote(vote *types.VoteEnvelope) error {
copy(vote.Signature[:], signature.Marshal()[:]) copy(vote.Signature[:], signature.Marshal()[:])
return nil return nil
} }
func (signer *VoteSigner) UsingKey(bLSPublicKey *types.BLSPublicKey) bool {
return bytes.Equal(signer.pubKey[:], bLSPublicKey[:])
}

View File

@@ -62,7 +62,6 @@ func (h *bscHandler) Handle(peer *bsc.Peer, packet bsc.Packet) error {
// votes broadcast for the local node to process. // votes broadcast for the local node to process.
func (h *bscHandler) handleVotesBroadcast(peer *bsc.Peer, votes []*types.VoteEnvelope) error { func (h *bscHandler) handleVotesBroadcast(peer *bsc.Peer, votes []*types.VoteEnvelope) error {
if peer.IsOverLimitAfterReceiving() { if peer.IsOverLimitAfterReceiving() {
peer.Log().Warn("peer sending votes too much, votes dropped; it may be a ddos attack, please check!")
return nil return nil
} }
// Here we only put the first vote, to avoid ddos attack by sending a large batch of votes. // Here we only put the first vote, to avoid ddos attack by sending a large batch of votes.

View File

@@ -26,7 +26,7 @@ const (
receiveRateLimitPerSecond = 10 receiveRateLimitPerSecond = 10
// the time span of one period // the time span of one period
secondsPerPeriod = float64(10) secondsPerPeriod = float64(30)
) )
// max is a helper function which returns the larger of the two given integers. // max is a helper function which returns the larger of the two given integers.
@@ -133,6 +133,9 @@ func (p *Peer) AsyncSendVotes(votes []*types.VoteEnvelope) {
// Otherwise, check whether the number of received votes extra (secondsPerPeriod * receiveRateLimitPerSecond) // Otherwise, check whether the number of received votes extra (secondsPerPeriod * receiveRateLimitPerSecond)
func (p *Peer) IsOverLimitAfterReceiving() bool { func (p *Peer) IsOverLimitAfterReceiving() bool {
if timeInterval := time.Since(p.periodBegin).Seconds(); timeInterval >= secondsPerPeriod { if timeInterval := time.Since(p.periodBegin).Seconds(); timeInterval >= secondsPerPeriod {
if p.periodCounter > uint(secondsPerPeriod*receiveRateLimitPerSecond) {
p.Log().Debug("sending votes too much", "secondsPerPeriod", secondsPerPeriod, "count ", p.periodCounter)
}
p.periodBegin = time.Now() p.periodBegin = time.Now()
p.periodCounter = 0 p.periodCounter = 0
return false return false

View File

@@ -54,6 +54,14 @@ import (
const UnHealthyTimeout = 5 * time.Second const UnHealthyTimeout = 5 * time.Second
// max is a helper function which returns the larger of the two given integers.
func max(a, b int64) int64 {
if a > b {
return a
}
return b
}
// PublicEthereumAPI provides an API to access Ethereum related information. // PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct { type PublicEthereumAPI struct {
@@ -773,6 +781,52 @@ func (s *PublicBlockChainAPI) Health() bool {
return true return true
} }
// GetFinalizedHeader returns the requested finalized block header.
// - probabilisticFinalized should be in range [2,21],
// then the block header with number `max(fastFinalized, latest-probabilisticFinalized)` is returned
func (s *PublicBlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFinalized int64) (map[string]interface{}, error) {
if probabilisticFinalized < 2 || probabilisticFinalized > 21 {
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized)
}
var err error
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
if err != nil { // impossible
return nil, err
}
latestHeader, err := s.b.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil { // impossible
return nil, err
}
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized)
return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber))
}
// GetFinalizedBlock returns the requested finalized block.
// - probabilisticFinalized should be in range [2,21],
// then the block with number `max(fastFinalized, latest-probabilisticFinalized)` is returned
// - When fullTx is true all transactions in the block are returned, otherwise
// only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFinalized int64, fullTx bool) (map[string]interface{}, error) {
if probabilisticFinalized < 2 || probabilisticFinalized > 21 {
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized)
}
var err error
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
if err != nil { // impossible
return nil, err
}
latestHeader, err := s.b.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil { // impossible
return nil, err
}
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized)
return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx)
}
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index.
func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr) block, err := s.b.BlockByNumber(ctx, blockNr)

View File

@@ -611,7 +611,7 @@ func (srv *Server) setupDiscovery() error {
Tail []rlp.RawValue `rlp:"tail"` Tail []rlp.RawValue `rlp:"tail"`
} }
if r.Load(enr.WithEntry("eth", &eth)) != nil { if r.Load(enr.WithEntry("eth", &eth)) != nil {
return false return true
} }
return srv.forkFilter(eth.ForkID) == nil return srv.forkFilter(eth.ForkID) == nil
} }

View File

@@ -23,7 +23,7 @@ import (
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 2 // Minor version component of the current release VersionMinor = 2 // Minor version component of the current release
VersionPatch = 9 // Patch version component of the current release VersionPatch = 10 // Patch version component of the current release
VersionMeta = "" // Version metadata to append to the version string VersionMeta = "" // Version metadata to append to the version string
) )