Skip to content

Commit

Permalink
Unexport payment signer (#847)
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-shim authored Oct 31, 2024
1 parent e2ead56 commit ebb9bf3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 31 deletions.
43 changes: 24 additions & 19 deletions core/auth/payment_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,46 @@ package auth
import (
"crypto/ecdsa"
"fmt"
"log"

commonpb "github.com/Layr-Labs/eigenda/api/grpc/common"
"github.com/Layr-Labs/eigenda/core"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

type PaymentSigner struct {
type paymentSigner struct {
PrivateKey *ecdsa.PrivateKey
}

var _ core.PaymentSigner = &PaymentSigner{}

func NewPaymentSigner(privateKeyHex string) *PaymentSigner {
var _ core.PaymentSigner = &paymentSigner{}

func NewPaymentSigner(privateKeyHex string) (*paymentSigner, error) {
if len(privateKeyHex) == 0 {
return nil, fmt.Errorf("private key cannot be empty")
}
privateKeyBytes := common.FromHex(privateKeyHex)
privateKey, err := crypto.ToECDSA(privateKeyBytes)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
return nil, fmt.Errorf("failed to convert hex to ECDSA private key: %w", err)
}

return &PaymentSigner{
return &paymentSigner{
PrivateKey: privateKey,
}
}, nil
}

// SignBlobPayment signs the payment header and returns the signature
func (s *PaymentSigner) SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) {
func (s *paymentSigner) SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) {
header.AccountId = s.GetAccountID()
pm := core.ConvertPaymentHeader(header)
hash, err := pm.Hash()
if err != nil {
return nil, fmt.Errorf("failed to hash payment header: %v", err)
return nil, fmt.Errorf("failed to hash payment header: %w", err)
}

sig, err := crypto.Sign(hash[:], s.PrivateKey)
if err != nil {
return nil, fmt.Errorf("failed to sign hash: %v", err)
return nil, fmt.Errorf("failed to sign hash: %w", err)
}

return sig, nil
Expand All @@ -62,35 +63,39 @@ func (s *NoopPaymentSigner) GetAccountID() string {
}

// VerifyPaymentSignature verifies the signature against the payment metadata
func VerifyPaymentSignature(paymentHeader *commonpb.PaymentHeader, paymentSignature []byte) bool {
func VerifyPaymentSignature(paymentHeader *commonpb.PaymentHeader, paymentSignature []byte) error {
pm := core.ConvertPaymentHeader(paymentHeader)
hash, err := pm.Hash()
if err != nil {
return false
return fmt.Errorf("failed to hash payment header: %w", err)
}

recoveredPubKey, err := crypto.SigToPub(hash[:], paymentSignature)
if err != nil {
log.Printf("Failed to recover public key from signature: %v\n", err)
return false
return fmt.Errorf("failed to recover public key from signature: %w", err)
}

recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey)
accountId := common.HexToAddress(paymentHeader.AccountId)
if recoveredAddress != accountId {
log.Printf("Signature address %s does not match account id %s\n", recoveredAddress.Hex(), accountId.Hex())
return false
return fmt.Errorf("signature address %s does not match account id %s", recoveredAddress.Hex(), accountId.Hex())
}

return crypto.VerifySignature(
ok := crypto.VerifySignature(
crypto.FromECDSAPub(recoveredPubKey),
hash[:],
paymentSignature[:len(paymentSignature)-1], // Remove recovery ID
)

if !ok {
return fmt.Errorf("invalid signature")
}

return nil
}

// GetAccountID returns the Ethereum address of the signer
func (s *PaymentSigner) GetAccountID() string {
func (s *paymentSigner) GetAccountID() string {
publicKey := crypto.FromECDSAPub(&s.PrivateKey.PublicKey)
hash := crypto.Keccak256(publicKey[1:])

Expand Down
15 changes: 8 additions & 7 deletions core/auth/payment_signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ func TestPaymentSigner(t *testing.T) {
require.NoError(t, err)

privateKeyHex := hex.EncodeToString(crypto.FromECDSA(privateKey))
signer := auth.NewPaymentSigner(privateKeyHex)
signer, err := auth.NewPaymentSigner(privateKeyHex)
require.NoError(t, err)

t.Run("SignBlobPayment", func(t *testing.T) {
header := &commonpb.PaymentHeader{
Expand All @@ -30,8 +31,8 @@ func TestPaymentSigner(t *testing.T) {
assert.NotEmpty(t, signature)

// Verify the signature
isValid := auth.VerifyPaymentSignature(header, signature)
assert.True(t, isValid)
err = auth.VerifyPaymentSignature(header, signature)
assert.NoError(t, err)
})

t.Run("VerifyPaymentSignature_InvalidSignature", func(t *testing.T) {
Expand All @@ -43,8 +44,8 @@ func TestPaymentSigner(t *testing.T) {

// Create an invalid signature
invalidSignature := make([]byte, 65)
isValid := auth.VerifyPaymentSignature(header, invalidSignature)
assert.False(t, isValid)
err = auth.VerifyPaymentSignature(header, invalidSignature)
assert.Error(t, err)
})

t.Run("VerifyPaymentSignature_ModifiedHeader", func(t *testing.T) {
Expand All @@ -60,8 +61,8 @@ func TestPaymentSigner(t *testing.T) {
// Modify the header after signing
header.BinIndex = 2

isValid := auth.VerifyPaymentSignature(header, signature)
assert.False(t, isValid)
err = auth.VerifyPaymentSignature(header, signature)
assert.Error(t, err)
})
}

Expand Down
11 changes: 6 additions & 5 deletions disperser/apiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,6 @@ func (s *DispersalServer) DispersePaidBlob(ctx context.Context, req *pb.Disperse
cumulativePayment := new(big.Int).SetBytes(req.PaymentHeader.CumulativePayment)
//todo: before disperse blob, validate the signature
signature := req.PaymentSignature
if !auth.VerifyPaymentSignature(req.GetPaymentHeader(), signature) {
return nil, api.NewErrorInvalidArg("payment signature is invalid")
}
if err != nil {
for _, quorumID := range req.QuorumNumbers {
s.metrics.HandleFailedRequest(codes.InvalidArgument.String(), fmt.Sprint(quorumID), len(req.GetData()), "DispersePaidBlob")
Expand All @@ -340,6 +337,10 @@ func (s *DispersalServer) DispersePaidBlob(ctx context.Context, req *pb.Disperse
return nil, api.NewErrorInvalidArg(err.Error())
}

if err = auth.VerifyPaymentSignature(req.GetPaymentHeader(), signature); err != nil {
return nil, api.NewErrorInvalidArg("payment signature is invalid")
}

paymentHeader := core.PaymentMetadata{
AccountID: blob.RequestHeader.AccountID,
BinIndex: binIndex,
Expand Down Expand Up @@ -1114,8 +1115,8 @@ func (s *DispersalServer) validatePaidRequestAndGetBlob(ctx context.Context, req
seenQuorums := make(map[uint8]struct{})

// TODO: validate payment signature against payment metadata
if !auth.VerifyPaymentSignature(req.GetPaymentHeader(), req.GetPaymentSignature()) {
return nil, fmt.Errorf("payment signature is invalid")
if err = auth.VerifyPaymentSignature(req.GetPaymentHeader(), req.GetPaymentSignature()); err != nil {
return nil, fmt.Errorf("payment signature is invalid: %w", err)
}
// Unlike regular blob dispersal request validation, there's no check with required quorums
// Because Reservation has their specific quorum requirements, and on-demand is only allowed and paid to the required quorums.
Expand Down

0 comments on commit ebb9bf3

Please sign in to comment.