Skip to content

Commit

Permalink
Add deneb signature checking for block contents
Browse files Browse the repository at this point in the history
  • Loading branch information
avalonche committed Aug 2, 2023
1 parent 6a991e8 commit af69cf5
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 8 deletions.
50 changes: 43 additions & 7 deletions services/api/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
Expand All @@ -22,6 +21,7 @@ import (

"github.com/NYTimes/gziphandler"
apiv1 "github.com/attestantio/go-builder-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/buger/jsonparser"
"github.com/flashbots/go-boost-utils/bls"
Expand All @@ -36,6 +36,7 @@ import (
"github.com/go-redis/redis/v9"
"github.com/gorilla/mux"
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
uberatomic "go.uber.org/atomic"
"golang.org/x/exp/slices"
Expand Down Expand Up @@ -1184,6 +1185,42 @@ func (api *RelayAPI) handleGetHeader(w http.ResponseWriter, req *http.Request) {
api.RespondOK(w, bid)
}

func (api *RelayAPI) checkProposerSignature(block *common.VersionedSignedBlindedBlockRequest, pubKey []byte) (bool, error) {
switch block.Version {
case spec.DataVersionCapella:
return verifyBlockSignature(block, api.opts.EthNetDetails.DomainBeaconProposerCapella, pubKey)
case spec.DataVersionDeneb:
domain := api.opts.EthNetDetails.DomainBeaconProposerDeneb
if ok, err := verifyBlockSignature(block, domain, pubKey); !ok || err != nil {
return false, errors.Wrap(err, "failed to verify block signature for deneb")
}
// verify sidecar signatures
for i, sidecar := range block.Deneb.SignedBlindedBlobSidecars {
if sidecar == nil || sidecar.Message == nil {
return false, errors.New("nil sidecar or message")
}
root, err := sidecar.Message.HashTreeRoot()
if err != nil {
return false, errors.Wrap(err, fmt.Sprintf("failed to calculate hash tree root for sidecar index %d", i))
}
signingData := phase0.SigningData{ObjectRoot: root, Domain: domain}
msg, err := signingData.HashTreeRoot()
if err != nil {
return false, err
}

if ok, err := bls.VerifySignatureBytes(msg[:], sidecar.Signature[:], pubKey[:]); !ok || err != nil {
return false, errors.Wrap(err, fmt.Sprintf("failed to verify signature for sidecar index %d ", i))
}
}
case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix:
fallthrough
default:
return false, errors.New("unsupported consensus data version")
}
return true, nil
}

func (api *RelayAPI) handleGetPayload(w http.ResponseWriter, req *http.Request) {
api.getPayloadCallsInFlight.Add(1)
defer api.getPayloadCallsInFlight.Done()
Expand Down Expand Up @@ -1228,8 +1265,8 @@ func (api *RelayAPI) handleGetPayload(w http.ResponseWriter, req *http.Request)
// Decode payload
payload := new(common.VersionedSignedBlindedBlockRequest)
if err := json.NewDecoder(bytes.NewReader(body)).Decode(payload); err != nil {
log.WithError(err).Warn("failed to decode capella getPayload request")
api.RespondError(w, http.StatusBadRequest, "failed to decode capella payload")
log.WithError(err).Warn("failed to decode getPayload request")
api.RespondError(w, http.StatusBadRequest, "failed to decode payload")
return
}

Expand Down Expand Up @@ -1297,14 +1334,13 @@ func (api *RelayAPI) handleGetPayload(w http.ResponseWriter, req *http.Request)
}

// Validate proposer signature
// TODO: add deneb support.
ok, err := checkProposerSignature(payload, api.opts.EthNetDetails.DomainBeaconProposerCapella, pk[:])
ok, err := api.checkProposerSignature(payload, pk[:])
if !ok || err != nil {
if api.ffLogInvalidSignaturePayload {
txt, _ := json.Marshal(payload) //nolint:errchkjson
log.Info("payload_invalid_sig_capella: ", string(txt), "pubkey:", proposerPubkey.String())
log.Info("payload_invalid_sig: ", string(txt), "pubkey:", proposerPubkey.String())
}
log.WithError(err).Warn("could not verify capella payload signature")
log.WithError(err).Warn("could not verify payload signature")
api.RespondError(w, http.StatusBadRequest, "could not verify payload signature")
return
}
Expand Down
54 changes: 54 additions & 0 deletions services/api/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,60 @@ func TestCheckSubmissionSlotDetails(t *testing.T) {
}
}

func TestCheckProposerSignature(t *testing.T) {
t.Run("Unsupported version", func(t *testing.T) {
_, _, backend := startTestBackend(t)
payload := new(common.VersionedSignedBlindedBlockRequest)
payload.Version = consensusspec.DataVersionBellatrix
ok, err := backend.relay.checkProposerSignature(payload, []byte{})
require.Error(t, err, "unsupported consensus data version")
require.False(t, ok)
})

t.Run("Valid Capella Signature", func(t *testing.T) {
jsonBytes := common.LoadGzippedBytes(t, "../../testdata/signedBlindedBeaconBlock_Goerli.json.gz")
payload := new(common.VersionedSignedBlindedBlockRequest)
err := json.Unmarshal(jsonBytes, payload)
require.NoError(t, err)
// start backend with goerli network
_, _, backend := startTestBackend(t)
goerli, err := common.NewEthNetworkDetails(common.EthNetworkGoerli)
require.NoError(t, err)
backend.relay.opts.EthNetDetails = *goerli
// check signature
pubkey, err := utils.HexToPubkey("0xa8afcb5313602f936864b30600f568e04069e596ceed9b55e2a1c872c959ddcb90589636469c15d97e7565344d9ed4ad")
require.NoError(t, err)
ok, err := backend.relay.checkProposerSignature(payload, pubkey[:])
require.NoError(t, err)
require.True(t, ok)
})

t.Run("Invalid Capella Signature", func(t *testing.T) {
jsonBytes := common.LoadGzippedBytes(t, "../../testdata/signedBlindedBeaconBlock_Goerli.json.gz")
payload := new(common.VersionedSignedBlindedBlockRequest)
err := json.Unmarshal(jsonBytes, payload)
require.NoError(t, err)
// change signature
signature, err := utils.HexToSignature(
"0x942d85822e86a182b0a535361b379015a03e5ce4416863d3baa46b42eef06f070462742b79fbc77c0802699ba6d2ab00" +
"11740dad6bfcf05b1f15c5a11687ae2aa6a08c03ad1ff749d7a48e953d13b5d7c2bd1da4cfcf30ba6d918b587d6525f0",
)
require.NoError(t, err)
payload.Capella.Signature = signature
// start backend with goerli network
_, _, backend := startTestBackend(t)
goerli, err := common.NewEthNetworkDetails(common.EthNetworkGoerli)
require.NoError(t, err)
backend.relay.opts.EthNetDetails = *goerli
// check signature
pubkey, err := utils.HexToPubkey("0xa8afcb5313602f936864b30600f568e04069e596ceed9b55e2a1c872c959ddcb90589636469c15d97e7565344d9ed4ad")
require.NoError(t, err)
ok, err := backend.relay.checkProposerSignature(payload, pubkey[:])
require.NoError(t, err)
require.False(t, ok)
})
}

func gzipBytes(t *testing.T, b []byte) []byte {
t.Helper()
var buf bytes.Buffer
Expand Down
2 changes: 1 addition & 1 deletion services/api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func hasReachedFork(slot, forkEpoch uint64) bool {
return currentEpoch >= forkEpoch
}

func checkProposerSignature(block *common.VersionedSignedBlindedBlockRequest, domain phase0.Domain, pubKey []byte) (bool, error) {
func verifyBlockSignature(block *common.VersionedSignedBlindedBlockRequest, domain phase0.Domain, pubKey []byte) (bool, error) {
root, err := block.Root()
if err != nil {
return false, err
Expand Down

0 comments on commit af69cf5

Please sign in to comment.