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

feat: extend api to include rollup id, add Hash function to Batch, add maxBytes #13

Merged
merged 14 commits into from
Oct 2, 2024
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ go 1.21.0

require (
github.com/cosmos/gogoproto v1.7.0
github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.67.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro=
github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
Expand All @@ -16,3 +22,7 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
10 changes: 8 additions & 2 deletions proto/sequencing/sequencing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ service SequencerOutput {

// GetNextBatchRequest ...
message GetNextBatchRequest {
// the unhashed rollup id
bytes rollup_id = 1;
// Merkle tree hash of the last batch
bytes last_batch_hash = 1;
bytes last_batch_hash = 2;
// maximum bytes that execution client can support per block
uint64 max_bytes = 3;
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// GetNextBatchResponse ...
Expand All @@ -52,8 +56,10 @@ service BatchVerifier {

// VerifyBatchRequest ...
message VerifyBatchRequest {
// the unhashed rollup id
bytes rollup_id = 1;
// Merkle tree hash of the batch
bytes batch_hash = 1;
bytes batch_hash = 2;
}

// VerifyBatchResponse
Expand Down
29 changes: 15 additions & 14 deletions proxy/grpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package grpc

import (
"context"
"time"

"google.golang.org/grpc"

Expand Down Expand Up @@ -46,34 +45,36 @@ func (c *Client) Stop() error {
}

// SubmitRollupTransaction submits a transaction from rollup to sequencer.
func (c *Client) SubmitRollupTransaction(ctx context.Context, rollupId []byte, tx []byte) error {
func (c *Client) SubmitRollupTransaction(ctx context.Context, req sequencing.SubmitRollupTransactionRequest) (*sequencing.SubmitRollupTransactionResponse, error) {
_, err := c.SequencerInputClient.SubmitRollupTransaction(ctx, &pbseq.SubmitRollupTransactionRequest{
RollupId: rollupId,
Data: tx,
RollupId: req.RollupId,
Data: req.RollupId,
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
})
return err
return nil, err
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// GetNextBatch returns the next batch of transactions from sequencer to rollup.
func (c *Client) GetNextBatch(ctx context.Context, lastBatchHash []byte) (*sequencing.Batch, time.Time, error) {
resp, err := c.SequencerOutputClient.GetNextBatch(ctx, &pbseq.GetNextBatchRequest{LastBatchHash: lastBatchHash[:]})
func (c *Client) GetNextBatch(ctx context.Context, req sequencing.GetNextBatchRequest) (*sequencing.GetNextBatchResponse, error) {
resp, err := c.SequencerOutputClient.GetNextBatch(ctx, &pbseq.GetNextBatchRequest{RollupId: req.RollupId, LastBatchHash: req.LastBatchHash})
if err != nil {
return nil, time.Now(), err
return nil, err
}
b := &sequencing.Batch{}
b.FromProto(resp.Batch)
t, err := types.TimestampFromProto(resp.Timestamp)
if err != nil {
return nil, time.Now(), err
return nil, err
}
return b, t, nil
return &sequencing.GetNextBatchResponse{Batch: b, Timestamp: t}, nil
}

// VerifyBatch verifies a batch of transactions received from the sequencer.
func (c *Client) VerifyBatch(ctx context.Context, batchHash []byte) (bool, error) {
resp, err := c.BatchVerifierClient.VerifyBatch(ctx, &pbseq.VerifyBatchRequest{BatchHash: batchHash[:]})
func (c *Client) VerifyBatch(ctx context.Context, req sequencing.VerifyBatchRequest) (*sequencing.VerifyBatchResponse, error) {
resp, err := c.BatchVerifierClient.VerifyBatch(ctx, &pbseq.VerifyBatchRequest{RollupId: req.RollupId, BatchHash: req.BatchHash})
if err != nil {
return false, err
return nil, err
}
return resp.Status, nil
return &sequencing.VerifyBatchResponse{Status: resp.Status}, nil
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

var _ sequencing.Sequencer = &Client{}
12 changes: 6 additions & 6 deletions proxy/grpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type proxyVerificationSrv struct {

// SubmitRollupTransaction submits a transaction from rollup to sequencer.
func (s *proxyInputSrv) SubmitRollupTransaction(ctx context.Context, req *pbseq.SubmitRollupTransactionRequest) (*pbseq.SubmitRollupTransactionResponse, error) {
err := s.SequencerInput.SubmitRollupTransaction(ctx, req.RollupId, req.Data)
_, err := s.SequencerInput.SubmitRollupTransaction(ctx, sequencing.SubmitRollupTransactionRequest{RollupId: req.RollupId, Tx: req.Data})
if err != nil {
return nil, err
}
Expand All @@ -55,22 +55,22 @@ func (s *proxyInputSrv) SubmitRollupTransaction(ctx context.Context, req *pbseq.

// GetNextBatch returns the next batch of transactions from sequencer to rollup.
func (s *proxyOutputSrv) GetNextBatch(ctx context.Context, req *pbseq.GetNextBatchRequest) (*pbseq.GetNextBatchResponse, error) {
batch, timestamp, err := s.SequencerOutput.GetNextBatch(ctx, req.LastBatchHash[:])
resp, err := s.SequencerOutput.GetNextBatch(ctx, sequencing.GetNextBatchRequest{RollupId: req.RollupId, LastBatchHash: req.LastBatchHash})
if err != nil {
return nil, err
}
ts, err := types.TimestampProto(timestamp)
ts, err := types.TimestampProto(resp.Timestamp)
if err != nil {
return nil, err
}
return &pbseq.GetNextBatchResponse{Batch: batch.ToProto(), Timestamp: ts}, nil
return &pbseq.GetNextBatchResponse{Batch: resp.Batch.ToProto(), Timestamp: ts}, nil
}

// VerifyBatch verifies a batch of transactions received from the sequencer.
func (s *proxyVerificationSrv) VerifyBatch(ctx context.Context, req *pbseq.VerifyBatchRequest) (*pbseq.VerifyBatchResponse, error) {
ok, err := s.BatchVerifier.VerifyBatch(ctx, req.BatchHash[:])
resp, err := s.BatchVerifier.VerifyBatch(ctx, sequencing.VerifyBatchRequest{BatchHash: req.BatchHash})
if err != nil {
return nil, err
}
return &pbseq.VerifyBatchResponse{Status: ok}, nil
return &pbseq.VerifyBatchResponse{Status: resp.Status}, nil
}
50 changes: 46 additions & 4 deletions sequencing.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,29 @@ type Sequencer interface {
// SequencerInput provides a method for submitting a transaction from rollup to sequencer
type SequencerInput interface {
// SubmitRollupTransaction submits a transaction from rollup to sequencer
SubmitRollupTransaction(ctx context.Context, rollupId RollupId, tx Tx) error
// RollupId is the unique identifier for the rollup chain
// Tx is the transaction to submit
// returns an error if any from the sequencer
SubmitRollupTransaction(ctx context.Context, req SubmitRollupTransactionRequest) (*SubmitRollupTransactionResponse, error)
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// SequencerOutput provides a method for getting the next batch of transactions from sequencer to rollup
type SequencerOutput interface {
// GetNextBatch returns the next batch of transactions from sequencer to rollup
// lastBatch is the last batch of transactions received from the sequencer
// RollupId is the unique identifier for the rollup chain
// LastBatchHash is the cryptographic hash of the last batch received by the rollup
// MaxBytes is the maximum number of bytes to return in the batch
// returns the next batch of transactions and an error if any from the sequencer
GetNextBatch(ctx context.Context, lastBatchHash Hash) (*Batch, time.Time, error)
GetNextBatch(ctx context.Context, req GetNextBatchRequest) (*GetNextBatchResponse, error)
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// BatchVerifier provides a method for verifying a batch of transactions received from the sequencer
type BatchVerifier interface {
// VerifyBatch verifies a batch of transactions received from the sequencer
VerifyBatch(ctx context.Context, batchHash Hash) (bool, error)
// RollupId is the unique identifier for the rollup chain
// BatchHash is the cryptographic hash of the batch to verify
// returns a boolean indicating if the batch is valid and an error if any from the sequencer
VerifyBatch(ctx context.Context, req VerifyBatchRequest) (*VerifyBatchResponse, error)
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// RollupId is a unique identifier for a rollup chain
Expand All @@ -45,3 +53,37 @@ type Hash = []byte
type Batch struct {
Transactions []Tx
}

// SubmitRollupTransactionRequest is a request to submit a transaction from rollup to sequencer
type SubmitRollupTransactionRequest struct {
RollupId RollupId
Tx Tx
}

// SubmitRollupTransactionResponse is a response to submitting a transaction from rollup to sequencer
type SubmitRollupTransactionResponse struct {
MSevey marked this conversation as resolved.
Show resolved Hide resolved
}

gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
// GetNextBatchRequest is a request to get the next batch of transactions from sequencer to rollup
type GetNextBatchRequest struct {
RollupId RollupId
LastBatchHash Hash
MaxBytes uint64
}

// GetNextBatchResponse is a response to getting the next batch of transactions from sequencer to rollup
type GetNextBatchResponse struct {
Batch *Batch
Timestamp time.Time
}

// VerifyBatchRequest is a request to verify a batch of transactions received from the sequencer
type VerifyBatchRequest struct {
RollupId RollupId
BatchHash Hash
}

// VerifyBatchResponse is a response to verifying a batch of transactions received from the sequencer
type VerifyBatchResponse struct {
Status bool
}
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 12 additions & 0 deletions serialization.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package sequencing

import (
"crypto/sha256"

pbseq "github.com/rollkit/go-sequencing/types/pb/sequencing"
)

Expand Down Expand Up @@ -46,3 +48,13 @@ func (batch *Batch) Unmarshal(data []byte) error {
batch.FromProto(&pb)
return nil
}

// Hash returns the hash of a batch.
func (batch *Batch) Hash() ([]byte, error) {
batchBytes, err := batch.Marshal()
if err != nil {
return nil, err
}
hash := sha256.Sum256(batchBytes)
return hash[:], nil
}
81 changes: 40 additions & 41 deletions test/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ package test
import (
"bytes"
"context"
"crypto/sha256"
"errors"
"sync"
"time"

"github.com/rollkit/go-sequencing"
)

// ErrorRollupIdMismatch is returned when the rollup id does not match
var ErrorRollupIdMismatch = errors.New("rollup id mismatch")

// TransactionQueue is a queue of transactions
type TransactionQueue struct {
queue []sequencing.Tx
Expand Down Expand Up @@ -50,62 +46,70 @@ func (tq *TransactionQueue) GetNextBatch() *sequencing.Batch {

// DummySequencer is a dummy sequencer for testing that serves a single rollup
type DummySequencer struct {
sequencing.RollupId

tq *TransactionQueue
lastBatchHash []byte
tq *TransactionQueue
lastBatchHash []byte
lastBatchHashMutex sync.RWMutex

seenBatches map[string]struct{}
seenBatches map[string]struct{}
seenBatchesMutex sync.Mutex
}

// SubmitRollupTransaction implements sequencing.Sequencer.
func (d *DummySequencer) SubmitRollupTransaction(ctx context.Context, rollupId []byte, tx []byte) error {
if d.RollupId == nil {
d.RollupId = rollupId
} else {
if !bytes.Equal(d.RollupId, rollupId) {
return ErrorRollupIdMismatch
}
}
d.tq.AddTransaction(tx)
return nil
func (d *DummySequencer) SubmitRollupTransaction(ctx context.Context, req sequencing.SubmitRollupTransactionRequest) (*sequencing.SubmitRollupTransactionResponse, error) {
d.tq.AddTransaction(req.Tx)
return &sequencing.SubmitRollupTransactionResponse{}, nil
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// GetNextBatch implements sequencing.Sequencer.
func (d *DummySequencer) GetNextBatch(ctx context.Context, lastBatchHash []byte) (*sequencing.Batch, time.Time, error) {
func (d *DummySequencer) GetNextBatch(ctx context.Context, req sequencing.GetNextBatchRequest) (*sequencing.GetNextBatchResponse, error) {
now := time.Now()
if d.lastBatchHash == nil {
if lastBatchHash != nil {
return nil, now, errors.New("lastBatch is supposed to be nil")
d.lastBatchHashMutex.RLock()
lastBatchHash := d.lastBatchHash
d.lastBatchHashMutex.RUnlock()
if lastBatchHash == nil {
if req.LastBatchHash != nil {
return nil, errors.New("lastBatch is supposed to be nil")
}
} else if lastBatchHash == nil {
return nil, now, errors.New("lastBatch is not supposed to be nil")
} else if req.LastBatchHash == nil {
return nil, errors.New("lastBatch is not supposed to be nil")
} else {
if !bytes.Equal(d.lastBatchHash, lastBatchHash) {
return nil, now, errors.New("supplied lastBatch does not match with sequencer last batch")
if !bytes.Equal(lastBatchHash, req.LastBatchHash) {
return nil, errors.New("supplied lastBatch does not match with sequencer last batch")
}
}

gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
batch := d.tq.GetNextBatch()
batchRes := &sequencing.GetNextBatchResponse{Batch: batch, Timestamp: now}
// If there are no transactions, return empty batch without updating the last batch hash
if batch.Transactions == nil {
return batch, now, nil
return batchRes, nil
}

batchBytes, err := batch.Marshal()
h, err := batch.Hash()
if err != nil {
return nil, now, err
return nil, err
}

d.lastBatchHash = hashSHA256(batchBytes)
d.seenBatches[string(d.lastBatchHash)] = struct{}{}
return batch, now, nil
d.lastBatchHashMutex.Lock()
d.lastBatchHash = h
d.lastBatchHashMutex.Unlock()

d.seenBatchesMutex.Lock()
d.seenBatches[string(h)] = struct{}{}
d.seenBatchesMutex.Unlock()
return batchRes, nil
}

// VerifyBatch implements sequencing.Sequencer.
func (d *DummySequencer) VerifyBatch(ctx context.Context, batchHash []byte) (bool, error) {
_, ok := d.seenBatches[string(batchHash)]
return ok, nil
func (d *DummySequencer) VerifyBatch(ctx context.Context, req sequencing.VerifyBatchRequest) (*sequencing.VerifyBatchResponse, error) {
d.seenBatchesMutex.Lock()
defer d.seenBatchesMutex.Unlock()
for batchHash := range d.seenBatches {
if bytes.Equal([]byte(batchHash), req.BatchHash) {
return &sequencing.VerifyBatchResponse{Status: true}, nil
}
}
return &sequencing.VerifyBatchResponse{Status: false}, nil
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// NewDummySequencer creates a new DummySequencer
Expand All @@ -116,9 +120,4 @@ func NewDummySequencer() *DummySequencer {
}
}

func hashSHA256(data []byte) []byte {
hash := sha256.Sum256(data)
return hash[:]
}

var _ sequencing.Sequencer = &DummySequencer{}
Loading
Loading