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

da: add client context #28

Merged
merged 1 commit into from
Jan 12, 2024
Merged
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
14 changes: 8 additions & 6 deletions da.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
package da

import "context"

// DA defines very generic interface for interaction with Data Availability layers.
type DA interface {
// MaxBlobSize returns the max blob size
MaxBlobSize() (uint64, error)
MaxBlobSize(ctx context.Context) (uint64, error)

// Get returns Blob for each given ID, or an error.
//
// Error should be returned if ID is not formatted properly, there is no Blob for given ID or any other client-level
// error occurred (dropped connection, timeout, etc).
Get(ids []ID) ([]Blob, error)
Get(ctx context.Context, ids []ID) ([]Blob, error)

// GetIDs returns IDs of all Blobs located in DA at given height.
GetIDs(height uint64) ([]ID, error)
GetIDs(ctx context.Context, height uint64) ([]ID, error)

// Commit creates a Commitment for each given Blob.
Commit(blobs []Blob) ([]Commitment, error)
Commit(ctx context.Context, blobs []Blob) ([]Commitment, error)

// Submit submits the Blobs to Data Availability layer.
//
// This method is synchronous. Upon successful submission to Data Availability layer, it returns ID identifying blob
// in DA and Proof of inclusion.
// If options is nil, default options are used.
Submit(blobs []Blob, gasPrice float64) ([]ID, []Proof, error)
Submit(ctx context.Context, blobs []Blob, gasPrice float64) ([]ID, []Proof, error)

// Validate validates Commitments against the corresponding Proofs. This should be possible without retrieving the Blobs.
Validate(ids []ID, proofs []Proof) ([]bool, error)
Validate(ctx context.Context, ids []ID, proofs []Proof) ([]bool, error)
}

// Blob is the data submitted/received from DA interface.
Expand Down
24 changes: 12 additions & 12 deletions proxy/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,24 @@
}

