bsc/cmd/extradump/main.go

178 lines
5.5 KiB
Go

// Copyright 2023 The bsc Authors
// This file is part of bsc.
package main
import (
"bytes"
"encoding/hex"
"errors"
"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)---|---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
extraSealLength = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
)
type Extra struct {
ExtraVanity string
ValidatorSize uint8
Validators validatorsAscending
TurnLength *uint8
*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, errors.New("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, errors.New("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 TurnLength
if dataLength > 0 {
if data[0] != '\xf8' {
extra.TurnLength = &data[0]
data = data[1:]
dataLength = len(data)
}
}
}
// parse Vote Attestation
if dataLength > 0 {
if err := rlp.Decode(bytes.NewReader(data), &extra.VoteAttestation); err != nil {
return nil, errors.New("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.TurnLength != nil {
fmt.Printf("TurnLength : %d\n", *extra.TurnLength)
}
if extra.VoteAttestation != nil {
fmt.Printf("Attestation :\n")
fmt.Printf("\tVoteAddressSet : %b, %d\n", extra.VoteAddressSet, bitset.From([]uint64{uint64(extra.VoteAddressSet)}).Count())
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))
}