Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/validatorapi: add verification functions to validatorapi #953

Merged
merged 2 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
77 changes: 36 additions & 41 deletions core/validatorapi/validatorapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (
"github.com/obolnetwork/charon/app/log"
"github.com/obolnetwork/charon/app/z"
"github.com/obolnetwork/charon/core"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/eth2util/signing"
"github.com/obolnetwork/charon/tbls/tblsconv"
)
Expand Down Expand Up @@ -259,8 +258,11 @@ func (c Component) SubmitAttestations(ctx context.Context, attestations []*eth2p
return err
}

// Verify signature
if err = c.verifySignedData(ctx, c.eth2Cl, pubkey, att); err != nil {
// Verify attestation signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyAttestation(ctx, c.eth2Cl, pubshare, *att)
})
if err != nil {
return err
}

Expand Down Expand Up @@ -304,7 +306,11 @@ func (c Component) BeaconBlockProposal(ctx context.Context, slot eth2p0.Slot, ra
// TODO(corver): Refactor RANDAO partial signature to contain epoch.
parSig := core.NewPartialSignature(core.SigFromETH2(randao), c.shareIdx)

if err := c.verifyRandaoParSig(ctx, slot, randao, pubkey); err != nil {
// Verify randao signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyRandao(ctx, c.eth2Cl, pubshare, randao, slot)
})
if err != nil {
return nil, err
}

Expand Down Expand Up @@ -356,7 +362,11 @@ func (c Component) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedS
duty := core.NewProposerDuty(int64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

if err = c.verifySignedData(ctx, c.eth2Cl, pubkey, block); err != nil {
// Verify block signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyBlock(ctx, c.eth2Cl, pubshare, *block)
})
if err != nil {
return err
}

Expand Down Expand Up @@ -386,7 +396,11 @@ func (c Component) BlindedBeaconBlockProposal(ctx context.Context, slot eth2p0.S
return nil, err
}

if err := c.verifyRandaoParSig(ctx, slot, randao, pubkey); err != nil {
// Verify randao signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyRandao(ctx, c.eth2Cl, pubshare, randao, slot)
})
if err != nil {
return nil, err
}