// MaxBlobSize returns the DA MaxBlobSize
func (c *Client) MaxBlobSize() (uint64, error) {
func (c *Client) MaxBlobSize(ctx context.Context) (uint64, error) {

Check warning on line 41 in proxy/client.go

View check run for this annotation

Codecov / codecov/patch

proxy/client.go#L41

Added line #L41 was not covered by tests
req := &pbda.MaxBlobSizeRequest{}
resp, err := c.client.MaxBlobSize(context.TODO(), req)
resp, err := c.client.MaxBlobSize(ctx, req)

Check warning on line 43 in proxy/client.go

View check run for this annotation

Codecov / codecov/patch

proxy/client.go#L43

Added line #L43 was not covered by tests
if err != nil {
return 0, err
}
return resp.MaxBlobSize, nil
}

// Get returns Blob for each given ID, or an error.
func (c *Client) Get(ids []da.ID) ([]da.Blob, error) {
func (c *Client) Get(ctx context.Context, ids []da.ID) ([]da.Blob, error) {
req := &pbda.GetRequest{
Ids: make([]*pbda.ID, len(ids)),
}
for i := range ids {
req.Ids[i] = &pbda.ID{Value: ids[i]}
}
resp, err := c.client.Get(context.TODO(), req)
resp, err := c.client.Get(ctx, req)
if err != nil {
return nil, err
}
Expand All @@ -64,9 +64,9 @@
}

// GetIDs returns IDs of all Blobs located in DA at given height.
func (c *Client) GetIDs(height uint64) ([]da.ID, error) {
func (c *Client) GetIDs(ctx context.Context, height uint64) ([]da.ID, error) {
req := &pbda.GetIDsRequest{Height: height}
resp, err := c.client.GetIDs(context.TODO(), req)
resp, err := c.client.GetIDs(ctx, req)
if err != nil {
return nil, err
}
Expand All @@ -75,12 +75,12 @@
}

// Commit creates a Commitment for each given Blob.
func (c *Client) Commit(blobs []da.Blob) ([]da.Commitment, error) {
func (c *Client) Commit(ctx context.Context, blobs []da.Blob) ([]da.Commitment, error) {
req := &pbda.CommitRequest{
Blobs: blobsDA2PB(blobs),
}

resp, err := c.client.Commit(context.TODO(), req)
resp, err := c.client.Commit(ctx, req)
if err != nil {
return nil, err
}
Expand All @@ -89,13 +89,13 @@
}

// Submit submits the Blobs to Data Availability layer.
func (c *Client) Submit(blobs []da.Blob, gasPrice float64) ([]da.ID, []da.Proof, error) {
func (c *Client) Submit(ctx context.Context, blobs []da.Blob, gasPrice float64) ([]da.ID, []da.Proof, error) {
req := &pbda.SubmitRequest{
Blobs: blobsDA2PB(blobs),
GasPrice: gasPrice,
}

resp, err := c.client.Submit(context.TODO(), req)
resp, err := c.client.Submit(ctx, req)
if err != nil {
return nil, nil, err
}
Expand All @@ -111,11 +111,11 @@
}

// Validate validates Commitments against the corresponding Proofs. This should be possible without retrieving the Blobs.
func (c *Client) Validate(ids []da.ID, proofs []da.Proof) ([]bool, error) {
func (c *Client) Validate(ctx context.Context, ids []da.ID, proofs []da.Proof) ([]bool, error) {
req := &pbda.ValidateRequest{
Ids: idsDA2PB(ids),
Proofs: proofsDA2PB(proofs),
}
resp, err := c.client.Validate(context.TODO(), req)
resp, err := c.client.Validate(ctx, req)
return resp.Results, err
}
12 changes: 6 additions & 6 deletions proxy/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@
}

func (p *proxySrv) MaxBlobSize(ctx context.Context, request *pbda.MaxBlobSizeRequest) (*pbda.MaxBlobSizeResponse, error) {
maxBlobSize, err := p.target.MaxBlobSize()
maxBlobSize, err := p.target.MaxBlobSize(ctx)

Check warning on line 28 in proxy/server.go

View check run for this annotation

Codecov / codecov/patch

proxy/server.go#L28

Added line #L28 was not covered by tests
return &pbda.MaxBlobSizeResponse{MaxBlobSize: maxBlobSize}, err
}

func (p *proxySrv) Get(ctx context.Context, request *pbda.GetRequest) (*pbda.GetResponse, error) {
ids := idsPB2DA(request.Ids)
blobs, err := p.target.Get(ids)
blobs, err := p.target.Get(ctx, ids)
return &pbda.GetResponse{Blobs: blobsDA2PB(blobs)}, err
}

func (p *proxySrv) GetIDs(ctx context.Context, request *pbda.GetIDsRequest) (*pbda.GetIDsResponse, error) {
ids, err := p.target.GetIDs(request.Height)
ids, err := p.target.GetIDs(ctx, request.Height)
if err != nil {
return nil, err
}
Expand All @@ -46,7 +46,7 @@

func (p *proxySrv) Commit(ctx context.Context, request *pbda.CommitRequest) (*pbda.CommitResponse, error) {
blobs := blobsPB2DA(request.Blobs)
commits, err := p.target.Commit(blobs)
commits, err := p.target.Commit(ctx, blobs)
if err != nil {
return nil, err
}
Expand All @@ -57,7 +57,7 @@
func (p *proxySrv) Submit(ctx context.Context, request *pbda.SubmitRequest) (*pbda.SubmitResponse, error) {
blobs := blobsPB2DA(request.Blobs)

ids, proofs, err := p.target.Submit(blobs, request.GasPrice)
ids, proofs, err := p.target.Submit(ctx, blobs, request.GasPrice)
if err != nil {
return nil, err
}
Expand All @@ -79,7 +79,7 @@
ids := idsPB2DA(request.Ids)
proofs := proofsPB2DA(request.Proofs)
//TODO implement me
validity, err := p.target.Validate(ids, proofs)
validity, err := p.target.Validate(ctx, ids, proofs)
if err != nil {
return nil, err
}
Expand Down
13 changes: 7 additions & 6 deletions test/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package test

import (
"bytes"
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
Expand Down Expand Up @@ -49,12 +50,12 @@ func NewDummyDA(opts ...func(*DummyDA) *DummyDA) *DummyDA {
var _ da.DA = &DummyDA{}

// MaxBlobSize returns the max blob size in bytes.
func (d *DummyDA) MaxBlobSize() (uint64, error) {
func (d *DummyDA) MaxBlobSize(ctx context.Context) (uint64, error) {
return d.maxBlobSize, nil
}

// Get returns Blobs for given IDs.
func (d *DummyDA) Get(ids []da.ID) ([]da.Blob, error) {
func (d *DummyDA) Get(ctx context.Context, ids []da.ID) ([]da.Blob, error) {
d.mu.Lock()
defer d.mu.Unlock()
blobs := make([]da.Blob, len(ids))
Expand All @@ -78,7 +79,7 @@ func (d *DummyDA) Get(ids []da.ID) ([]da.Blob, error) {
}

// GetIDs returns IDs of Blobs at given DA height.
func (d *DummyDA) GetIDs(height uint64) ([]da.ID, error) {
func (d *DummyDA) GetIDs(ctx context.Context, height uint64) ([]da.ID, error) {
d.mu.Lock()
defer d.mu.Unlock()
kvps := d.data[height]
Expand All @@ -90,7 +91,7 @@ func (d *DummyDA) GetIDs(height uint64) ([]da.ID, error) {
}

// Commit returns cryptographic Commitments for given blobs.
func (d *DummyDA) Commit(blobs []da.Blob) ([]da.Commitment, error) {
func (d *DummyDA) Commit(ctx context.Context, blobs []da.Blob) ([]da.Commitment, error) {
commits := make([]da.Commitment, len(blobs))
for i, blob := range blobs {
commits[i] = d.getHash(blob)
Expand All @@ -99,7 +100,7 @@ func (d *DummyDA) Commit(blobs []da.Blob) ([]da.Commitment, error) {
}

// Submit stores blobs in DA layer.
func (d *DummyDA) Submit(blobs []da.Blob, gasPrice float64) ([]da.ID, []da.Proof, error) {
func (d *DummyDA) Submit(ctx context.Context, blobs []da.Blob, gasPrice float64) ([]da.ID, []da.Proof, error) {
d.mu.Lock()
defer d.mu.Unlock()
ids := make([]da.ID, len(blobs))
Expand All @@ -116,7 +117,7 @@ func (d *DummyDA) Submit(blobs []da.Blob, gasPrice float64) ([]da.ID, []da.Proof
}

// Validate checks the Proofs for given IDs.
func (d *DummyDA) Validate(ids []da.ID, proofs []da.Proof) ([]bool, error) {
func (d *DummyDA) Validate(ctx context.Context, ids []da.ID, proofs []da.Proof) ([]bool, error) {
if len(ids) != len(proofs) {
return nil, errors.New("number of IDs doesn't equal to number of proofs")
}
Expand Down
38 changes: 22 additions & 16 deletions test/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package test

import (
"bytes"
"context"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -40,58 +41,59 @@ func BasicDATest(t *testing.T, da da.DA) {
msg1 := []byte("message 1")
msg2 := []byte("message 2")

id1, proof1, err := da.Submit([]Blob{msg1}, -1)
ctx := context.TODO()
id1, proof1, err := da.Submit(ctx, []Blob{msg1}, -1)
assert.NoError(t, err)
assert.NotEmpty(t, id1)
assert.NotEmpty(t, proof1)

id2, proof2, err := da.Submit([]Blob{msg2}, -1)
id2, proof2, err := da.Submit(ctx, []Blob{msg2}, -1)
assert.NoError(t, err)
assert.NotEmpty(t, id2)
assert.NotEmpty(t, proof2)

id3, proof3, err := da.Submit([]Blob{msg1}, -1)
id3, proof3, err := da.Submit(ctx, []Blob{msg1}, -1)
assert.NoError(t, err)
assert.NotEmpty(t, id3)
assert.NotEmpty(t, proof3)

assert.NotEqual(t, id1, id2)
assert.NotEqual(t, id1, id3)

ret, err := da.Get(id1)
ret, err := da.Get(ctx, id1)
assert.NoError(t, err)
assert.Equal(t, []Blob{msg1}, ret)

commitment1, err := da.Commit([]Blob{msg1})
commitment1, err := da.Commit(ctx, []Blob{msg1})
assert.NoError(t, err)
assert.NotEmpty(t, commitment1)

commitment2, err := da.Commit([]Blob{msg2})
commitment2, err := da.Commit(ctx, []Blob{msg2})
assert.NoError(t, err)
assert.NotEmpty(t, commitment2)

oks, err := da.Validate(id1, proof1)
oks, err := da.Validate(ctx, id1, proof1)
assert.NoError(t, err)
assert.NotEmpty(t, oks)
for _, ok := range oks {
assert.True(t, ok)
}

oks, err = da.Validate(id2, proof2)
oks, err = da.Validate(ctx, id2, proof2)
assert.NoError(t, err)
assert.NotEmpty(t, oks)
for _, ok := range oks {
assert.True(t, ok)
}

oks, err = da.Validate(id1, proof2)
oks, err = da.Validate(ctx, id1, proof2)
assert.NoError(t, err)
assert.NotEmpty(t, oks)
for _, ok := range oks {
assert.False(t, ok)
}

oks, err = da.Validate(id2, proof1)
oks, err = da.Validate(ctx, id2, proof1)
assert.NoError(t, err)
assert.NotEmpty(t, oks)
for _, ok := range oks {
Expand All @@ -101,7 +103,8 @@ func BasicDATest(t *testing.T, da da.DA) {

// CheckErrors ensures that errors are handled properly by DA.
func CheckErrors(t *testing.T, da da.DA) {
blob, err := da.Get([]ID{[]byte("invalid")})
ctx := context.TODO()
blob, err := da.Get(ctx, []ID{[]byte("invalid")})
assert.Error(t, err)
assert.Empty(t, blob)
}
Expand All @@ -110,7 +113,8 @@ func CheckErrors(t *testing.T, da da.DA) {
func GetIDsTest(t *testing.T, da da.DA) {
msgs := [][]byte{[]byte("msg1"), []byte("msg2"), []byte("msg3")}

ids, proofs, err := da.Submit(msgs, -1)
ctx := context.TODO()
ids, proofs, err := da.Submit(ctx, msgs, -1)
assert.NoError(t, err)
assert.Len(t, ids, len(msgs))
assert.Len(t, proofs, len(msgs))
Expand All @@ -122,12 +126,12 @@ func GetIDsTest(t *testing.T, da da.DA) {
// As we're the only user, we don't need to handle external data (that could be submitted in real world).
// There is no notion of height, so we need to scan the DA to get test data back.
for i := uint64(1); !found && !time.Now().After(end); i++ {
ret, err := da.GetIDs(i)
ret, err := da.GetIDs(ctx, i)
if err != nil {
t.Error("failed to get IDs:", err)
}
if len(ret) > 0 {
blobs, err := da.Get(ret)
blobs, err := da.Get(ctx, ret)
assert.NoError(t, err)

// Submit ensures atomicity of batch, so it makes sense to compare actual blobs (bodies) only when lengths
Expand All @@ -151,18 +155,20 @@ func ConcurrentReadWriteTest(t *testing.T, da da.DA) {
var wg sync.WaitGroup
wg.Add(2)

ctx := context.TODO()

go func() {
defer wg.Done()
for i := uint64(1); i <= 100; i++ {
_, err := da.GetIDs(i)
_, err := da.GetIDs(ctx, i)
assert.NoError(t, err)
}
}()

go func() {
defer wg.Done()
for i := uint64(1); i <= 100; i++ {
_, _, err := da.Submit([][]byte{[]byte("test")}, -1)
_, _, err := da.Submit(ctx, [][]byte{[]byte("test")}, -1)
assert.NoError(t, err)
}
}()
Expand Down