From f0c0ceb204e06d1526bfc59a8125bbc9520ad08d Mon Sep 17 00:00:00 2001 From: fearlseefe <505380967@qq.com> Date: Tue, 9 Apr 2024 18:30:21 +0800 Subject: [PATCH] feat: verify update --- portalnetwork/beacon/light_client.go | 171 +++++++++++++++++++- portalnetwork/beacon/light_client_helper.go | 46 ++++++ 2 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 portalnetwork/beacon/light_client_helper.go diff --git a/portalnetwork/beacon/light_client.go b/portalnetwork/beacon/light_client.go index 4e154f493c..106f0bd300 100644 --- a/portalnetwork/beacon/light_client.go +++ b/portalnetwork/beacon/light_client.go @@ -10,8 +10,22 @@ import ( "github.com/protolambda/zrnt/eth2/beacon/capella" "github.com/protolambda/zrnt/eth2/beacon/common" "github.com/protolambda/zrnt/eth2/util/merkle" + + "github.com/ethereum/go-ethereum/common/hexutil" + blsu "github.com/protolambda/bls12-381-util" "github.com/protolambda/ztyp/tree" "github.com/protolambda/ztyp/view" + "github.com/prysmaticlabs/go-bitfield" +) + +var ( + ErrInsufficientParticipation = errors.New("insufficient participation") + ErrInvalidTimestamp = errors.New("invalid timestamp") + ErrInvalidPeriod = errors.New("invalid sync committee period") + ErrNotRelevant = errors.New("update not relevant") + ErrInvalidFinalityProof = errors.New("invalid finality proof") + ErrInvalidNextSyncCommitteeProof = errors.New("invalid next sync committee proof") + ErrInvalidSignature = errors.New("invalid sync committee signature") ) type ConsensusAPI interface { @@ -24,10 +38,10 @@ type ConsensusAPI interface { } type LightClientStore struct { - FinalizedHeader common.BeaconBlockHeader - CurrentSyncCommittee common.SyncCommittee - NextSyncCommittee common.SyncCommittee - OptimisticHeader common.BeaconBlockHeader + FinalizedHeader *common.BeaconBlockHeader + CurrentSyncCommittee *common.SyncCommittee + NextSyncCommittee *common.SyncCommittee + OptimisticHeader *common.BeaconBlockHeader PreviousMaxActiveParticipants view.Uint64View CurrentMaxActiveParticipants view.Uint64View } @@ -93,9 +107,9 @@ func (c *ConsensusLightClient) bootstrap() error { } c.Store = LightClientStore{ - FinalizedHeader: bootstrap.Header.Beacon, - CurrentSyncCommittee: bootstrap.CurrentSyncCommittee, - OptimisticHeader: bootstrap.Header.Beacon, + FinalizedHeader: &bootstrap.Header.Beacon, + CurrentSyncCommittee: &bootstrap.CurrentSyncCommittee, + OptimisticHeader: &bootstrap.Header.Beacon, PreviousMaxActiveParticipants: view.Uint64View(0), CurrentMaxActiveParticipants: view.Uint64View(0), } @@ -135,3 +149,146 @@ func (c *ConsensusLightClient) slotTimestamp(slot common.Slot) (common.Timestamp func (c *ConsensusLightClient) isCurrentCommitteeProofValid(attestedHeader common.BeaconBlockHeader, currentCommittee common.SyncCommittee, currentCommitteeBranch altair.SyncCommitteeProofBranch) bool { return merkle.VerifyMerkleBranch(currentCommittee.HashTreeRoot(c.Config.Spec, tree.GetHashFn()), currentCommitteeBranch[:], 5, 22, attestedHeader.StateRoot) } +type GenericUpdate struct { + AttestedHeader *common.BeaconBlockHeader + SyncAggregate *altair.SyncAggregate + SingnatureSlot common.Slot + NextSyncCommittee *common.SyncCommittee + NextSyncCommitteeBranch *altair.SyncCommitteeProofBranch + FinalizedHeader *common.BeaconBlockHeader + FinalityBranch *altair.FinalizedRootProofBranch +} + +func FromLightClientUpdate(update *capella.LightClientUpdate) *GenericUpdate { + return &GenericUpdate{ + AttestedHeader: &update.AttestedHeader.Beacon, + SyncAggregate: &update.SyncAggregate, + SingnatureSlot: update.SignatureSlot, + NextSyncCommittee: &update.NextSyncCommittee, + NextSyncCommitteeBranch: &update.NextSyncCommitteeBranch, + FinalizedHeader: &update.FinalizedHeader.Beacon, + FinalityBranch: &update.FinalityBranch, + } +} + +func FromLightClientFinalityUpdate(update *capella.LightClientFinalityUpdate) *GenericUpdate { + return &GenericUpdate{ + AttestedHeader: &update.AttestedHeader.Beacon, + SyncAggregate: &update.SyncAggregate, + SingnatureSlot: update.SignatureSlot, + FinalizedHeader: &update.FinalizedHeader.Beacon, + FinalityBranch: &update.FinalityBranch, + } +} + +func FromLightClientOptimisticUpdate(update *capella.LightClientOptimisticUpdate) *GenericUpdate { + return &GenericUpdate{ + AttestedHeader: &update.AttestedHeader.Beacon, + SyncAggregate: &update.SyncAggregate, + SingnatureSlot: update.SignatureSlot, + } +} + +func (clc *ConsensusLightClient) VerifyGenericUpdate(update *GenericUpdate) error { + bits := bitfield.Bitlist(update.SyncAggregate.SyncCommitteeBits).Count() + if bits == 0 { + return ErrInsufficientParticipation + } + updateFinalizedSlot := update.FinalizedHeader.Slot + validTime := clc.CurrentSlot() >= uint64(update.SingnatureSlot) && update.SingnatureSlot > update.AttestedHeader.Slot && update.AttestedHeader.Slot >= updateFinalizedSlot + if !validTime { + return ErrInvalidTimestamp + } + + storePeriod := CalcSyncPeriod(uint64(clc.Store.FinalizedHeader.Slot)) + updateSigPeriod := CalcSyncPeriod(uint64(update.SingnatureSlot)) + validPeriod := false + if clc.Store.NextSyncCommittee != nil { + validPeriod = (updateSigPeriod == storePeriod || updateSigPeriod == storePeriod+1) + } else { + validPeriod = (updateSigPeriod == storePeriod) + } + if !validPeriod { + return ErrInvalidPeriod + } + + updateAttestedPeriod := CalcSyncPeriod(uint64(update.AttestedHeader.Slot)) + updateHasNextCommittee := (clc.Store.NextSyncCommittee == nil && update.NextSyncCommittee != nil && updateAttestedPeriod == storePeriod) + + if update.AttestedHeader.Slot <= clc.Store.FinalizedHeader.Slot && !updateHasNextCommittee { + return ErrNotRelevant + } + if update.FinalizedHeader != nil && update.FinalityBranch != nil { + isValid := IsFinalityProofValid(*update.AttestedHeader, *update.FinalizedHeader, *update.FinalityBranch) + if !isValid { + return ErrInvalidFinalityProof + } + } + if update.NextSyncCommittee != nil && update.NextSyncCommitteeBranch != nil { + isValid := IsNextCommitteeProofValid(clc.Config.Spec, *update.AttestedHeader, *update.NextSyncCommittee, *update.NextSyncCommitteeBranch) + if !isValid { + return ErrInvalidNextSyncCommitteeProof + } + } + var syncCommittee *common.SyncCommittee + + if updateSigPeriod == storePeriod { + syncCommittee = clc.Store.CurrentSyncCommittee + } else { + syncCommittee = clc.Store.NextSyncCommittee + } + + pks := GetParticipatingKeys(*syncCommittee, update.SyncAggregate.SyncCommitteeBits) + + isValidSig, err := clc.VerifySyncCommitteeSignature(pks, *update.AttestedHeader, update.SyncAggregate.SyncCommitteeSignature, update.SingnatureSlot) + if err != nil { + return err + } + if !isValidSig { + return ErrInvalidSignature + } + return nil +} + +func (clc *ConsensusLightClient) VerifyFinalityUpdate(update *capella.LightClientFinalityUpdate) error { + genericUpdate := FromLightClientFinalityUpdate(update) + return clc.VerifyGenericUpdate(genericUpdate) +} + +func (clc *ConsensusLightClient) VerifyOptimisticUpdate(update *capella.LightClientOptimisticUpdate) error { + genericUpdate := FromLightClientOptimisticUpdate(update) + return clc.VerifyGenericUpdate(genericUpdate) +} + +func (clc *ConsensusLightClient) VerifySyncCommitteeSignature(pks []common.BLSPubkey, attestedHeader common.BeaconBlockHeader, signature common.BLSSignature, signatureSlot common.Slot) (bool, error) { + headerRoot := attestedHeader.HashTreeRoot(tree.GetHashFn()) + signingRoot := clc.ComputeCommitteeSignRoot(headerRoot, signatureSlot) + blsuPubKeys := make([]*blsu.Pubkey, 0, len(pks)) + for _, p := range pks { + blsuPubKey, err := p.Pubkey() + if err != nil { + return false, err + } + blsuPubKeys = append(blsuPubKeys, blsuPubKey) + } + blsuSig, err := signature.Signature() + if err != nil { + return false, err + } + return blsu.FastAggregateVerify(blsuPubKeys, signingRoot[:], blsuSig), nil +} + +func (clc *ConsensusLightClient) ComputeCommitteeSignRoot(headerRoot tree.Root, slot common.Slot) common.Root { + genesisRoot := clc.Config.ChainConfig.GenesisRoot + domainType := hexutil.MustDecode("0x07000000") + forkVersion := clc.Config.Spec.ForkVersion(slot) + domain := common.ComputeDomain(common.BLSDomainType(domainType), forkVersion, genesisRoot) + return ComputeSigningRoot(headerRoot, domain) +} + +func (clc *ConsensusLightClient) CurrentSlot() uint64 { + now := time.Now().Unix() + genesisTime := clc.Config.ChainConfig.GenesisTime + sinceGenesis := now - int64(genesisTime) + return uint64(sinceGenesis / 2) +} diff --git a/portalnetwork/beacon/light_client_helper.go b/portalnetwork/beacon/light_client_helper.go new file mode 100644 index 0000000000..b801c18c7f --- /dev/null +++ b/portalnetwork/beacon/light_client_helper.go @@ -0,0 +1,46 @@ +package beacon + +import ( + "github.com/protolambda/zrnt/eth2/beacon/altair" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/configs" + "github.com/protolambda/zrnt/eth2/util/merkle" + "github.com/protolambda/ztyp/tree" + "github.com/prysmaticlabs/go-bitfield" +) + +func ComputeSigningRoot(root common.Root, domain common.BLSDomain) common.Root { + data := common.SigningData{ + ObjectRoot: root, + Domain: domain, + } + return data.HashTreeRoot(tree.GetHashFn()) +} + +func CalcSyncPeriod(slot uint64) uint64 { + epoch := slot / 32 // 32 slots per epoch + return epoch / 256 // 256 epochs per sync committee +} + +func IsFinalityProofValid(attestedHeader common.BeaconBlockHeader, finalityHeader common.BeaconBlockHeader, finalityBranch altair.FinalizedRootProofBranch) bool { + leaf := finalityHeader.HashTreeRoot(tree.GetHashFn()) + root := attestedHeader.StateRoot + return merkle.VerifyMerkleBranch(leaf, finalityBranch[:], 6, 41, root) +} + +func IsNextCommitteeProofValid(spec *common.Spec, attestedHeader common.BeaconBlockHeader, nextCommittee common.SyncCommittee, nextCommitteeBranch altair.SyncCommitteeProofBranch) bool { + leaf := nextCommittee.HashTreeRoot(configs.Mainnet, tree.GetHashFn()) + root := attestedHeader.StateRoot + return merkle.VerifyMerkleBranch(leaf, nextCommitteeBranch[:], 5, 23, root) +} + +func GetParticipatingKeys(committee common.SyncCommittee, syncBits altair.SyncCommitteeBits) []common.BLSPubkey { + bits := bitfield.Bitlist(syncBits) + res := make([]common.BLSPubkey, 0, bits.Count()) + for i := 0; i < int(bits.Len()); i++ { + if bits.BitAt(uint64(i)) { + res = append(res, committee.Pubkeys[i]) + } + } + return res +}