Skip to content

Commit

Permalink
save rollups (#1526)
Browse files Browse the repository at this point in the history
  • Loading branch information
tudor-malene authored Sep 15, 2023
1 parent 2adec7a commit 0529e96
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 24 deletions.
12 changes: 6 additions & 6 deletions go/enclave/components/rollup_compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,34 +112,34 @@ func (rc *RollupCompression) CreateExtRollup(r *core.Rollup) (*common.ExtRollup,
}

// ProcessExtRollup - given an External rollup, responsible with checking and saving all batches found inside
func (rc *RollupCompression) ProcessExtRollup(rollup *common.ExtRollup) error {
func (rc *RollupCompression) ProcessExtRollup(rollup *common.ExtRollup) (*common.CalldataRollupHeader, error) {
transactionsPerBatch := make([][]*common.L2Tx, 0)
err := rc.decryptDecompressAndDeserialise(rollup.BatchPayloads, &transactionsPerBatch)
if err != nil {
return err
return nil, err
}

calldataRollupHeader := new(common.CalldataRollupHeader)
err = rc.decryptDecompressAndDeserialise(rollup.CalldataRollupHeader, calldataRollupHeader)
if err != nil {
return err
return nil, err
}

// The recreation of batches is a 2-step process:

// 1. calculate fields like: sequence, height, time, l1Proof, from the implicit and explicit information from the metadata
incompleteBatches, err := rc.createIncompleteBatches(calldataRollupHeader, transactionsPerBatch, rollup.Header.CompressionL1Head)
if err != nil {
return err
return nil, err
}

// 2. execute each batch to be able to calculate the hash which is necessary for the next batch as it is the parent.
err = rc.executeAndSaveIncompleteBatches(calldataRollupHeader, incompleteBatches)
if err != nil {
return err
return nil, err
}

return nil
return calldataRollupHeader, nil
}

// the main logic that goes from a list of batches to the rollup header
Expand Down
8 changes: 7 additions & 1 deletion go/enclave/components/rollup_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,14 @@ func (rc *rollupConsumerImpl) ProcessRollupsInBlock(b *common.BlockAndReceipts)
if len(rollups) > 0 {
for _, rollup := range rollups {
// read batch data from rollup, verify and store it
if err := rc.rollupCompression.ProcessExtRollup(rollup); err != nil {
internalHeader, err := rc.rollupCompression.ProcessExtRollup(rollup)
if err != nil {
rc.logger.Error("Failed processing rollup", log.RollupHashKey, rollup.Hash(), log.ErrKey, err)
// todo - issue challenge as a validator
return err
}
if err := rc.storage.StoreRollup(rollup, internalHeader); err != nil {
rc.logger.Error("Failed storing rollup", log.RollupHashKey, rollup.Hash(), log.ErrKey, err)
return err
}
}
Expand Down
22 changes: 17 additions & 5 deletions go/enclave/nodetype/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,24 @@ func (s *sequencer) SubmitTransaction(transaction *common.L2Tx) error {
}

func (s *sequencer) OnL1Fork(fork *common.ChainFork) error {
if fork.IsFork() {
err := s.duplicateBatches(fork.NewCanonical, fork.NonCanonicalPath)
if err != nil {
return fmt.Errorf("could not duplicate batches. Cause %w", err)
}
if !fork.IsFork() {
return nil
}

err := s.duplicateBatches(fork.NewCanonical, fork.NonCanonicalPath)
if err != nil {
return fmt.Errorf("could not duplicate batches. Cause %w", err)
}

rollup, err := s.storage.FetchReorgedRollup(fork.NonCanonicalPath)
if err == nil {
s.logger.Error("Reissue rollup", log.RollupHashKey, rollup)
return nil
}
if !errors.Is(err, errutil.ErrNotFound) {
return fmt.Errorf("could not call FetchReorgedRollup. Cause: %w", err)
}

return nil
}

Expand Down
34 changes: 29 additions & 5 deletions go/enclave/storage/enclavedb/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const (
l1msgValue = "(?,?)"
selectL1Msg = "select message from l1_msg "

rollupInsert = "insert into rollup values (?,?,?,?)"
rollupInsert = "replace into rollup values (?,?,?,?,?)"
rollupSelect = "select hash from rollup where compression_block in "

updateCanonicalBlock = "update block set is_canonical=? where hash in "

Expand Down Expand Up @@ -144,21 +145,44 @@ func FetchL1Messages(db *sql.DB, blockHash common.L1BlockHash) (common.CrossChai
return result, nil
}

func WriteRollup(dbtx DBTransaction, rollup *common.RollupHeader) error {
func WriteRollup(dbtx DBTransaction, rollup *common.RollupHeader, internalHeader *common.CalldataRollupHeader) error {
// Write the encoded header
data, err := rlp.EncodeToBytes(rollup)
if err != nil {
return fmt.Errorf("could not encode batch header. Cause: %w", err)
}
dbtx.ExecuteSQL(rollupInsert,
0,
0,
rollup.Hash(),
internalHeader.FirstBatchSequence.Uint64(),
rollup.LastBatchSeqNo,
data,
nil,
rollup.CompressionL1Head.Bytes(),
)
return nil
}

func FetchReorgedRollup(db *sql.DB, reorgedBlocks []common.L1BlockHash) (*common.L2BatchHash, error) {
argPlaceholders := strings.Repeat("?,", len(reorgedBlocks))
argPlaceholders = argPlaceholders[0 : len(argPlaceholders)-1] // remove trailing comma

query := rollupSelect + " (" + argPlaceholders + ")"

args := make([]any, 0)
for _, value := range reorgedBlocks {
args = append(args, value.Bytes())
}
rollup := new(common.L2BatchHash)
err := db.QueryRow(query, args...).Scan(&rollup)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// make sure the error is converted to obscuro-wide not found error
return nil, errutil.ErrNotFound
}
return nil, err
}
return rollup, nil
}

func fetchBlockHeader(db *sql.DB, whereQuery string, args ...any) (*types.Header, error) {
var header string
query := selectBlockHeader + " " + whereQuery
Expand Down
13 changes: 13 additions & 0 deletions go/enclave/storage/init/edgelessdb/002_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
drop table obsdb.rollup;

create table if not exists obsdb.rollup
(
hash binary(32),
start_seq int NOT NULL,
end_seq int NOT NULL,
header blob NOT NULL,
compression_block binary(32) NOT NULL,
INDEX (compression_block),
primary key (hash)
);
GRANT ALL ON obsdb.rollup TO obscuro;
10 changes: 10 additions & 0 deletions go/enclave/storage/init/sqlite/002_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
drop table rollup;

create table rollup
(
hash binary(32) primary key,
start_seq int NOT NULL,
end_seq int NOT NULL,
header blob NOT NULL,
compression_block binary(32) NOT NULL
);
4 changes: 4 additions & 0 deletions go/enclave/storage/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ type BatchResolver interface {
StoreBatch(batch *core.Batch) error
// StoreExecutedBatch - store the batch after it was executed
StoreExecutedBatch(batch *core.Batch, receipts []*types.Receipt) error

// StoreRollup
StoreRollup(rollup *common.ExtRollup, header *common.CalldataRollupHeader) error
FetchReorgedRollup(reorgedBlocks []common.L1BlockHash) (*common.L2BatchHash, error)
}

type GethStateDB interface {
Expand Down
8 changes: 6 additions & 2 deletions go/enclave/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,12 @@ func (s *storageImpl) GetEnclaveKey() (*ecdsa.PrivateKey, error) {
return enclaveKey, nil
}

func (s *storageImpl) StoreRollup(rollup *common.ExtRollup) error {
func (s *storageImpl) StoreRollup(rollup *common.ExtRollup, internalHeader *common.CalldataRollupHeader) error {
callStart := time.Now()
defer s.logDuration("StoreRollup", callStart)
dbBatch := s.db.NewDBTransaction()

if err := enclavedb.WriteRollup(dbBatch, rollup.Header); err != nil {
if err := enclavedb.WriteRollup(dbBatch, rollup.Header, internalHeader); err != nil {
return fmt.Errorf("could not write rollup. Cause: %w", err)
}

Expand All @@ -475,6 +475,10 @@ func (s *storageImpl) StoreRollup(rollup *common.ExtRollup) error {
return nil
}

func (s *storageImpl) FetchReorgedRollup(reorgedBlocks []common.L1BlockHash) (*common.L2BatchHash, error) {
return enclavedb.FetchReorgedRollup(s.db.GetSQLDB(), reorgedBlocks)
}

func (s *storageImpl) DebugGetLogs(txHash common.TxHash) ([]*tracers.DebugLogs, error) {
callStart := time.Now()
defer s.logDuration("DebugGetLogs", callStart)
Expand Down
13 changes: 8 additions & 5 deletions integration/simulation/validate_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func checkObscuroBlockchainValidity(t *testing.T, s *Simulation, maxL1Height uin
min, max := minMax(heights)
// This checks that all the nodes are in sync. When a node falls behind with processing blocks it might highlight a problem.
// since there is one node that only listens to rollups it will be naturally behind.
if max-min > max/5 {
if max-min > max/3 {
t.Errorf("There is a problem with the Obscuro chain. Nodes fell out of sync. Max height: %d. Min height: %d -> %+v", max, min, heights)
}
}
Expand Down Expand Up @@ -264,7 +264,7 @@ func checkBlockchainOfObscuroNode(t *testing.T, rpcHandles *network.RPCHandles,
t.Errorf("Node %d: Obscuro node fell behind by %d blocks.", nodeIdx, maxEthereumHeight-l1Height)
}

// check that the height of the Rollup chain is higher than a minimum expected value.
// check that the height of the l2 chain is higher than a minimum expected value.
headBatchHeader, err := getHeadBatchHeader(obscuroClient)
if err != nil {
t.Error(fmt.Errorf("node %d: %w", nodeIdx, err))
Expand All @@ -279,13 +279,16 @@ func checkBlockchainOfObscuroNode(t *testing.T, rpcHandles *network.RPCHandles,
t.Errorf("Node %d: Node only mined %d rollups. Expected at least: %d.", nodeIdx, l2Height, minObscuroHeight)
}

// check that the height from the rollup header is consistent with the height returned by eth_blockNumber.
// check that the height from the head batch header is consistent with the height returned by eth_blockNumber.
l2HeightFromBatchNumber, err := obscuroClient.BatchNumber()
if err != nil {
t.Errorf("Node %d: Could not retrieve block number. Cause: %s", nodeIdx, err)
}
if l2HeightFromBatchNumber != l2Height.Uint64() {
t.Errorf("Node %d: Node's head rollup had a height %d, but %s height was %d", nodeIdx, l2Height, rpc.BatchNumber, l2HeightFromBatchNumber)
// due to the difference in calling time, the enclave could produce another batch
const maxAcceptedDiff = 2
heightDiff := int(l2HeightFromBatchNumber) - int(l2Height.Uint64())
if heightDiff > maxAcceptedDiff || heightDiff < -maxAcceptedDiff {
t.Errorf("Node %d: Node's head batch had a height %d, but %s height was %d", nodeIdx, l2Height, rpc.BatchNumber, l2HeightFromBatchNumber)
}

notFoundTransfers, notFoundWithdrawals, notFoundNativeTransfers := FindNotIncludedL2Txs(s.ctx, nodeIdx, rpcHandles, s.TxInjector)
Expand Down

0 comments on commit 0529e96

Please sign in to comment.