plugeth/beacon/light/test_helpers.go
Felföldi Zsolt fff843cfaf
beacon/light: add CommitteeChain (#27766)
This change implements CommitteeChain which is a key component of the beacon light client. It is a passive data structure that can validate, hold and update a chain of beacon light sync committees and updates, starting from a checkpoint that proves the starting committee through a beacon block hash, header and corresponding state. Once synced to the current sync period, CommitteeChain can also validate signed beacon headers.
2023-12-08 13:38:00 +01:00

153 lines
5.9 KiB
Go

// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"crypto/rand"
"crypto/sha256"
mrand "math/rand"
"github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/beacon/params"
"github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common"
)
func GenerateTestCommittee() *types.SerializedSyncCommittee {
s := new(types.SerializedSyncCommittee)
rand.Read(s[:32])
return s
}
func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate {
update := new(types.LightClientUpdate)
update.NextSyncCommitteeRoot = nextCommittee.Root()
var attestedHeader types.Header
if finalizedHeader {
update.FinalizedHeader = new(types.Header)
*update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock, merkle.Value(update.FinalizedHeader.Hash()))
} else {
attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
}
update.AttestedHeader = GenerateTestSignedHeader(attestedHeader, config, committee, attestedHeader.Slot+1, signerCount)
return update
}
func GenerateTestSignedHeader(header types.Header, config *types.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader {
bitmask := makeBitmask(signerCount)
signingRoot, _ := config.Forks.SigningRoot(header)
c, _ := dummyVerifier{}.deserializeSyncCommittee(committee)
return types.SignedHeader{
Header: header,
Signature: types.SyncAggregate{
Signers: bitmask,
Signature: makeDummySignature(c.(dummySyncCommittee), signingRoot, bitmask),
},
SignatureSlot: signatureSlot,
}
}
func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *types.BootstrapData {
header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee, merkle.Value(committee.Root()))
return &types.BootstrapData{
Header: header,
Committee: committee,
CommitteeRoot: committee.Root(),
CommitteeBranch: branch,
}
}
func makeBitmask(signerCount int) (bitmask [params.SyncCommitteeBitmaskSize]byte) {
for i := 0; i < params.SyncCommitteeSize; i++ {
if mrand.Intn(params.SyncCommitteeSize-i) < signerCount {
bitmask[i/8] += byte(1) << (i & 7)
signerCount--
}
}
return
}
func makeTestHeaderWithMerkleProof(slot, index uint64, value merkle.Value) (types.Header, merkle.Values) {
var branch merkle.Values
hasher := sha256.New()
for index > 1 {
var proofHash merkle.Value
rand.Read(proofHash[:])
hasher.Reset()
if index&1 == 0 {
hasher.Write(value[:])
hasher.Write(proofHash[:])
} else {
hasher.Write(proofHash[:])
hasher.Write(value[:])
}
hasher.Sum(value[:0])
index >>= 1
branch = append(branch, proofHash)
}
return types.Header{Slot: slot, StateRoot: common.Hash(value)}, branch
}
// syncCommittee holds either a blsSyncCommittee or a fake dummySyncCommittee used for testing
type syncCommittee interface{}
// committeeSigVerifier verifies sync committee signatures (either proper BLS
// signatures or fake signatures used for testing)
type committeeSigVerifier interface {
deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error)
verifySignature(committee syncCommittee, signedRoot common.Hash, aggregate *types.SyncAggregate) bool
}
// blsVerifier implements committeeSigVerifier
type blsVerifier struct{}
// deserializeSyncCommittee implements committeeSigVerifier
func (blsVerifier) deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error) {
return s.Deserialize()
}
// verifySignature implements committeeSigVerifier
func (blsVerifier) verifySignature(committee syncCommittee, signingRoot common.Hash, aggregate *types.SyncAggregate) bool {
return committee.(*types.SyncCommittee).VerifySignature(signingRoot, aggregate)
}
type dummySyncCommittee [32]byte
// dummyVerifier implements committeeSigVerifier
type dummyVerifier struct{}
// deserializeSyncCommittee implements committeeSigVerifier
func (dummyVerifier) deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error) {
var sc dummySyncCommittee
copy(sc[:], s[:32])
return sc, nil
}
// verifySignature implements committeeSigVerifier
func (dummyVerifier) verifySignature(committee syncCommittee, signingRoot common.Hash, aggregate *types.SyncAggregate) bool {
return aggregate.Signature == makeDummySignature(committee.(dummySyncCommittee), signingRoot, aggregate.Signers)
}
func makeDummySignature(committee dummySyncCommittee, signingRoot common.Hash, bitmask [params.SyncCommitteeBitmaskSize]byte) (sig [params.BLSSignatureSize]byte) {
for i, b := range committee[:] {
sig[i] = b ^ signingRoot[i]
}
copy(sig[32:], bitmask[:])
return
}