Skip to content

Commit

Permalink
Merge pull request #72 from blocknative/fix/metric-error
Browse files Browse the repository at this point in the history
Generalize metric error types
  • Loading branch information
aratz-lasa authored Jan 16, 2023
2 parents c1cd8fc + f856462 commit 7464aa8
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 43 deletions.
41 changes: 35 additions & 6 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"github.com/blocknative/dreamboat/pkg/relay"
"github.com/blocknative/dreamboat/pkg/structs"
"github.com/blocknative/dreamboat/pkg/validators"
)

// Router paths
Expand Down Expand Up @@ -252,7 +253,7 @@ func (a *API) submitBlock(w http.ResponseWriter, r *http.Request) (int, error) {
m := structs.NewMetricGroup(4)
if err := a.r.SubmitBlock(r.Context(), m, &req); err != nil {
m.ObserveWithError(a.m.RelayTiming, unwrapError(err, "submit block unknown"))
if errors.Is(err, structs.ErrPayloadAlreadyDelivered) {
if errors.Is(err, relay.ErrPayloadAlreadyDelivered) {
a.m.ApiReqCounter.WithLabelValues("submitBlock", "400", "payload already delivered").Inc()
} else {
a.m.ApiReqCounter.WithLabelValues("submitBlock", "400", "block submission").Inc()
Expand Down Expand Up @@ -524,18 +525,46 @@ type jsonError struct {
}

func unwrapError(err error, defaultMsg string) error {
if errors.Is(err, relay.ErrNoPayloadFound) {
if errors.Is(err, relay.ErrUnknownValue) {
return relay.ErrUnknownValue
} else if errors.Is(err, relay.ErrPayloadAlreadyDelivered) {
return relay.ErrPayloadAlreadyDelivered
} else if errors.Is(err, relay.ErrNoPayloadFound) {
return relay.ErrNoPayloadFound
} else if errors.Is(err, relay.ErrNoBuilderBid) {
return relay.ErrNoBuilderBid
} else if errors.Is(err, relay.ErrBadHeader) {
return relay.ErrBadHeader
} else if errors.Is(err, relay.ErrMissingRequest) {
return relay.ErrMissingRequest
} else if errors.Is(err, relay.ErrMissingSecretKey) {
return relay.ErrMissingSecretKey
} else if errors.Is(err, relay.ErrNoBuilderBid) {
return relay.ErrNoBuilderBid
} else if errors.Is(err, relay.ErrOldSlot) {
return relay.ErrOldSlot
} else if errors.Is(err, relay.ErrBadHeader) {
return relay.ErrBadHeader
} else if errors.Is(err, relay.ErrInvalidSignature) {
return relay.ErrInvalidSignature
} else if errors.Is(err, relay.ErrStore) {
return relay.ErrStore
} else if errors.Is(err, relay.ErrMarshal) {
return relay.ErrMarshal
} else if errors.Is(err, relay.ErrInternal) {
return relay.ErrInternal
} else if errors.Is(err, relay.ErrUnknownValidator) {
return relay.ErrUnknownValidator
} else if errors.Is(err, relay.ErrVerification) {
return relay.ErrVerification
} else if errors.Is(err, relay.ErrInvalidTimestamp) {
return relay.ErrInvalidTimestamp
} else if errors.Is(err, relay.ErrInvalidSlot) {
return relay.ErrInvalidSlot
} else if errors.Is(err, relay.ErrEmptyBlock) {
return relay.ErrEmptyBlock
} else if errors.Is(err, validators.ErrInvalidSignature) {
return validators.ErrInvalidSignature
} else if errors.Is(err, validators.ErrUnknownValidator) {
return validators.ErrUnknownValidator
} else if errors.Is(err, validators.ErrInvalidTimestamp) {
return validators.ErrInvalidTimestamp
}

return errors.New(defaultMsg)
Expand Down
61 changes: 36 additions & 25 deletions pkg/relay/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ import (
"github.com/blocknative/dreamboat/pkg/verify"
)

var (
ErrUnknownValue = errors.New("value is unknown")
ErrPayloadAlreadyDelivered = errors.New("slot payload already delivered")
ErrNoPayloadFound = errors.New("no payload found")
ErrMissingRequest = errors.New("req is nil")
ErrMissingSecretKey = errors.New("secret key is nil")
ErrNoBuilderBid = errors.New("no builder bid")
ErrOldSlot = errors.New("requested slot is old")
ErrBadHeader = errors.New("invalid block header from datastore")
ErrInvalidSignature = errors.New("invalid signature")
ErrStore = errors.New("failed to store")
ErrMarshal = errors.New("failed to marshal")
ErrInternal = errors.New("internal server error")
ErrUnknownValidator = errors.New("unknown validator")
ErrVerification = errors.New("failed to verify")
ErrInvalidTimestamp = errors.New("invalid timestamp")
ErrInvalidSlot = errors.New("invalid slot")
ErrEmptyBlock = errors.New("block is empty")
)

type State interface {
Beacon() *structs.BeaconState
}
Expand All @@ -24,15 +44,6 @@ type Verifier interface {
Enqueue(ctx context.Context, sig [96]byte, pubkey [48]byte, msg [32]byte) (err error)
}

var (
ErrNoPayloadFound = errors.New("no payload found")
ErrMissingRequest = errors.New("req is nil")
ErrMissingSecretKey = errors.New("secret key is nil")
ErrNoBuilderBid = errors.New("no builder bid")
ErrOldSlot = errors.New("requested slot is old")
ErrBadHeader = errors.New("invalid block header from datastore")
)

type Datastore interface {
CheckSlotDelivered(context.Context, uint64) (bool, error)
PutDelivered(context.Context, structs.Slot, structs.DeliveredTrace, time.Duration) error
Expand Down Expand Up @@ -169,7 +180,7 @@ func (rs *Relay) GetHeader(ctx context.Context, m *structs.MetricGroup, request
signature, err := types.SignMessage(&bid, rs.config.BuilderSigningDomain, rs.config.SecretKey)
m.AppendSince(tSignature, "getHeader", "signature")
if err != nil {
return nil, fmt.Errorf("internal server error")
return nil, ErrInternal
}

logger.With(log.F{
Expand All @@ -192,12 +203,12 @@ func (rs *Relay) GetPayload(ctx context.Context, m *structs.MetricGroup, payload
defer m.AppendSince(tStart, "getPayload", "all")

if len(payloadRequest.Signature) != 96 {
return nil, fmt.Errorf("invalid signature")
return nil, ErrInvalidSignature
}

proposerPubkey, err := rs.beaconState.Beacon().KnownValidatorByIndex(payloadRequest.Message.ProposerIndex)
if err != nil && errors.Is(err, structs.ErrUnknownValue) {
return nil, fmt.Errorf("unknown validator for index %d", payloadRequest.Message.ProposerIndex)
if err != nil && errors.Is(err, ErrUnknownValue) {
return nil, fmt.Errorf("%w for index %d", ErrUnknownValidator, payloadRequest.Message.ProposerIndex)
} else if err != nil {
return nil, err
}
Expand All @@ -218,11 +229,11 @@ func (rs *Relay) GetPayload(ctx context.Context, m *structs.MetricGroup, payload

msg, err := types.ComputeSigningRoot(payloadRequest.Message, rs.config.ProposerSigningDomain)
if err != nil {
return nil, fmt.Errorf("signature invalid") // err
return nil, ErrInvalidSignature // err
}
ok, err := verify.VerifySignatureBytes(msg, payloadRequest.Signature[:], pk[:])
if err != nil || !ok {
return nil, fmt.Errorf("signature invalid")
return nil, ErrInvalidSignature
}
m.AppendSince(tVerify, "getPayload", "verify")

Expand Down Expand Up @@ -344,15 +355,15 @@ func (rs *Relay) SubmitBlock(ctx context.Context, m *structs.MetricGroup, submit

_, err := rs.verifyBlock(submitBlockRequest, rs.beaconState.Beacon())
if err != nil {
return fmt.Errorf("verify block: %w", err)
return fmt.Errorf("%w: %s", ErrVerification, err.Error()) // TODO: multiple err wrapping in Go 1.20
}

tCheckDelivered := time.Now()
slot := structs.Slot(submitBlockRequest.Message.Slot)
ok, err := rs.d.CheckSlotDelivered(ctx, uint64(slot))
m.AppendSince(tCheckDelivered, "submitBlock", "checkDelivered")
if ok {
return structs.ErrPayloadAlreadyDelivered
return ErrPayloadAlreadyDelivered
}
if err != nil {
return err
Expand All @@ -361,14 +372,14 @@ func (rs *Relay) SubmitBlock(ctx context.Context, m *structs.MetricGroup, submit
tVerify := time.Now()
msg, err := types.ComputeSigningRoot(submitBlockRequest.Message, rs.config.BuilderSigningDomain)
if err != nil {
return fmt.Errorf("signature invalid")
return ErrInvalidSignature
}

err = rs.ver.Enqueue(ctx, submitBlockRequest.Signature, submitBlockRequest.Message.BuilderPubkey, msg)
m.AppendSince(tVerify, "submitBlock", "verify")

if err != nil {
return fmt.Errorf("verify block: %w", err)
return fmt.Errorf("%w: %s", ErrVerification, err.Error()) // TODO: multiple err wrapping in Go 1.20
}

complete, err := rs.prepareContents(submitBlockRequest)
Expand All @@ -378,12 +389,12 @@ func (rs *Relay) SubmitBlock(ctx context.Context, m *structs.MetricGroup, submit

b, err := json.Marshal(complete.Header)
if err != nil {
return fmt.Errorf("fail to marshal block as header: %w", err)
return fmt.Errorf("%w block as header: %s", ErrMarshal, err.Error()) // TODO: multiple err wrapping in Go 1.20
}

tPutPayload := time.Now()
if err := rs.d.PutPayload(ctx, SubmissionToKey(submitBlockRequest), &complete.Payload, rs.config.TTL); err != nil {
return fmt.Errorf("fail to store block as payload: %w", err)
return fmt.Errorf("%w block as payload: %s", ErrStore, err.Error()) // TODO: multiple err wrapping in Go 1.20
}
m.AppendSince(tPutPayload, "submitBlock", "putPayload")

Expand All @@ -398,7 +409,7 @@ func (rs *Relay) SubmitBlock(ctx context.Context, m *structs.MetricGroup, submit
HeaderAndTrace: complete.Header,
}, rs.config.TTL)
if err != nil {
return fmt.Errorf("fail to store block as header: %w", err)
return fmt.Errorf("%w block as header: %s", ErrStore, err.Error()) // TODO: multiple err wrapping in Go 1.20
}
m.AppendSince(tPutHeader, "submitBlock", "putHeader")

Expand Down Expand Up @@ -457,16 +468,16 @@ func (rs *Relay) prepareContents(submitBlockRequest *types.BuilderSubmitBlockReq

func (rs *Relay) verifyBlock(submitBlockRequest *types.BuilderSubmitBlockRequest, beaconState *structs.BeaconState) (bool, error) { // TODO(l): remove FB type
if submitBlockRequest == nil || submitBlockRequest.Message == nil {
return false, fmt.Errorf("block empty")
return false, ErrEmptyBlock
}

expectedTimestamp := beaconState.GenesisTime + (submitBlockRequest.Message.Slot * 12)
if submitBlockRequest.ExecutionPayload.Timestamp != expectedTimestamp {
return false, fmt.Errorf("builder submission with wrong timestamp. got %d, expected %d", submitBlockRequest.ExecutionPayload.Timestamp, expectedTimestamp)
return false, fmt.Errorf("%w: got %d, expected %d", ErrInvalidTimestamp, submitBlockRequest.ExecutionPayload.Timestamp, expectedTimestamp)
}

if structs.Slot(submitBlockRequest.Message.Slot) < beaconState.CurrentSlot {
return false, fmt.Errorf("builder submission with wrong slot. got %d, expected %d", submitBlockRequest.Message.Slot, beaconState.CurrentSlot)
return false, fmt.Errorf("%w: got %d, expected %d", ErrInvalidSlot, submitBlockRequest.Message.Slot, beaconState.CurrentSlot)
}

return true, nil
Expand Down
8 changes: 0 additions & 8 deletions pkg/structs/errors.go

This file was deleted.

5 changes: 4 additions & 1 deletion pkg/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package structs

import (
"encoding/json"
"errors"
"fmt"

"github.com/flashbots/go-boost-utils/types"
ds "github.com/ipfs/go-datastore"
)

var ErrUnknownValue = errors.New("value is unknown")

type Slot uint64

func (s Slot) Loggable() map[string]any {
Expand Down Expand Up @@ -255,4 +258,4 @@ func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *types.Signe
},
},
}
}
}
12 changes: 9 additions & 3 deletions pkg/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import (
"github.com/lthibault/log"
)

var (
ErrInvalidSignature = errors.New("invalid signature")
ErrUnknownValidator = errors.New("unknown validator")
ErrInvalidTimestamp = errors.New("invalid timestamp")
)

type State interface {
Beacon() *structs.BeaconState
}
Expand Down Expand Up @@ -107,7 +113,7 @@ SendPayloads:

msg, err := types.ComputeSigningRoot(payload[i].Message, rs.builderSigningDomain)
if err != nil {
response.Close(i, errors.New("invalid signature"))
response.Close(i, ErrInvalidSignature)
break SendPayloads
}

Expand Down Expand Up @@ -161,13 +167,13 @@ SendPayloads:

func verifyOther(beacon *structs.BeaconState, tsReg RegistrationManager, i int, sp types.SignedValidatorRegistration) (svresp verify.Resp, ok bool) {
if verifyTimestamp(sp.Message.Timestamp) {
return verify.Resp{Commit: false, ID: i, Err: fmt.Errorf("request too far in future for %s", sp.Message.Pubkey.String())}, false
return verify.Resp{Commit: false, ID: i, Err: fmt.Errorf("%w: too far in future for %s", ErrInvalidTimestamp, sp.Message.Pubkey.String())}, false
}

pk := structs.PubKey{PublicKey: sp.Message.Pubkey}
known, _ := beacon.IsKnownValidator(pk.PubkeyHex())
if !known {
return verify.Resp{Commit: false, ID: i, Err: fmt.Errorf("%s not a known validator", sp.Message.Pubkey.String())}, false
return verify.Resp{Commit: false, ID: i, Err: fmt.Errorf("%w: %s not a known validator", ErrUnknownValidator, sp.Message.Pubkey.String())}, false
}

previousValidatorTimestamp, ok := tsReg.Get(pk.String()) // Do not error on this
Expand Down

0 comments on commit 7464aa8

Please sign in to comment.