178 lines
5.5 KiB
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))
|
|
}
|