Commit 230ce603 authored by Conner Fromknecht's avatar Conner Fromknecht

feat: add IsMarkerContext, MarkerBatchType, and MarkerContext helpers

This commit adds helper methods to extract some of the serialization
logic into testable functions, and also add hooks to easily extend for
future formats.
parent 141a480f
...@@ -47,6 +47,25 @@ type BatchContext struct { ...@@ -47,6 +47,25 @@ type BatchContext struct {
BlockNumber uint64 `json:"block_number"` BlockNumber uint64 `json:"block_number"`
} }
// IsMarkerContext returns true if the BatchContext is a marker context used to
// specify the encoding format. This is only valid if called on the first
// BatchContext in the calldata.
func (c BatchContext) IsMarkerContext() bool {
return c.Timestamp == 0
}
// MarkerBatchType returns the BatchType specified by a marker BatchContext.
// The return value is only valid if called on the first BatchContext in the
// calldata and IsMarkerContext returns true.
func (c BatchContext) MarkerBatchType() BatchType {
switch c.BlockNumber {
case 0:
return BatchTypeZlib
default:
return BatchTypeLegacy
}
}
// Write encodes the BatchContext into a 16-byte stream using the following // Write encodes the BatchContext into a 16-byte stream using the following
// encoding: // encoding:
// - num_sequenced_txs: 3 bytes // - num_sequenced_txs: 3 bytes
...@@ -122,6 +141,26 @@ func (b BatchType) String() string { ...@@ -122,6 +141,26 @@ func (b BatchType) String() string {
} }
} }
// MarkerContext returns the marker context, if any, for the given batch type.
func (b BatchType) MarkerContext() *BatchContext {
switch b {
// No marker context for legacy encoding.
case BatchTypeLegacy:
return nil
// Zlib marker context sets block number equal to zero.
case BatchTypeZlib:
return &BatchContext{
Timestamp: 0,
BlockNumber: 0,
}
default:
return nil
}
}
// AppendSequencerBatchParams holds the raw data required to submit a batch of // AppendSequencerBatchParams holds the raw data required to submit a batch of
// L2 txs to L1 CTC contract. Rather than encoding the objects using the // L2 txs to L1 CTC contract. Rather than encoding the objects using the
// standard ABI encoding, a custom encoding is and provided in the call data to // standard ABI encoding, a custom encoding is and provided in the call data to
...@@ -191,10 +230,10 @@ func (p *AppendSequencerBatchParams) Write( ...@@ -191,10 +230,10 @@ func (p *AppendSequencerBatchParams) Write(
// copy the contexts as to not malleate the struct // copy the contexts as to not malleate the struct
// when it is a typed batch // when it is a typed batch
contexts := make([]BatchContext, 0, len(p.Contexts)+1) contexts := make([]BatchContext, 0, len(p.Contexts)+1)
if batchType == BatchTypeZlib { // Add the marker context, if any, for non-legacy encodings.
// All zero values for the single batch context markerContext := batchType.MarkerContext()
// is desired here as blocknumber 0 means it is a zlib batch if markerContext != nil {
contexts = append(contexts, BatchContext{}) contexts = append(contexts, *markerContext)
} }
contexts = append(contexts, p.Contexts...) contexts = append(contexts, p.Contexts...)
...@@ -270,6 +309,9 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error { ...@@ -270,6 +309,9 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
return err return err
} }
// Assume that it is a legacy batch at first, this will be overwrritten if
// we detect a marker context.
var batchType = BatchTypeLegacy
// Ensure that contexts is never nil // Ensure that contexts is never nil
p.Contexts = make([]BatchContext, 0) p.Contexts = make([]BatchContext, 0)
for i := uint64(0); i < numContexts; i++ { for i := uint64(0); i < numContexts; i++ {
...@@ -278,17 +320,17 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error { ...@@ -278,17 +320,17 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
return err return err
} }
p.Contexts = append(p.Contexts, batchContext) if i == 0 && batchContext.IsMarkerContext() {
batchType = batchContext.MarkerBatchType()
continue
} }
// Handle backwards compatible batch types p.Contexts = append(p.Contexts, batchContext)
if len(p.Contexts) > 0 && p.Contexts[0].Timestamp == 0 { }
switch p.Contexts[0].BlockNumber {
case 0:
// remove the first dummy context
p.Contexts = p.Contexts[1:]
numContexts--
// Handle newer batch type decodings.
switch batchType {
case BatchTypeZlib:
zr, err := zlib.NewReader(r) zr, err := zlib.NewReader(r)
if err != nil { if err != nil {
return err return err
...@@ -297,7 +339,6 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error { ...@@ -297,7 +339,6 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
r = bufio.NewReader(zr) r = bufio.NewReader(zr)
} }
}
// Deserialize any transactions. Since the number of txs is ommitted // Deserialize any transactions. Since the number of txs is ommitted
// from the encoding, loop until the stream is consumed. // from the encoding, loop until the stream is consumed.
...@@ -326,7 +367,6 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error { ...@@ -326,7 +367,6 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
p.Txs = append(p.Txs, NewCachedTx(tx)) p.Txs = append(p.Txs, NewCachedTx(tx))
} }
} }
// writeUint64 writes a the bottom `n` bytes of `val` to `w`. // writeUint64 writes a the bottom `n` bytes of `val` to `w`.
......
...@@ -184,3 +184,71 @@ func compareTxs(t *testing.T, a []*l2types.Transaction, b []*sequencer.CachedTx) ...@@ -184,3 +184,71 @@ func compareTxs(t *testing.T, a []*l2types.Transaction, b []*sequencer.CachedTx)
require.Equal(t, txA.Hash(), b[i].Tx().Hash()) require.Equal(t, txA.Hash(), b[i].Tx().Hash())
} }
} }
// TestMarkerContext asserts that each batch type returns the correct marker
// context.
func TestMarkerContext(t *testing.T) {
batchTypes := []sequencer.BatchType{
sequencer.BatchTypeLegacy,
sequencer.BatchTypeZlib,
}
for _, batchType := range batchTypes {
t.Run(batchType.String(), func(t *testing.T) {
markerContext := batchType.MarkerContext()
if batchType == sequencer.BatchTypeLegacy {
require.Nil(t, markerContext)
} else {
require.NotNil(t, markerContext)
// All marker contexts MUST have a zero timestamp.
require.Equal(t, uint64(0), markerContext.Timestamp)
// Currently all other fields besides block number are defined
// as zero.
require.Equal(t, uint64(0), markerContext.NumSequencedTxs)
require.Equal(t, uint64(0), markerContext.NumSubsequentQueueTxs)
// Assert that the block number for each batch type is set to
// the correct constant.
switch batchType {
case sequencer.BatchTypeZlib:
require.Equal(t, uint64(0), markerContext.BlockNumber)
default:
t.Fatalf("unknown batch type")
}
// Ensure MarkerBatchType produces the expected BatchType.
require.Equal(t, batchType, markerContext.MarkerBatchType())
}
})
}
}
// TestIsMarkerContext asserts that IsMarkerContext returns true iff the
// timestamp is zero.
func TestIsMarkerContext(t *testing.T) {
batchContext := sequencer.BatchContext{
NumSequencedTxs: 1,
NumSubsequentQueueTxs: 2,
Timestamp: 3,
BlockNumber: 4,
}
require.False(t, batchContext.IsMarkerContext())
batchContext = sequencer.BatchContext{
NumSequencedTxs: 0,
NumSubsequentQueueTxs: 0,
Timestamp: 3,
BlockNumber: 0,
}
require.False(t, batchContext.IsMarkerContext())
batchContext = sequencer.BatchContext{
NumSequencedTxs: 1,
NumSubsequentQueueTxs: 2,
Timestamp: 0,
BlockNumber: 4,
}
require.True(t, batchContext.IsMarkerContext())
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment