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

Disperser auth #984

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ lightnode/docker/args.sh
.vscode

icicle/*

# Just to make sure somebody doesn't accidentally commit the disperser's private key.
eigenda-disperser-private.pem
32 changes: 29 additions & 3 deletions api/clients/node_client_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package clients

import (
"context"
"crypto/ecdsa"
"fmt"
"github.com/Layr-Labs/eigenda/disperser/auth"
"sync"

commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
Expand All @@ -16,6 +18,8 @@ type NodeClientV2Config struct {
Hostname string
Port string
UseSecureGrpcFlag bool
// The .pem file containing the private key used to sign StoreChunks() requests. If "" then no signing is done.
PrivateKeyFile string
}

type NodeClientV2 interface {
Expand All @@ -27,6 +31,7 @@ type nodeClientV2 struct {
config *NodeClientV2Config
initOnce sync.Once
conn *grpc.ClientConn
key *ecdsa.PrivateKey

dispersalClient nodegrpc.DispersalClient
}
Expand All @@ -37,8 +42,19 @@ func NewNodeClientV2(config *NodeClientV2Config) (*nodeClientV2, error) {
if config == nil || config.Hostname == "" || config.Port == "" {
return nil, fmt.Errorf("invalid config: %v", config)
}

var key *ecdsa.PrivateKey // TODO update flags
if config.PrivateKeyFile != "" {
var err error
key, err = auth.ReadPrivateECDSAKeyFile(config.PrivateKeyFile)
if err != nil {
return nil, fmt.Errorf("failed to read private key file: %v", err)
}
}

return &nodeClientV2{
config: config,
key: key,
}, nil
}

Expand All @@ -60,16 +76,26 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c
}
}

// Call the gRPC method to store chunks
response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{
request := &nodegrpc.StoreChunksRequest{
Batch: &commonpb.Batch{
Header: &commonpb.BatchHeader{
BatchRoot: batch.BatchHeader.BatchRoot[:],
ReferenceBlockNumber: batch.BatchHeader.ReferenceBlockNumber,
},
BlobCertificates: blobCerts,
},
})
}

if c.key != nil {
signature, err := auth.SignStoreChunksRequest(c.key, request) // TODO
if err != nil {
return nil, fmt.Errorf("failed to sign request: %v", err)
}
request.Signature = signature
}

// Call the gRPC method to store chunks
response, err := c.dispersalClient.StoreChunks(ctx, request)
if err != nil {
return nil, err
}
Expand Down
41 changes: 40 additions & 1 deletion api/docs/eigenda-protos.html
Original file line number Diff line number Diff line change
Expand Up @@ -3479,7 +3479,7 @@ <h3 id="node.v2.StoreChunksReply">StoreChunksReply</h3>


<h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<p></p>
<p>Request that the Node store a batch of chunks.</p>


<table class="field-table">
Expand All @@ -3495,6 +3495,45 @@ <h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<td><p>batch of blobs to store </p></td>
</tr>

<tr>
<td>disperserID</td>
<td><a href="#uint32">uint32</a></td>
<td></td>
<td><p>ID of the disperser that is requesting the storage of the batch. </p></td>
</tr>

<tr>
<td>signature</td>
<td><a href="#bytes">bytes</a></td>
<td></td>
<td><p>Signature using the disperser&#39;s ECDSA key over keccak hash of the batch. The purpose of this signature
is to prevent hooligans from tricking DA nodes into storing data that they shouldn&#39;t be storing.

Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
A reference implementation (golang) can be found at
https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go

1. digest batch.BatchHeader.BatchRoot
2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
3. for each certificate in batch.BlobCertificates:
a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
i. digest quorum_number (4 bytes, unsigned big endian)
c. digest certificate.BlobHeader.Commitment.Commitment
d. digest certificate.BlobHeader.Commitment.LengthCommitment
e. digest certificate.BlobHeader.Commitment.LengthProof
f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
g. digest certificate.BlobHeader.PaymentHeader.AccountId
h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian)
i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
j. digest certificate.BlobHeader.Signature
k. for each relay in certificate.Relays:
i. digest relay (4 bytes, unsigned big endian)
4. digest disperserID (4 bytes, unsigned big endian)

Note that this signature is not included in the hash for obvious reasons. </p></td>
</tr>

</tbody>
</table>

Expand Down
10 changes: 9 additions & 1 deletion api/docs/eigenda-protos.md
Original file line number Diff line number Diff line change
Expand Up @@ -1476,12 +1476,20 @@ Node info request
<a name="node-v2-StoreChunksRequest"></a>

### StoreChunksRequest

Request that the Node store a batch of chunks.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store |
| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. |
| signature | [bytes](#bytes) | | Signature using the disperser&#39;s ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn&#39;t be storing.

Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go

1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian)

Note that this signature is not included in the hash for obvious reasons. |



Expand Down
41 changes: 40 additions & 1 deletion api/docs/node_v2.html
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ <h3 id="node.v2.StoreChunksReply">StoreChunksReply</h3>


<h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<p></p>
<p>Request that the Node store a batch of chunks.</p>


<table class="field-table">
Expand All @@ -385,6 +385,45 @@ <h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<td><p>batch of blobs to store </p></td>
</tr>

<tr>
<td>disperserID</td>
<td><a href="#uint32">uint32</a></td>
<td></td>
<td><p>ID of the disperser that is requesting the storage of the batch. </p></td>
</tr>

<tr>
<td>signature</td>
<td><a href="#bytes">bytes</a></td>
<td></td>
<td><p>Signature using the disperser&#39;s ECDSA key over keccak hash of the batch. The purpose of this signature
is to prevent hooligans from tricking DA nodes into storing data that they shouldn&#39;t be storing.

Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
A reference implementation (golang) can be found at
https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go

1. digest batch.BatchHeader.BatchRoot
2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
3. for each certificate in batch.BlobCertificates:
a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
i. digest quorum_number (4 bytes, unsigned big endian)
c. digest certificate.BlobHeader.Commitment.Commitment
d. digest certificate.BlobHeader.Commitment.LengthCommitment
e. digest certificate.BlobHeader.Commitment.LengthProof
f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
g. digest certificate.BlobHeader.PaymentHeader.AccountId
h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian)
i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
j. digest certificate.BlobHeader.Signature
k. for each relay in certificate.Relays:
i. digest relay (4 bytes, unsigned big endian)
4. digest disperserID (4 bytes, unsigned big endian)

Note that this signature is not included in the hash for obvious reasons. </p></td>
</tr>

</tbody>
</table>

Expand Down
10 changes: 9 additions & 1 deletion api/docs/node_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,20 @@ Node info request
<a name="node-v2-StoreChunksRequest"></a>

### StoreChunksRequest

Request that the Node store a batch of chunks.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store |
| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. |
| signature | [bytes](#bytes) | | Signature using the disperser&#39;s ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn&#39;t be storing.

Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go

1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian)

Note that this signature is not included in the hash for obvious reasons. |



Expand Down
52 changes: 50 additions & 2 deletions api/grpc/node/v2/node_v2.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions api/proto/node/v2/node_v2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,41 @@ service Retrieval {

// Requests and replies

// Request that the Node store a batch of chunks.
message StoreChunksRequest {
// batch of blobs to store
common.v2.Batch batch = 1;

// ID of the disperser that is requesting the storage of the batch.
uint32 disperserID = 2;

// Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature
// is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
//
// Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
// A reference implementation (golang) can be found at
// https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
//
// 1. digest batch.BatchHeader.BatchRoot
// 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
// 3. for each certificate in batch.BlobCertificates:
// a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
// b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
// i. digest quorum_number (4 bytes, unsigned big endian)
// c. digest certificate.BlobHeader.Commitment.Commitment
// d. digest certificate.BlobHeader.Commitment.LengthCommitment
// e. digest certificate.BlobHeader.Commitment.LengthProof
// f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
// g. digest certificate.BlobHeader.PaymentHeader.AccountId
// h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian)
// i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
// j. digest certificate.BlobHeader.Signature
// k. for each relay in certificate.Relays:
// i. digest relay (4 bytes, unsigned big endian)
// 4. digest disperserID (4 bytes, unsigned big endian)
//
// Note that this signature is not included in the hash for obvious reasons.
bytes signature = 3;
}

message StoreChunksReply {
Expand Down
3 changes: 3 additions & 0 deletions core/chainio.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ type Reader interface {

// GetRelayURLs returns the relay URL addresses for all relays.
GetRelayURLs(ctx context.Context) (map[uint32]string, error)

// GetDisperserAddress returns the disperser address with the given ID.
GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error)
}

type Writer interface {
Expand Down
7 changes: 7 additions & 0 deletions core/eth/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -748,3 +748,10 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) {

return res, nil
}

func (t *Reader) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) {
// TODO(cody-littley/arch): this is just a place holder until we register dispersers on chain
bytes := make([]byte, gethcommon.AddressLength)
address := gethcommon.BytesToAddress(bytes)
return address, nil
}
11 changes: 11 additions & 0 deletions core/mock/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,14 @@ func (t *MockWriter) GetRelayURLs(ctx context.Context) (map[uint32]string, error

return result.(map[uint32]string), args.Error(1)
}

func (t *MockWriter) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) {
args := t.Called()
result := args.Get(0)
if result == nil {
var zeroValue gethcommon.Address
return zeroValue, args.Error(1)
}

return result.(gethcommon.Address), args.Error(1)
}
Loading
Loading