Skip to content

Commit

Permalink
core/validatorapi: check proposal data against consensus one
Browse files Browse the repository at this point in the history
Instead of blindly accepting VC proposal data, check it for integrity against what we have in DutyDB: if they differ, stop the process.
  • Loading branch information
gsora committed Sep 18, 2024
1 parent c835711 commit 752db23
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 8 deletions.
134 changes: 128 additions & 6 deletions core/validatorapi/validatorapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (

eth2api "github.com/attestantio/go-eth2-client/api"
eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2spec "github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/altair"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
ssz "github.com/ferranbt/fastssz"
"go.opentelemetry.io/otel/trace"

"github.com/obolnetwork/charon/app/errors"
Expand Down Expand Up @@ -394,19 +396,120 @@ func (c Component) Proposal(ctx context.Context, opts *eth2api.ProposalOpts) (*e
return wrapResponse(proposal), nil
}

// propDataMatchesDuty checks that the VC-signed proposal data and prop are the same.
func propDataMatchesDuty(opts *eth2api.SubmitProposalOpts, prop *eth2api.VersionedProposal) error {
ourPropIdx, err := prop.ProposerIndex()
if err != nil {
return errors.Wrap(err, "cannot fetch validator index from dutydb proposal")
}

Check warning on line 404 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L403-L404

Added lines #L403 - L404 were not covered by tests

vcPropIdx, err := opts.Proposal.ProposerIndex()
if err != nil {
return errors.Wrap(err, "cannot fetch validator index from VC proposal")
}

Check warning on line 409 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L408-L409

Added lines #L408 - L409 were not covered by tests

if ourPropIdx != vcPropIdx {
return errors.New(
"dutydb and VC proposals have different index",
z.U64("vc", uint64(vcPropIdx)),
z.U64("dutydb", uint64(ourPropIdx)),
)
}

Check warning on line 417 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L412-L417

Added lines #L412 - L417 were not covered by tests

if opts.Proposal.Blinded != prop.Blinded {
return errors.New(
"dutydb and VC proposals have different blinded value",
z.Bool("vc", opts.Proposal.Blinded),
z.Bool("dutydb", prop.Blinded),
)
}

Check warning on line 425 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L420-L425

Added lines #L420 - L425 were not covered by tests

if opts.Proposal.Version != prop.Version {
return errors.New(
"dutydb and VC proposals have different version",
z.Str("vc", opts.Proposal.Version.String()),
z.Str("dutydb", prop.Version.String()),
)
}

Check warning on line 433 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L428-L433

Added lines #L428 - L433 were not covered by tests

checkHashes := func(d1, d2 ssz.HashRoot) error {
ddb, err := d1.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "hash tree root dutydb")
}

Check warning on line 439 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L438-L439

Added lines #L438 - L439 were not covered by tests

if d2 == nil {
return errors.New("validator client proposal data for the associated dutydb proposal is nil")
}

Check warning on line 443 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L442-L443

Added lines #L442 - L443 were not covered by tests

vc, err := d2.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "hash tree root dutydb")
}

Check warning on line 448 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L447-L448

Added lines #L447 - L448 were not covered by tests

if ddb != vc {
return errors.New("dutydb and VC proposal data have different hash tree root")
}

return nil
}

switch prop.Version {
case eth2spec.DataVersionPhase0:
return checkHashes(prop.Phase0, opts.Proposal.Phase0.Message)
case eth2spec.DataVersionAltair:
return checkHashes(prop.Altair, opts.Proposal.Altair.Message)

Check warning on line 461 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L458-L461

Added lines #L458 - L461 were not covered by tests
case eth2spec.DataVersionBellatrix:
switch prop.Blinded {
case false:
return checkHashes(prop.Bellatrix, opts.Proposal.Bellatrix.Message)
case true:
return checkHashes(prop.BellatrixBlinded, opts.Proposal.BellatrixBlinded.Message)
}
case eth2spec.DataVersionCapella:
switch prop.Blinded {
case false:
return checkHashes(prop.Capella, opts.Proposal.Capella.Message)
case true:
return checkHashes(prop.CapellaBlinded, opts.Proposal.CapellaBlinded.Message)
}
case eth2spec.DataVersionDeneb:
switch prop.Blinded {
case false:
return checkHashes(prop.Deneb.Block, opts.Proposal.Deneb.SignedBlock.Message)
case true:
return checkHashes(prop.DenebBlinded, opts.Proposal.DenebBlinded.Message)
}
case eth2spec.DataVersionUnknown:
return errors.New("unexpected block version", z.Str("version", prop.Version.String()))

Check warning on line 484 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L483-L484

Added lines #L483 - L484 were not covered by tests
}

return nil

Check warning on line 487 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L487

Added line #L487 was not covered by tests
}

func (c Component) SubmitProposal(ctx context.Context, opts *eth2api.SubmitProposalOpts) error {
slot, err := opts.Proposal.Slot()
if err != nil {
return err
}

pubkey, err := c.getProposerPubkey(ctx, core.NewProposerDuty(uint64(slot)))
duty := core.NewProposerDuty(uint64(slot))

pubkey, err := c.getProposerPubkey(ctx, duty)
if err != nil {
return err
}

prop, err := c.awaitProposalFunc(ctx, uint64(slot))
if err != nil {
return errors.Wrap(err, "could not fetch block definition from dutydb")
}

Check warning on line 506 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L505-L506

Added lines #L505 - L506 were not covered by tests

if err := propDataMatchesDuty(opts, prop); err != nil {
return errors.Wrap(err, "consensus proposal and VC-submitted one do not match")
}

// Save Partially Signed Block to ParSigDB
duty := core.NewProposerDuty(uint64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

signedData, err := core.NewPartialVersionedSignedProposal(opts.Proposal, c.shareIdx)
Expand Down Expand Up @@ -440,15 +543,34 @@ func (c Component) SubmitBlindedProposal(ctx context.Context, opts *eth2api.Subm
return err
}

pubkey, err := c.getProposerPubkey(ctx, core.NewProposerDuty(uint64(slot)))
duty := core.NewProposerDuty(uint64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

pubkey, err := c.getProposerPubkey(ctx, duty)
if err != nil {
return err
}

// Save Partially Signed Blinded Block to ParSigDB
duty := core.NewProposerDuty(uint64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))
prop, err := c.awaitProposalFunc(ctx, uint64(slot))
if err != nil {
return errors.Wrap(err, "could not fetch block definition from dutydb")
}

Check warning on line 557 in core/validatorapi/validatorapi.go

View check run for this annotation

Codecov / codecov/patch

core/validatorapi/validatorapi.go#L556-L557

Added lines #L556 - L557 were not covered by tests

if err := propDataMatchesDuty(&eth2api.SubmitProposalOpts{
Common: opts.Common,
Proposal: &eth2api.VersionedSignedProposal{
Version: opts.Proposal.Version,
Blinded: true,
BellatrixBlinded: opts.Proposal.Bellatrix,
CapellaBlinded: opts.Proposal.Capella,
DenebBlinded: opts.Proposal.Deneb,
},
BroadcastValidation: opts.BroadcastValidation,
}, prop); err != nil {
return errors.Wrap(err, "consensus proposal and VC-submitted one do not match")
}

// Save Partially Signed Blinded Block to ParSigDB
signedData, err := core.NewPartialVersionedSignedBlindedProposal(opts.Proposal, c.shareIdx)
if err != nil {
return err
Expand Down
Loading

0 comments on commit 752db23

Please sign in to comment.