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

Sign get chunks #1022

Merged
merged 10 commits into from
Dec 19, 2024
66 changes: 59 additions & 7 deletions api/clients/v2/relay_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package clients

import (
"context"
"errors"
"fmt"
"github.com/Layr-Labs/eigenda/core"
"github.com/Layr-Labs/eigenda/relay/auth"
"sync"

relaygrpc "github.com/Layr-Labs/eigenda/api/grpc/relay"
Expand All @@ -12,9 +15,14 @@ import (
"google.golang.org/grpc"
)

// MessageSigner is a function that signs a message with a private BLS key.
type MessageSigner func(ctx context.Context, data [32]byte) (*core.Signature, error)

type RelayClientConfig struct {
Sockets map[corev2.RelayKey]string
UseSecureGrpcFlag bool
OperatorID *core.OperatorID
MessageSigner MessageSigner
}

type ChunkRequestByRange struct {
Expand Down Expand Up @@ -62,7 +70,7 @@ var _ RelayClient = (*relayClient)(nil)

// NewRelayClient creates a new RelayClient that connects to the relays specified in the config.
// It keeps a connection to each relay and reuses it for subsequent requests, and the connection is lazily instantiated.
func NewRelayClient(config *RelayClientConfig, logger logging.Logger) (*relayClient, error) {
func NewRelayClient(config *RelayClientConfig, logger logging.Logger) (RelayClient, error) {
if config == nil || len(config.Sockets) <= 0 {
return nil, fmt.Errorf("invalid config: %v", config)
}
Expand Down Expand Up @@ -97,7 +105,32 @@ func (c *relayClient) GetBlob(ctx context.Context, relayKey corev2.RelayKey, blo
return res.GetBlob(), nil
}

func (c *relayClient) GetChunksByRange(ctx context.Context, relayKey corev2.RelayKey, requests []*ChunkRequestByRange) ([][]byte, error) {
// signGetChunksRequest signs the GetChunksRequest with the operator's private key
// and sets the signature in the request.
func (c *relayClient) signGetChunksRequest(ctx context.Context, request *relaygrpc.GetChunksRequest) error {
if c.config.OperatorID == nil {
return errors.New("no operator ID provided in config, cannot sign get chunks request")
}
if c.config.MessageSigner == nil {
return errors.New("no message signer provided in config, cannot sign get chunks request")
}

hash := auth.HashGetChunksRequest(request)
Copy link
Contributor

Choose a reason for hiding this comment

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

To confirm, all the values of the request are being hashed right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, as long as I didn't make a mistake when coding the hashing function. Makes me nervous writing these types of things by hand due to the possibility of human error.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems good area to focus for auditors

hashArray := [32]byte{}
copy(hashArray[:], hash)
signature, err := c.config.MessageSigner(ctx, hashArray)
if err != nil {
return fmt.Errorf("failed to sign get chunks request: %v", err)
}
request.OperatorSignature = signature.Serialize()
return nil
}

func (c *relayClient) GetChunksByRange(
ctx context.Context,
relayKey corev2.RelayKey,
requests []*ChunkRequestByRange) ([][]byte, error) {

if len(requests) == 0 {
return nil, fmt.Errorf("no requests")
}
Expand All @@ -118,18 +151,29 @@ func (c *relayClient) GetChunksByRange(ctx context.Context, relayKey corev2.Rela
},
}
}
res, err := client.GetChunks(ctx, &relaygrpc.GetChunksRequest{

request := &relaygrpc.GetChunksRequest{
ChunkRequests: grpcRequests,
})
OperatorId: c.config.OperatorID[:],
}
err = c.signGetChunksRequest(ctx, request)
if err != nil {
return nil, err
}

res, err := client.GetChunks(ctx, request)
if err != nil {
return nil, err
}

return res.GetData(), nil
}

func (c *relayClient) GetChunksByIndex(ctx context.Context, relayKey corev2.RelayKey, requests []*ChunkRequestByIndex) ([][]byte, error) {
func (c *relayClient) GetChunksByIndex(
ctx context.Context,
relayKey corev2.RelayKey,
requests []*ChunkRequestByIndex) ([][]byte, error) {

if len(requests) == 0 {
return nil, fmt.Errorf("no requests")
}
Expand All @@ -150,9 +194,17 @@ func (c *relayClient) GetChunksByIndex(ctx context.Context, relayKey corev2.Rela
},
}
}
res, err := client.GetChunks(ctx, &relaygrpc.GetChunksRequest{

request := &relaygrpc.GetChunksRequest{
ChunkRequests: grpcRequests,
})
OperatorId: c.config.OperatorID[:],
}
err = c.signGetChunksRequest(ctx, request)
if err != nil {
return nil, err
}

res, err := client.GetChunks(ctx, request)

if err != nil {
return nil, err
Expand Down
4 changes: 4 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ func NewNode(
relayClient, err = clients.NewRelayClient(&clients.RelayClientConfig{
Sockets: relayURLs,
UseSecureGrpcFlag: config.UseSecureGrpc,
OperatorID: &config.ID,
MessageSigner: n.SignMessage,
}, logger)

if err != nil {
Expand Down Expand Up @@ -460,6 +462,8 @@ func (n *Node) RefreshOnchainState(ctx context.Context) error {
relayClient, err := clients.NewRelayClient(&clients.RelayClientConfig{
Sockets: relayURLs,
UseSecureGrpcFlag: n.Config.UseSecureGrpc,
OperatorID: &n.Config.ID,
MessageSigner: n.SignMessage,
}, n.Logger)
if err != nil {
n.Logger.Error("error creating relay client", "err", err)
Expand Down
9 changes: 8 additions & 1 deletion node/node_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,15 @@ func TestRefreshOnchainStateSuccess(t *testing.T) {
relayURLs := map[v2.RelayKey]string{
0: "http://localhost:8080",
}

messageSigner := func(ctx context.Context, data [32]byte) (*core.Signature, error) {
return nil, nil
}

relayClient, err := clients.NewRelayClient(&clients.RelayClientConfig{
Sockets: relayURLs,
Sockets: relayURLs,
OperatorID: &c.node.Config.ID,
MessageSigner: messageSigner,
}, c.node.Logger)
require.NoError(t, err)
// set up non-mock client
Expand Down
68 changes: 68 additions & 0 deletions relay/mock/ics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package mock
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need a separate mock for relay?
don't we have an existing mock for IndexedChainState?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The existing ChainDataMock struct has a bunch of internal logic that looks like it was written to support specific unit tests. I just need a mock implementation where I can use the pattern when X method is called with Y arguments, return Z.


import (
"context"
"github.com/Layr-Labs/eigenda/core"
"github.com/stretchr/testify/mock"
)

var _ core.IndexedChainState = (*IndexedChainState)(nil)

// IndexedChainState is a mock implementation of core.IndexedChainState.
type IndexedChainState struct {
Mock mock.Mock
}

func (m *IndexedChainState) GetCurrentBlockNumber() (uint, error) {
args := m.Mock.Called()
return args.Get(0).(uint), args.Error(1)
}

func (m *IndexedChainState) GetOperatorState(
ctx context.Context,
blockNumber uint,
quorums []core.QuorumID) (*core.OperatorState, error) {

args := m.Mock.Called(blockNumber, quorums)
return args.Get(0).(*core.OperatorState), args.Error(1)
}

func (m *IndexedChainState) GetOperatorStateByOperator(
ctx context.Context,
blockNumber uint,
operator core.OperatorID) (*core.OperatorState, error) {

args := m.Mock.Called(blockNumber, operator)
return args.Get(0).(*core.OperatorState), args.Error(1)
}

func (m *IndexedChainState) GetOperatorSocket(
ctx context.Context,
blockNumber uint,
operator core.OperatorID) (string, error) {

args := m.Mock.Called(blockNumber, operator)
return args.Get(0).(string), args.Error(1)
}

func (m *IndexedChainState) GetIndexedOperatorState(
ctx context.Context,
blockNumber uint,
quorums []core.QuorumID) (*core.IndexedOperatorState, error) {

args := m.Mock.Called(blockNumber, quorums)
return args.Get(0).(*core.IndexedOperatorState), args.Error(1)
}

func (m *IndexedChainState) GetIndexedOperators(
ctx context.Context,
blockNumber uint) (map[core.OperatorID]*core.IndexedOperatorInfo, error) {

args := m.Mock.Called(blockNumber)
return args.Get(0).(map[core.OperatorID]*core.IndexedOperatorInfo), args.Error(1)
}

func (m *IndexedChainState) Start(context context.Context) error {
args := m.Mock.Called()
return args.Error(0)
}
Loading
Loading