Skip to content
This repository has been archived by the owner on Feb 18, 2025. It is now read-only.

consortium-v2: new RLP encoder/decoder for HeaderExtraData #415

Merged
merged 5 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions consensus/consortium/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ func (api *consortiumV2Api) GetFinalityVoteAtHash(hash common.Hash) (*finalityVo
return nil, consortiumCommon.ErrUnknownBlock
}

isShillin := api.consortium.chainConfig.IsShillin(header.Number)
extraData, err := finality.DecodeExtra(header.Extra, isShillin)
extraData, err := finality.DecodeExtraV2(header.Extra, api.consortium.chainConfig, header.Number)
if err != nil {
return nil, err
}
Expand Down
33 changes: 23 additions & 10 deletions consensus/consortium/v2/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he

// Check extra data
isShillin := c.chainConfig.IsShillin(header.Number)
extraData, err := finality.DecodeExtra(header.Extra, isShillin)
extraData, err := finality.DecodeExtraV2(header.Extra, c.chainConfig, header.Number)
if err != nil {
return err
}
Expand Down Expand Up @@ -726,7 +726,6 @@ func (c *Consortium) Prepare(chain consensus.ChainHeaderReader, header *types.He
// Set the correct difficulty
header.Difficulty = CalcDifficulty(snap, coinbase)

isShillin := c.chainConfig.IsShillin(header.Number)
var extraData finality.HeaderExtraData

if number%c.config.EpochV2 == 0 || c.chainConfig.IsOnConsortiumV2(big.NewInt(int64(number))) {
Expand All @@ -741,7 +740,10 @@ func (c *Consortium) Prepare(chain consensus.ChainHeaderReader, header *types.He
// not assemble finality vote yet. Let's wait some time for the
// finality votes to be broadcasted around the network. The
// finality votes are assembled later in Seal function.
header.Extra = extraData.Encode(isShillin)
header.Extra, err = extraData.EncodeV2(c.chainConfig, header.Number)
if err != nil {
return err
}

// Mix digest is reserved for now, set to empty
header.MixDigest = common.Hash{}
Expand All @@ -767,9 +769,10 @@ func (c *Consortium) processSystemTransactions(chain consensus.ChainHeaderReader
_, _, _, contract := c.readSignerAndContract()

// If the parent's block includes the finality votes, distribute reward for the voters
if c.chainConfig.IsShillin(new(big.Int).Sub(header.Number, common.Big1)) {
parentNumber := new(big.Int).Sub(header.Number, common.Big1)
if c.chainConfig.IsShillin(parentNumber) {
parentHeader := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
extraData, err := finality.DecodeExtra(parentHeader.Extra, true)
extraData, err := finality.DecodeExtraV2(parentHeader.Extra, c.chainConfig, parentNumber)
if err != nil {
return err
}
Expand Down Expand Up @@ -891,7 +894,7 @@ func (c *Consortium) Finalize(chain consensus.ChainHeaderReader, header *types.H
if err != nil {
return err
}
extraData, err := finality.DecodeExtra(header.Extra, isShillin)
extraData, err := finality.DecodeExtraV2(header.Extra, c.chainConfig, header.Number)
if err != nil {
return err
}
Expand Down Expand Up @@ -1097,9 +1100,15 @@ func (c *Consortium) SealHash(header *types.Header) common.Hash {
// FinalizeAndAssemble call.
copyHeader := types.CopyHeader(header)

extraData, _ := finality.DecodeExtra(copyHeader.Extra, true)
extraData, err := finality.DecodeExtraV2(copyHeader.Extra, c.chainConfig, header.Number)
if err != nil {
log.Error("Failed to decode header extra data", "err", err)
}
extraData.HasFinalityVote = 0
copyHeader.Extra = extraData.Encode(true)
copyHeader.Extra, err = extraData.EncodeV2(c.chainConfig, header.Number)
if err != nil {
log.Error("Failed to encode header extra data", "err", err)
}
return calculateSealHash(copyHeader, c.chainConfig.ChainID)
} else {
return calculateSealHash(header, c.chainConfig.ChainID)
Expand Down Expand Up @@ -1263,7 +1272,7 @@ func (c *Consortium) assembleFinalityVote(header *types.Header, snap *Snapshot)

bitSetCount := len(finalityVotedValidators.Indices())
if bitSetCount >= finalityThreshold {
extraData, err := finality.DecodeExtra(header.Extra, true)
extraData, err := finality.DecodeExtraV2(header.Extra, c.chainConfig, header.Number)
if err != nil {
// This should not happen
log.Error("Failed to decode header extra data", "err", err)
Expand All @@ -1272,7 +1281,11 @@ func (c *Consortium) assembleFinalityVote(header *types.Header, snap *Snapshot)
extraData.HasFinalityVote = 1
extraData.FinalityVotedValidators = finalityVotedValidators
extraData.AggregatedFinalityVotes = blst.AggregateSignatures(signatures)
header.Extra = extraData.Encode(true)
header.Extra, err = extraData.EncodeV2(c.chainConfig, header.Number)
if err != nil {
log.Error("Failed to encode header extra data", "err", err)
return
}
}
}
}
Expand Down
169 changes: 169 additions & 0 deletions consensus/consortium/v2/consortium_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package v2
import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"math/big"
"testing"
"time"
Expand Down Expand Up @@ -530,6 +532,173 @@ func TestExtraDataDecode(t *testing.T) {
}
}

func mockExtraData(nVal int, bits uint32) *finality.HeaderExtraData {
var (
finalityVotedValidators finality.FinalityVoteBitSet
aggregatedFinalityVotes blsCommon.Signature
checkpointValidators []finality.ValidatorWithBlsPub
seal = make([]byte, finality.ExtraSeal)
ret = &finality.HeaderExtraData{}
)

bits = bits % 7 // ensure bits can be represented by 3-bit integer
for i := 0; i < 3; i++ {
if bits&(1<<i) != 0 {
switch i {
case 0:
ret.HasFinalityVote = 1
finalityVotedValidators = finality.FinalityVoteBitSet(uint64(8))
ret.FinalityVotedValidators = finalityVotedValidators

delegated, _ := blst.RandKey()
msg := make([]byte, 64)
rand.Read(msg)
aggregatedFinalityVotes = delegated.Sign(msg)
ret.AggregatedFinalityVotes = aggregatedFinalityVotes
case 1:
for i := 0; i < nVal; i++ {
s, _ := blst.RandKey()
sk, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
addr := crypto.PubkeyToAddress(sk.PublicKey)
val := finality.ValidatorWithBlsPub{
Address: addr,
BlsPublicKey: s.PublicKey(),
}
checkpointValidators = append(checkpointValidators, val)
}
ret.CheckpointValidators = checkpointValidators
case 2:
// Even seal does not get assigned a random value, seal
// is still be zero-filled byte array of size ExtraSeal
rand.Read(seal)
ret.Seal = [finality.ExtraSeal]byte(seal)
}
}
}
return ret
}

func TestExtraDataEncodeRLP(t *testing.T) {
nVal := 22
for i := 0; i < 7; i++ {
ext := mockExtraData(nVal, uint32(i))
enc, err := ext.EncodeRLP()
if err != nil {
t.Errorf("encode rlp error: %v", err)
}
if len(enc) < finality.ExtraSeal {
t.Error("encode rlp error: invalid length of encoded data")
}
}

var extraData finality.HeaderExtraData
extraData.HasFinalityVote = 2
_, err := extraData.EncodeRLP()
if !errors.Is(err, finality.ErrInvalidHasFinalityVote) {
t.Fatalf("Expect error: %s, got: %s", finality.ErrInvalidHasFinalityVote, err)
}
}

func TestExtraDataDecodeRLP(t *testing.T) {
nVals := 22
// loop 64 times, equivalent to 64 combinations of 6 bits
for i := 0; i < 7; i++ {
ext := mockExtraData(nVals, uint32(i))
enc, err := ext.EncodeRLP()
if err != nil {
t.Errorf("encode rlp error: %v", err)
}
dec, err := finality.DecodeExtraRLP(enc)
if err != nil {
t.Errorf("decode rlp error: %v", err)
}
if !bytes.Equal(dec.Vanity[:], ext.Vanity[:]) {
t.Errorf("Mismatched decoded data")
}
if dec.FinalityVotedValidators != ext.FinalityVotedValidators {
t.Errorf("Mismatch decoded data")
}
if (dec.AggregatedFinalityVotes != nil && ext.AggregatedFinalityVotes == nil) ||
(dec.AggregatedFinalityVotes == nil && ext.AggregatedFinalityVotes != nil) {
t.Errorf("Mismatch decoded data")
}
if dec.AggregatedFinalityVotes != nil &&
ext.AggregatedFinalityVotes != nil &&
!bytes.Equal(dec.AggregatedFinalityVotes.Marshal(), ext.AggregatedFinalityVotes.Marshal()) {
t.Errorf("Mismatch decoded data")
}
if len(dec.CheckpointValidators) != len(ext.CheckpointValidators) {
t.Errorf("Mismatch decoded data")
}
for i := 0; i < len(ext.CheckpointValidators); i++ {
if dec.CheckpointValidators[i].Address.Hex() != ext.CheckpointValidators[i].Address.Hex() {
t.Errorf("Mismatch decoded data")
}
if (dec.CheckpointValidators[i].BlsPublicKey == nil && ext.CheckpointValidators[i].BlsPublicKey != nil) ||
(dec.CheckpointValidators[i].BlsPublicKey != nil && ext.CheckpointValidators[i].BlsPublicKey == nil) {
t.Errorf("Mismatch decoded data")
}
if dec.CheckpointValidators[i].BlsPublicKey != nil &&
ext.CheckpointValidators[i].BlsPublicKey != nil &&
!bytes.Equal(dec.CheckpointValidators[i].BlsPublicKey.Marshal(), ext.CheckpointValidators[i].BlsPublicKey.Marshal()) {
t.Errorf("Mismatch decoded data")
}
}
if !bytes.Equal(dec.Seal[:], ext.Seal[:]) {
t.Errorf("Mismatch decoded data")
}
}

_, err := finality.DecodeExtraRLP([]byte{})
if !errors.Is(err, finality.ErrInvalidEncodedExtraData) {
t.Fatalf("Expect error: %s, got: %s", finality.ErrInvalidEncodedExtraData, err)
}

encodedData := [finality.ExtraSeal]byte{}
_, err = finality.DecodeExtraRLP(encodedData[:])
if !errors.Is(err, io.EOF) {
t.Fatalf("Expect error: %s, got: %s", io.EOF, err)
}
}

func BenchmarkEncodeRLP(b *testing.B) {
nVal := 22
ext := mockExtraData(nVal, 7)
b.ResetTimer()
for i := 0; i < b.N; i++ {
ext.EncodeRLP()
}
}

func BenchmarkEncode(b *testing.B) {
nVal := 22
ext := mockExtraData(nVal, 7)
b.ResetTimer()
for i := 0; i < b.N; i++ {
ext.Encode(true)
}
}

func BenchmarkDecodeRLP(b *testing.B) {
nVal := 22
ext := mockExtraData(nVal, 7)
dec, _ := ext.EncodeRLP()
b.ResetTimer()
for i := 0; i < b.N; i++ {
finality.DecodeExtraRLP(dec)
}
}

func BenchmarkDecode(b *testing.B) {
nVal := 22
ext := mockExtraData(nVal, 7)
dec := ext.Encode(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
finality.DecodeExtra(dec, true)
}
}

func TestVerifyFinalitySignature(t *testing.T) {
const numValidator = 3
var err error
Expand Down
Loading
Loading