Expand Down Expand Up @@ -441,7 +455,11 @@ func (c Component) SubmitBlindedBeaconBlock(ctx context.Context, block *eth2api.
duty := core.NewBuilderProposerDuty(int64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

if err = c.verifySignedData(ctx, c.eth2Cl, pubkey, block); err != nil {
// Verify Blinded block signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyBlindedBlock(ctx, c.eth2Cl, pubshare, *block)
})
if err != nil {
return err
}

Expand Down Expand Up @@ -489,7 +507,10 @@ func (c Component) submitRegistration(ctx context.Context, registration *eth2api
duty := core.NewBuilderRegistrationDuty(int64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

if err = c.verifySignedData(ctx, c.eth2Cl, pubkey, registration); err != nil {
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyValidatorRegistration(ctx, c.eth2Cl, pubshare, *registration)
})
if err != nil {
return err
}

Expand Down Expand Up @@ -556,7 +577,11 @@ func (c Component) SubmitVoluntaryExit(ctx context.Context, exit *eth2p0.SignedV
duty := core.NewVoluntaryExit(int64(slotsPerEpoch) * int64(exit.Message.Epoch))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

if err = c.verifySignedData(ctx, c.eth2Cl, pubkey, exit); err != nil {
// Verify voluntary exit signature
err = c.verifyPartialSig(pubkey, func(pubshare *bls_sig.PublicKey) error {
return signing.VerifyVoluntaryExit(ctx, c.eth2Cl, pubshare, *exit)
})
if err != nil {
return err
}

Expand Down Expand Up @@ -703,37 +728,7 @@ func (c Component) getProposerPubkey(ctx context.Context, duty core.Duty) (core.
return pubkey, nil
}

// TODO(corver): Use verifySignedData once randao partial signature contains epoch.
func (c Component) verifyRandaoParSig(ctx context.Context, slot eth2p0.Slot,
randao eth2p0.BLSSignature, pubkey core.PubKey,
) error {
if c.insecureTest {
return nil
}

epoch, err := c.epochFromSlot(ctx, slot)
if err != nil {
return err
}

// Randao signing root is the epoch.
sigRoot, err := eth2util.EpochHashRoot(epoch)
if err != nil {
return err
}

pubshare, err := c.getVerifyShareFunc(pubkey)
if err != nil {
return err
}

return signing.Verify(ctx, c.eth2Cl, signing.DomainRandao, epoch, sigRoot, randao, pubshare)
}

// verifySignedData aliases signing.VerifySignedData skipping it for insecureTest.
func (c Component) verifySignedData(ctx context.Context, eth2Cl signing.Eth2Provider,
pubkey core.PubKey, eth2SignedType interface{},
) error {
func (c Component) verifyPartialSig(pubkey core.PubKey, verifyFunc func(pubshare *bls_sig.PublicKey) error) error {
if c.insecureTest {
return nil
}
Expand All @@ -743,5 +738,5 @@ func (c Component) verifySignedData(ctx context.Context, eth2Cl signing.Eth2Prov
return err
}

return signing.VerifySignedData(ctx, eth2Cl, pubshare, eth2SignedType)
return verifyFunc(pubshare)
}
190 changes: 89 additions & 101 deletions eth2util/signing/signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ package signing

import (
"context"
"fmt"
"reflect"

eth2client "github.com/attestantio/go-eth2-client"
eth2api "github.com/attestantio/go-eth2-client/api"
Expand All @@ -27,7 +25,7 @@ import (
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"

"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/app/z"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/tbls"
"github.com/obolnetwork/charon/tbls/tblsconv"
)
Expand Down Expand Up @@ -128,121 +126,111 @@ func GetDataRoot(ctx context.Context, eth2Cl Eth2Provider, name DomainName, epoc
//
//}

// VerifySignedData verifies any eth2 signed data signature.
// If it is partially signed, provide the pubshare.
// If it is aggregate signed, provide the group pubkey.
func VerifySignedData(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey,
eth2SignedType interface{},
) error {
// eth2util shouldn't import core package, so can't use core.SignedData.
// To avoid pointer vs non-pointer issues, always get value if pointer is provided.
if reflect.TypeOf(eth2SignedType).Kind() == reflect.Pointer {
eth2SignedType = reflect.ValueOf(eth2SignedType).Elem().Interface()
func VerifyAttestation(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, att eth2p0.Attestation) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all these eth2 types should be pointers, since that is how goeth2 users them, always as pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

sigRoot, err := att.Data.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "hash attestation data")
}

switch signed := eth2SignedType.(type) {
case eth2p0.Attestation:
sigRoot, err := signed.Data.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "hash attestation data")
}
return verify(ctx, eth2Cl, DomainBeaconAttester, att.Data.Target.Epoch, sigRoot, att.Signature, pubkey)
}

return Verify(ctx, eth2Cl, DomainBeaconAttester, signed.Data.Target.Epoch,
sigRoot, signed.Signature, pubkey)
case spec.VersionedSignedBeaconBlock:
func VerifyBlock(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, block spec.VersionedSignedBeaconBlock) error {
slot, err := block.Slot()
if err != nil {
return err
}

slot, err := signed.Slot()
if err != nil {
return err
}
// Calculate slot epoch
epoch, err := epochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return err
}

// Calculate slot epoch
epoch, err := epochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return err
}
sigRoot, err := block.Root()
if err != nil {
return err
}

sigRoot, err := signed.Root()
if err != nil {
return err
}
var sig eth2p0.BLSSignature
switch block.Version {
case spec.DataVersionPhase0:
sig = block.Phase0.Signature
case spec.DataVersionAltair:
sig = block.Altair.Signature
case spec.DataVersionBellatrix:
sig = block.Bellatrix.Signature
default:
return errors.New("unknown version")
}

var sig eth2p0.BLSSignature
switch signed.Version {
case spec.DataVersionPhase0:
sig = signed.Phase0.Signature
case spec.DataVersionAltair:
sig = signed.Altair.Signature
case spec.DataVersionBellatrix:
sig = signed.Bellatrix.Signature
default:
return errors.New("unknown version")
}
return verify(ctx, eth2Cl, DomainBeaconProposer, epoch, sigRoot, sig, pubkey)
}

return Verify(ctx, eth2Cl, DomainBeaconProposer, epoch, sigRoot, sig, pubkey)
case eth2api.VersionedSignedBlindedBeaconBlock:
slot, err := signed.Slot()
if err != nil {
return err
}
func VerifyBlindedBlock(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, block eth2api.VersionedSignedBlindedBeaconBlock) error {
slot, err := block.Slot()
if err != nil {
return err
}

// Calculate slot epoch
epoch, err := epochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return err
}
// Calculate slot epoch
epoch, err := epochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return err
}

sigRoot, err := signed.Root()
if err != nil {
return err
}
sigRoot, err := block.Root()
if err != nil {
return err
}

var sig eth2p0.BLSSignature
switch signed.Version {
case spec.DataVersionBellatrix:
sig = signed.Bellatrix.Signature
default:
return errors.New("unknown version")
}
var sig eth2p0.BLSSignature
switch block.Version {
case spec.DataVersionBellatrix:
sig = block.Bellatrix.Signature
default:
return errors.New("unknown version")
}

return Verify(ctx, eth2Cl, DomainBeaconProposer, epoch, sigRoot, sig, pubkey)
// case core.Signature:
// TODO(corver): Refactor randao SignedData to include epoch.
// return errors.New("randao not supported yet")

// var epoch eth2p0.Epoch
//
// sigRoot, err := eth2util.EpochHashRoot(epoch)
// if err != nil {
// return err
//}
//
// return Verify(ctx, eth2Cl, DomainRandao, epoch, sigRoot, signed.ToETH2(), pubkey)
case eth2p0.SignedVoluntaryExit:
sigRoot, err := signed.Message.HashTreeRoot()
if err != nil {
return err
}
return verify(ctx, eth2Cl, DomainBeaconProposer, epoch, sigRoot, sig, pubkey)
}

return Verify(ctx, eth2Cl, DomainExit, signed.Message.Epoch, sigRoot,
signed.Signature, pubkey)
case eth2api.VersionedSignedValidatorRegistration:
// TODO: switch to signed.Root() when implemented on go-eth2-client (PR has been raised)
sigRoot, err := signed.V1.Message.HashTreeRoot()
if err != nil {
return err
}
func VerifyRandao(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, randao eth2p0.BLSSignature, slot eth2p0.Slot) error {
// Calculate slot epoch
epoch, err := epochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return err
}

return Verify(ctx, eth2Cl, DomainApplicationBuilder, 0, sigRoot,
signed.V1.Signature, pubkey)
default:
return errors.New("unsupported eth2 signed data type", z.Str("type", fmt.Sprintf("%T", eth2SignedType)))
sigRoot, err := eth2util.EpochHashRoot(epoch)
if err != nil {
return err
}

return verify(ctx, eth2Cl, DomainRandao, epoch, sigRoot, randao, pubkey)
}

