-
Notifications
You must be signed in to change notification settings - Fork 276
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(permissionless batches): recovery mode after permissionless batches #1073
base: syncUpstream/active
Are you sure you want to change the base?
Changes from all commits
66bddc5
605da8b
20caa19
eb6df8a
b23eb93
afdc961
86aa203
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2021,15 +2021,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) | |
return it.index, err | ||
} | ||
|
||
func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types.Header, txs types.Transactions) (WriteStatus, error) { | ||
func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types.Header, txs types.Transactions, sign bool) (*types.Block, WriteStatus, error) { | ||
if !bc.chainmu.TryLock() { | ||
return NonStatTy, errInsertionInterrupted | ||
return nil, NonStatTy, errInsertionInterrupted | ||
} | ||
defer bc.chainmu.Unlock() | ||
|
||
statedb, err := state.New(parentBlock.Root(), bc.stateCache, bc.snaps) | ||
if err != nil { | ||
return NonStatTy, err | ||
return nil, NonStatTy, err | ||
} | ||
|
||
statedb.StartPrefetcher("l1sync") | ||
|
@@ -2040,18 +2040,51 @@ func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types | |
tempBlock := types.NewBlockWithHeader(header).WithBody(txs, nil) | ||
receipts, logs, gasUsed, err := bc.processor.Process(tempBlock, statedb, bc.vmConfig) | ||
if err != nil { | ||
return NonStatTy, fmt.Errorf("error processing block: %w", err) | ||
return nil, NonStatTy, fmt.Errorf("error processing block %d: %w", header.Number.Uint64(), err) | ||
} | ||
|
||
// TODO: once we have the extra and difficulty we need to verify the signature of the block with Clique | ||
// This should be done with https://github.com/scroll-tech/go-ethereum/pull/913. | ||
|
||
// finalize and assemble block as fullBlock | ||
if sign { | ||
// remember the time as Clique will override it | ||
originalTime := header.Time | ||
|
||
err = bc.engine.Prepare(bc, header) | ||
if err != nil { | ||
return nil, NonStatTy, fmt.Errorf("error preparing block %d: %w", tempBlock.Number().Uint64(), err) | ||
} | ||
|
||
// we want to re-sign the block: set time to original value again. | ||
header.Time = originalTime | ||
} | ||
|
||
// finalize and assemble block as fullBlock: replicates consensus.FinalizeAndAssemble() | ||
header.GasUsed = gasUsed | ||
header.Root = statedb.IntermediateRoot(bc.chainConfig.IsEIP158(header.Number)) | ||
|
||
fullBlock := types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) | ||
|
||
// Sign the block if requested | ||
if sign { | ||
resultCh, stopCh := make(chan *types.Block), make(chan struct{}) | ||
if err = bc.engine.Seal(bc, fullBlock, resultCh, stopCh); err != nil { | ||
return nil, NonStatTy, fmt.Errorf("error sealing block %d: %w", fullBlock.Number().Uint64(), err) | ||
} | ||
// Clique.Seal() will only wait for a second before giving up on us. So make sure there is nothing computational heavy | ||
// or a call that blocks between the call to Seal and the line below. Seal might introduce some delay, so we keep track of | ||
// that artificially added delay and subtract it from overall runtime of commit(). | ||
fullBlock = <-resultCh | ||
if fullBlock == nil { | ||
return nil, NonStatTy, fmt.Errorf("sealing block failed %d: block is nil", header.Number.Uint64()) | ||
} | ||
|
||
// verify the generated block with local consensus engine to make sure everything is as expected | ||
if err = bc.engine.VerifyHeader(bc, fullBlock.Header()); err != nil { | ||
return nil, NonStatTy, fmt.Errorf("error verifying signed block %d: %w", fullBlock.Number().Uint64(), err) | ||
} | ||
} | ||
|
||
blockHash := fullBlock.Hash() | ||
// manually replace the block hash in the receipts | ||
for i, receipt := range receipts { | ||
|
@@ -2068,7 +2101,14 @@ func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types | |
l.BlockHash = blockHash | ||
} | ||
|
||
return bc.writeBlockAndSetHead(fullBlock, receipts, logs, statedb, false) | ||
// Double check: even though we just built the block, make sure it is valid. | ||
if err = bc.validator.ValidateState(fullBlock, statedb, receipts, gasUsed); err != nil { | ||
bc.reportBlock(fullBlock, receipts, err) | ||
return nil, NonStatTy, fmt.Errorf("error validating block %d: %w", fullBlock.Number().Uint64(), err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not that expensive to run but I can't see a reason that this would ever fail. |
||
} | ||
|
||
writeStatus, err := bc.writeBlockAndSetHead(fullBlock, receipts, logs, statedb, false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would set |
||
return fullBlock, writeStatus, err | ||
} | ||
|
||
// insertSideChain is called when an import batch hits upon a pruned ancestor | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,9 @@ import ( | |
|
||
"github.com/scroll-tech/go-ethereum/core" | ||
"github.com/scroll-tech/go-ethereum/log" | ||
"github.com/scroll-tech/go-ethereum/rollup/ccc" | ||
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" | ||
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors" | ||
) | ||
|
||
var ( | ||
|
@@ -14,35 +16,70 @@ var ( | |
) | ||
|
||
type DASyncer struct { | ||
blockchain *core.BlockChain | ||
asyncChecker *ccc.AsyncChecker | ||
l2EndBlock uint64 | ||
blockchain *core.BlockChain | ||
} | ||
|
||
func NewDASyncer(blockchain *core.BlockChain) *DASyncer { | ||
return &DASyncer{ | ||
func NewDASyncer(blockchain *core.BlockChain, cccEnable bool, cccNumWorkers int, l2EndBlock uint64) *DASyncer { | ||
s := &DASyncer{ | ||
l2EndBlock: l2EndBlock, | ||
blockchain: blockchain, | ||
} | ||
|
||
if cccEnable { | ||
s.asyncChecker = ccc.NewAsyncChecker(blockchain, cccNumWorkers, false) | ||
} | ||
|
||
return s | ||
} | ||
|
||
// SyncOneBlock receives a PartialBlock, makes sure it's the next block in the chain, executes it and inserts it to the blockchain. | ||
func (s *DASyncer) SyncOneBlock(block *da.PartialBlock) error { | ||
func (s *DASyncer) SyncOneBlock(block *da.PartialBlock, override bool, sign bool) error { | ||
currentBlock := s.blockchain.CurrentBlock() | ||
|
||
// we expect blocks to be consecutive. block.PartialHeader.Number == parentBlock.Number+1. | ||
if block.PartialHeader.Number <= currentBlock.Number.Uint64() { | ||
// if override is true, we allow blocks to be lower than the current block number and replace the blocks. | ||
if !override && block.PartialHeader.Number <= currentBlock.Number.Uint64() { | ||
log.Debug("block number is too low", "block number", block.PartialHeader.Number, "parent block number", currentBlock.Number.Uint64()) | ||
return ErrBlockTooLow | ||
} else if block.PartialHeader.Number > currentBlock.Number.Uint64()+1 { | ||
log.Debug("block number is too high", "block number", block.PartialHeader.Number, "parent block number", currentBlock.Number.Uint64()) | ||
return ErrBlockTooHigh | ||
} | ||
|
||
parentBlock := s.blockchain.GetBlockByNumber(currentBlock.Number.Uint64()) | ||
if _, err := s.blockchain.BuildAndWriteBlock(parentBlock, block.PartialHeader.ToHeader(), block.Transactions); err != nil { | ||
parentBlockNumber := currentBlock.Number.Uint64() | ||
if override { | ||
parentBlockNumber = block.PartialHeader.Number - 1 | ||
} | ||
|
||
parentBlock := s.blockchain.GetBlockByNumber(parentBlockNumber) | ||
if parentBlock == nil { | ||
return fmt.Errorf("failed getting parent block, number: %d", parentBlockNumber) | ||
} | ||
|
||
fullBlock, _, err := s.blockchain.BuildAndWriteBlock(parentBlock, block.PartialHeader.ToHeader(), block.Transactions, sign) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting. |
||
if err != nil { | ||
return fmt.Errorf("failed building and writing block, number: %d, error: %v", block.PartialHeader.Number, err) | ||
} | ||
|
||
if s.blockchain.CurrentBlock().Number.Uint64()%1000 == 0 { | ||
log.Info("L1 sync progress", "blockhain height", s.blockchain.CurrentBlock().Number.Uint64(), "block hash", s.blockchain.CurrentBlock().Hash(), "root", s.blockchain.CurrentBlock().Root) | ||
if s.asyncChecker != nil { | ||
_ = s.asyncChecker.Check(fullBlock) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There seems to be no need to handle errors. what about handling errors here like printing some logs or removing error returns in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check cannot really fail but what happens if async CCC task fails? What does it mean for the enforced batch? |
||
} | ||
|
||
currentBlock = s.blockchain.CurrentBlock() | ||
if override && block.PartialHeader.Number != currentBlock.Number.Uint64() && block.PartialHeader.Number%100 == 0 { | ||
newBlock := s.blockchain.GetHeaderByNumber(block.PartialHeader.Number) | ||
log.Info("L1 sync progress", "processed block ", newBlock.Number.Uint64(), "block hash", newBlock.Hash(), "root", newBlock.Root) | ||
log.Info("L1 sync progress", "blockhain height", currentBlock.Number.Uint64(), "block hash", currentBlock.Hash(), "root", currentBlock.Root) | ||
} else if currentBlock.Number.Uint64()%100 == 0 { | ||
log.Info("L1 sync progress", "blockhain height", currentBlock.Number.Uint64(), "block hash", currentBlock.Hash(), "root", currentBlock.Root) | ||
} | ||
|
||
if s.l2EndBlock > 0 && s.l2EndBlock == block.PartialHeader.Number { | ||
newBlock := s.blockchain.GetHeaderByNumber(block.PartialHeader.Number) | ||
log.Warn("L1 sync reached L2EndBlock: you can terminate recovery mode now", "L2EndBlock", newBlock.Number.Uint64(), "block hash", newBlock.Hash(), "root", newBlock.Root) | ||
return serrors.Terminated | ||
} | ||
|
||
return nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't change this