fff843cfaf
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.
153 lines
5.9 KiB
Go
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
|
|
}
|