func VerifyVoluntaryExit(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, exit eth2p0.SignedVoluntaryExit) error {
sigRoot, err := exit.Message.HashTreeRoot()
if err != nil {
return err
}

return verify(ctx, eth2Cl, DomainExit, exit.Message.Epoch, sigRoot, exit.Signature, pubkey)
}

func VerifyValidatorRegistration(ctx context.Context, eth2Cl Eth2Provider, pubkey *bls_sig.PublicKey, reg eth2api.VersionedSignedValidatorRegistration) error {
// TODO: switch to signed.Root() when implemented on go-eth2-client (PR has been raised)
sigRoot, err := reg.V1.Message.HashTreeRoot()
if err != nil {
return err
}

return verify(ctx, eth2Cl, DomainApplicationBuilder, 0, sigRoot, reg.V1.Signature, pubkey)
}

// Verify returns an error if the signature doesn't match the eth2 domain signed root.
// TODO(corver): Unexport this once Randao partial signatures contain their epoch.
func Verify(ctx context.Context, eth2Cl Eth2Provider, domain DomainName, epoch eth2p0.Epoch,
// verify returns an error if the signature doesn't match the eth2 domain signed root.
func verify(ctx context.Context, eth2Cl Eth2Provider, domain DomainName, epoch eth2p0.Epoch,
sigRoot [32]byte, sig eth2p0.BLSSignature, pubshare *bls_sig.PublicKey,
) error {
sigData, err := GetDataRoot(ctx, eth2Cl, domain, epoch, sigRoot)
Expand Down
Loading