From c208d28a68c414541cfaf2651b7cff725d2d3221 Mon Sep 17 00:00:00 2001 From: NathanBSC <122502194+NathanBSC@users.noreply.github.com> Date: Thu, 3 Aug 2023 10:05:54 +0800 Subject: [PATCH] tool: add a tool extradump to parse extra data after luban (#1795) --- cmd/extradump/extradump_test.go | 108 +++++++++++++++++++++ cmd/extradump/main.go | 162 ++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 cmd/extradump/extradump_test.go create mode 100644 cmd/extradump/main.go diff --git a/cmd/extradump/extradump_test.go b/cmd/extradump/extradump_test.go new file mode 100644 index 0000000000..f203d322e6 --- /dev/null +++ b/cmd/extradump/extradump_test.go @@ -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) + } + } + } +} diff --git a/cmd/extradump/main.go b/cmd/extradump/main.go new file mode 100644 index 0000000000..a19b6439ee --- /dev/null +++ b/cmd/extradump/main.go @@ -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)) +}