From 17b2a9ba9379d453ddec8d78523d59a3b4d626a9 Mon Sep 17 00:00:00 2001
From: ledgerwatch <akhounov@gmail.com>
Date: Thu, 24 Feb 2022 00:03:10 +0000
Subject: [PATCH] Bor fixes (#3553)

* Integration to work with bor

* Turn off validator set check

* disable verifySeal, add skeleton of postExec stage

* Pass around syscall

* Print

* Print more

* Default heimdall values for integration

* restore contract

* Print

* Print

* Print

* Print

* Print

* Print

* Print

* Print

* Fix nonce of system contract

* Remove prints

* Revert some more printing

* More fixes

* Print log

* Fix transfer log

* More printing

* More printing

* Print

* Print

* Print

* Print

* Print

* Print

* Print

* Fix validaor reward

* Remove printing

* Remove more prints

* Less printing

* Fetch validators from heimdall

* Remove syscall from Seal and CalcDifficulty

* Remove syscall from Prepare

* Print

* Remove DNS discovery

* Print apply snapshot

* Print

* Chunk up snapshot generation

* Chunk up snapshot generation

* Better logs when snapshotting

* Handle parents

* Prevent shadowing of snap

* Fix heimdall fetch

* Logging fixes

* Save generated snapshots

* Add header

* Less logging

Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro.local>
---
 cmd/integration/commands/stages.go        |   3 +
 consensus/bor/bor.go                      | 320 +++++++++++++---------
 consensus/bor/genesis_contracts_client.go |   7 +-
 consensus/bor/rest.go                     |   2 +-
 consensus/bor/span.go                     |  13 +
 core/blockchain.go                        |  14 +
 core/state_transition.go                  |  15 +-
 eth/stagedsync/stage_postexec.go          |  84 ++++++
 params/bootnodes.go                       |   6 -
 turbo/trie/hashbuilder.go                 |   3 +-
 10 files changed, 316 insertions(+), 151 deletions(-)
 create mode 100644 eth/stagedsync/stage_postexec.go

diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go
index dde83271b6..6f99cf2928 100644
--- a/cmd/integration/commands/stages.go
+++ b/cmd/integration/commands/stages.go
@@ -1109,6 +1109,9 @@ func newSync(ctx context.Context, db kv.RwDB, miningConfig *params.MiningConfig)
 	} else if chainConfig.Parlia != nil {
 		consensusConfig := &params.ParliaConfig{DBPath: filepath.Join(datadir, "parlia")}
 		engine = ethconfig.CreateConsensusEngine(chainConfig, logger, consensusConfig, config.Miner.Notify, config.Miner.Noverify, "", true, datadir)
+	} else if chainConfig.Bor != nil {
+		consensusConfig := &config.Bor
+		engine = ethconfig.CreateConsensusEngine(chainConfig, logger, consensusConfig, config.Miner.Notify, config.Miner.Noverify, "http://localhost:1317", false, datadir)
 	} else { //ethash
 		engine = ethash.NewFaker()
 	}
diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go
index 1f59ccb2ac..fe6445a020 100644
--- a/consensus/bor/bor.go
+++ b/consensus/bor/bor.go
@@ -14,6 +14,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/google/btree"
 	lru "github.com/hashicorp/golang-lru"
 	"github.com/ledgerwatch/erigon-lib/kv"
 	"github.com/ledgerwatch/erigon/accounts/abi"
@@ -223,9 +224,8 @@ type Bor struct {
 
 	// scope event.SubscriptionScope
 	// The fields below are for testing only
-	fakeDiff bool // Skip difficulty verifications
-
-	sysCall consensus.SystemCall
+	fakeDiff  bool // Skip difficulty verifications
+	spanCache *btree.BTree
 }
 
 // New creates a Matic Bor consensus engine.
@@ -261,6 +261,7 @@ func New(
 		GenesisContractsClient: genesisContractsClient,
 		HeimdallClient:         heimdallClient,
 		WithoutHeimdall:        withoutHeimdall,
+		spanCache:              btree.New(32),
 	}
 
 	// make sure we can decode all the GenesisAlloc in the BorConfig.
@@ -429,97 +430,130 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
 func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
 	// Search for a snapshot in memory or on disk for checkpoints
 	var (
-		headers []*types.Header
-		snap    *Snapshot
+		snap *Snapshot
 	)
 
-	for snap == nil {
-		// If an in-memory snapshot was found, use that
-		if s, ok := c.recents.Get(hash); ok {
-			snap = s.(*Snapshot)
-			break
-		}
-
-		// If an on-disk checkpoint snapshot can be found, use that
-		if number%checkpointInterval == 0 {
-			if s, err := loadSnapshot(c.config, c.signatures, c.DB, hash); err == nil {
-				log.Trace("Loaded snapshot from disk", "number", number, "hash", hash)
-				snap = s
+	cont := true // Continue applying snapshots
+	limit := 256
+	for cont {
+		var headersList [][]*types.Header // List of lists because we will apply headers to snapshot such that we can persist snapshot after every list
+		var headers []*types.Header
+		h := hash
+		n := number
+		p := parents
+		cont = false
+		for snap == nil {
+			// If an in-memory snapshot was found, use that
+			if s, ok := c.recents.Get(h); ok {
+				snap = s.(*Snapshot)
 				break
 			}
-		}
 
-		// If we're at the genesis, snapshot the initial state. Alternatively if we're
-		// at a checkpoint block without a parent (light client CHT), or we have piled
-		// up more headers than allowed to be reorged (chain reinit from a freezer),
-		// consider the checkpoint trusted and snapshot it.
-		// TODO fix this
-		if number == 0 {
-			checkpoint := chain.GetHeaderByNumber(number)
-			if checkpoint != nil {
-				// get checkpoint data
-				hash := checkpoint.Hash()
-
-				// get validators and current span
-				validators, err := c.GetCurrentValidators(number + 1)
-				if err != nil {
-					return nil, err
+			// If an on-disk checkpoint snapshot can be found, use that
+			if n%checkpointInterval == 0 {
+				if s, err := loadSnapshot(c.config, c.signatures, c.DB, h); err == nil {
+					log.Trace("Loaded snapshot from disk", "number", n, "hash", h)
+					snap = s
+					break
 				}
+			}
 
-				// new snap shot
-				snap = newSnapshot(c.config, c.signatures, number, hash, validators)
-				if err := snap.store(c.DB); err != nil {
-					return nil, err
+			// If we're at the genesis, snapshot the initial state. Alternatively if we're
+			// at a checkpoint block without a parent (light client CHT), or we have piled
+			// up more headers than allowed to be reorged (chain reinit from a freezer),
+			// consider the checkpoint trusted and snapshot it.
+			// TODO fix this
+			if n == 0 {
+				checkpoint := chain.GetHeaderByNumber(n)
+				if checkpoint != nil {
+					// get checkpoint data
+					h := checkpoint.Hash()
+
+					// get validators and current span
+					validators, err := c.GetCurrentValidators(n + 1)
+					if err != nil {
+						return nil, err
+					}
+
+					// new snap shot
+					snap = newSnapshot(c.config, c.signatures, n, h, validators)
+					if err := snap.store(c.DB); err != nil {
+						return nil, err
+					}
+					log.Info("Stored checkpoint snapshot to disk", "number", n, "hash", h)
+					break
 				}
-				log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
-				break
 			}
-		}
 
-		// No snapshot for this header, gather the header and move backward
-		var header *types.Header
-		if len(parents) > 0 {
-			// If we have explicit parents, pick from there (enforced)
-			header = parents[len(parents)-1]
-			if header.Hash() != hash || header.Number.Uint64() != number {
-				return nil, consensus.ErrUnknownAncestor
+			// No snapshot for this header, gather the header and move backward
+			var header *types.Header
+			if len(p) > 0 {
+				// If we have explicit parents, pick from there (enforced)
+				header = p[len(p)-1]
+				if header.Hash() != h || header.Number.Uint64() != n {
+					return nil, consensus.ErrUnknownAncestor
+				}
+				p = p[:len(p)-1]
+			} else {
+				// No explicit parents (or no more left), reach out to the database
+				header = chain.GetHeader(h, n)
+				if header == nil {
+					return nil, consensus.ErrUnknownAncestor
+				}
 			}
-			parents = parents[:len(parents)-1]
-		} else {
-			// No explicit parents (or no more left), reach out to the database
-			header = chain.GetHeader(hash, number)
-			if header == nil {
-				return nil, consensus.ErrUnknownAncestor
+			if n%checkpointInterval == 0 && len(headers) > 0 {
+				headersList = append(headersList, headers)
+				if len(headersList) > limit {
+					headersList = headersList[1:]
+					cont = true
+				}
+				headers = nil
 			}
+			headers = append(headers, header)
+			n--
+			h = header.ParentHash
 		}
-		headers = append(headers, header)
-		number, hash = number-1, header.ParentHash
-	}
-
-	// check if snapshot is nil
-	if snap == nil {
-		return nil, fmt.Errorf("Unknown error while retrieving snapshot at block number %v", number)
-	}
 
-	// Previous snapshot found, apply any pending headers on top of it
-	for i := 0; i < len(headers)/2; i++ {
-		headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
-	}
-
-	snap, err := snap.apply(headers)
-	if err != nil {
-		return nil, err
-	}
-	c.recents.Add(snap.Hash, snap)
+		// check if snapshot is nil
+		if snap == nil {
+			return nil, fmt.Errorf("unknown error while retrieving snapshot at block number %v", n)
+		}
+		if len(headers) > 0 {
+			headersList = append(headersList, headers)
+		}
 
-	// If we've generated a new checkpoint snapshot, save to disk
-	if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
-		if err = snap.store(c.DB); err != nil {
-			return nil, err
+		// Previous snapshot found, apply any pending headers on top of it
+		if cont {
+			lastList := headersList[len(headersList)-1]
+			firstList := headersList[0]
+			log.Info("Applying headers to snapshot", "from", lastList[len(lastList)-1].Number.Uint64(), "to", firstList[0].Number.Uint64())
+		}
+		for i := 0; i < len(headersList)/2; i++ {
+			headersList[i], headersList[len(headersList)-1-i] = headersList[len(headersList)-1-i], headersList[i]
+		}
+		for j := 0; j < len(headersList); j++ {
+			hs := headersList[j]
+			for i := 0; i < len(hs)/2; i++ {
+				hs[i], hs[len(hs)-1-i] = hs[len(hs)-1-i], hs[i]
+			}
+			var err error
+			snap, err = snap.apply(hs)
+			if err != nil {
+				return nil, err
+			}
+			c.recents.Add(snap.Hash, snap)
+			// We've generated a new checkpoint snapshot, save to disk
+			if err = snap.store(c.DB); err != nil {
+				return nil, err
+			}
+			log.Trace("Stored snapshot to disk", "number", snap.Number, "hash", snap.Hash)
+		}
+		if cont {
+			snap = nil
 		}
-		log.Trace("Stored snapshot to disk", "number", snap.Number, "hash", snap.Hash)
 	}
-	return snap, err
+
+	return snap, nil
 }
 
 // VerifyUncles implements consensus.Engine, always returning an error for any
@@ -549,17 +583,17 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
 	if number == 0 {
 		return errUnknownBlock
 	}
-	// Retrieve the snapshot needed to verify this header and cache it
-	snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
-	if err != nil {
-		return err
-	}
 
 	// Resolve the authorization key and check against signers
 	signer, err := ecrecover(header, c.signatures, c.config)
 	if err != nil {
 		return err
 	}
+	// Retrieve the snapshot needed to verify this header and cache it
+	snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
+	if err != nil {
+		return err
+	}
 	if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
 		// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
 		return &UnauthorizedSignerError{number - 1, signer.Bytes()}
@@ -661,22 +695,19 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header, s
 // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
 // rewards given.
 func (c *Bor) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs types.Transactions, uncles []*types.Header, r types.Receipts, e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall) (types.Transactions, types.Receipts, error) {
-
-	// Update sysCall
-
 	var err error
 	headerNumber := header.Number.Uint64()
 	if headerNumber%c.config.Sprint == 0 {
 		cx := chainContext{Chain: chain, Bor: c}
 		// check and commit span
-		if err := c.checkAndCommitSpan(state, header, cx); err != nil {
+		if err := c.checkAndCommitSpan(state, header, cx, syscall); err != nil {
 			log.Error("Error while committing span", "error", err)
 			return nil, types.Receipts{}, err
 		}
 
 		if !c.WithoutHeimdall {
 			// commit states
-			_, err = c.CommitStates(state, header, cx)
+			_, err = c.CommitStates(state, header, cx, syscall)
 			if err != nil {
 				log.Error("Error while committing states", "error", err)
 				return nil, types.Receipts{}, err
@@ -738,7 +769,7 @@ func (c *Bor) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types
 		cx := chainContext{Chain: chain, Bor: c}
 
 		// check and commit span
-		err := c.checkAndCommitSpan(state, header, cx)
+		err := c.checkAndCommitSpan(state, header, cx, syscall)
 		if err != nil {
 			log.Error("Error while committing span", "error", err)
 			return nil, nil, types.Receipts{}, err
@@ -746,7 +777,7 @@ func (c *Bor) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types
 
 		if !c.WithoutHeimdall {
 			// commit states
-			_, err = c.CommitStates(state, header, cx)
+			_, err = c.CommitStates(state, header, cx, syscall)
 			if err != nil {
 				log.Error("Error while committing states", "error", err)
 				return nil, nil, types.Receipts{}, err
@@ -913,7 +944,7 @@ func (c *Bor) Close() error {
 }
 
 // GetCurrentSpan get current span from contract
-func (c *Bor) GetCurrentSpan(header *types.Header, state *state.IntraBlockState, chain chainContext) (*Span, error) {
+func (c *Bor) GetCurrentSpan(header *types.Header, state *state.IntraBlockState, chain chainContext, syscall consensus.SystemCall) (*Span, error) {
 
 	// method
 	method := "getCurrentSpan"
@@ -923,7 +954,7 @@ func (c *Bor) GetCurrentSpan(header *types.Header, state *state.IntraBlockState,
 		return nil, err
 	}
 
-	result, err := c.sysCall(common.HexToAddress(c.config.ValidatorContract), data)
+	result, err := syscall(common.HexToAddress(c.config.ValidatorContract), data)
 	if err != nil {
 		return nil, err
 	}
@@ -949,56 +980,27 @@ func (c *Bor) GetCurrentSpan(header *types.Header, state *state.IntraBlockState,
 }
 
 // GetCurrentValidators get current validators
-func (c *Bor) GetCurrentValidators(blockNumber uint64) ([]*Validator, error) { // method
-	method := "getBorValidators"
-
-	data, err := c.validatorSetABI.Pack(method, big.NewInt(0).SetUint64(blockNumber))
+func (c *Bor) GetCurrentValidators(blockNumber uint64) ([]*Validator, error) {
+	span, err := c.getSpanForBlock(blockNumber)
 	if err != nil {
-		log.Error("Unable to pack tx for getValidator", "error", err)
 		return nil, err
 	}
-
-	result, err := c.sysCall(common.HexToAddress(c.config.ValidatorContract), data)
-	if err != nil {
-		panic(err)
-	}
-
-	var (
-		ret0 = new([]common.Address)
-		ret1 = new([]*big.Int)
-	)
-	out := &[]interface{}{
-		ret0,
-		ret1,
-	}
-
-	if err := c.validatorSetABI.UnpackIntoInterface(out, method, result); err != nil {
-		return nil, err
-	}
-
-	valz := make([]*Validator, len(*ret0))
-	for i, a := range *ret0 {
-		valz[i] = &Validator{
-			Address:     a,
-			VotingPower: (*ret1)[i].Int64(),
-		}
-	}
-
-	return valz, nil
+	return span.ValidatorSet.Validators, nil
 }
 
 func (c *Bor) checkAndCommitSpan(
 	state *state.IntraBlockState,
 	header *types.Header,
 	chain chainContext,
+	syscall consensus.SystemCall,
 ) error {
 	headerNumber := header.Number.Uint64()
-	span, err := c.GetCurrentSpan(header, state, chain)
+	span, err := c.GetCurrentSpan(header, state, chain, syscall)
 	if err != nil {
 		return err
 	}
 	if c.needToCommitSpan(span, headerNumber) {
-		err := c.fetchAndCommitSpan(span.ID+1, state, header, chain)
+		err := c.fetchAndCommitSpan(span.ID+1, state, header, chain, syscall)
 		return err
 	}
 	return nil
@@ -1023,16 +1025,64 @@ func (c *Bor) needToCommitSpan(span *Span, headerNumber uint64) bool {
 	return false
 }
 
+func (c *Bor) getSpanForBlock(blockNum uint64) (*HeimdallSpan, error) {
+	log.Info("Getting span", "for block", blockNum)
+	var span *HeimdallSpan
+	c.spanCache.AscendGreaterOrEqual(&HeimdallSpan{Span: Span{EndBlock: blockNum}}, func(item btree.Item) bool {
+		span = item.(*HeimdallSpan)
+		return false
+	})
+	if span == nil {
+		// Span with high enough block number is not loaded
+		var spanID uint64
+		if c.spanCache.Len() > 0 {
+			spanID = c.spanCache.Max().(*HeimdallSpan).ID + 1
+		}
+		for span == nil || span.EndBlock < blockNum {
+			var heimdallSpan HeimdallSpan
+			log.Info("Span with high enough block number is not loaded", "fetching span", spanID)
+			response, err := c.HeimdallClient.FetchWithRetry(fmt.Sprintf("bor/span/%d", spanID), "")
+			if err != nil {
+				return nil, err
+			}
+			if err := json.Unmarshal(response.Result, &heimdallSpan); err != nil {
+				return nil, err
+			}
+			span = &heimdallSpan
+			c.spanCache.ReplaceOrInsert(span)
+			spanID++
+		}
+	} else {
+		for span.StartBlock > blockNum {
+			// Span wit low enough block number is not loaded
+			var spanID uint64 = span.ID - 1
+			var heimdallSpan HeimdallSpan
+			log.Info("Span with low enough block number is not loaded", "fetching span", spanID)
+			response, err := c.HeimdallClient.FetchWithRetry(fmt.Sprintf("bor/span/%d", spanID), "")
+			if err != nil {
+				return nil, err
+			}
+			if err := json.Unmarshal(response.Result, &heimdallSpan); err != nil {
+				return nil, err
+			}
+			span = &heimdallSpan
+			c.spanCache.ReplaceOrInsert(span)
+		}
+	}
+	return span, nil
+}
+
 func (c *Bor) fetchAndCommitSpan(
 	newSpanID uint64,
 	state *state.IntraBlockState,
 	header *types.Header,
 	chain chainContext,
+	syscall consensus.SystemCall,
 ) error {
 	var heimdallSpan HeimdallSpan
 
 	if c.WithoutHeimdall {
-		s, err := c.getNextHeimdallSpanForTest(newSpanID, state, header, chain)
+		s, err := c.getNextHeimdallSpanForTest(newSpanID, state, header, chain, syscall)
 		if err != nil {
 			return err
 		}
@@ -1051,9 +1101,9 @@ func (c *Bor) fetchAndCommitSpan(
 	// check if chain id matches with heimdall span
 	if heimdallSpan.ChainID != c.chainConfig.ChainID.String() {
 		return fmt.Errorf(
-			"Chain id proposed span, %s, and bor chain id, %s, doesn't match",
+			"chain id proposed span, %s, and bor chain id, %s, doesn't match",
 			heimdallSpan.ChainID,
-			c.chainConfig.ChainID,
+			c.chainConfig.ChainID.String(),
 		)
 	}
 
@@ -1079,7 +1129,7 @@ func (c *Bor) fetchAndCommitSpan(
 
 	// method
 	method := "commitSpan"
-	log.Info("✅ Committing new span",
+	log.Debug("✅ Committing new span",
 		"id", heimdallSpan.ID,
 		"startBlock", heimdallSpan.StartBlock,
 		"endBlock", heimdallSpan.EndBlock,
@@ -1100,7 +1150,7 @@ func (c *Bor) fetchAndCommitSpan(
 		return err
 	}
 
-	_, err = c.sysCall(common.HexToAddress(c.config.ValidatorContract), data)
+	_, err = syscall(common.HexToAddress(c.config.ValidatorContract), data)
 	// apply message
 	return err
 }
@@ -1110,10 +1160,11 @@ func (c *Bor) CommitStates(
 	state *state.IntraBlockState,
 	header *types.Header,
 	chain chainContext,
+	syscall consensus.SystemCall,
 ) ([]*types.StateSyncData, error) {
 	stateSyncs := make([]*types.StateSyncData, 0)
 	number := header.Number.Uint64()
-	_lastStateID, err := c.GenesisContractsClient.LastStateId(header, state, chain, c)
+	_lastStateID, err := c.GenesisContractsClient.LastStateId(header, state, chain, c, syscall)
 	if err != nil {
 		return nil, err
 	}
@@ -1153,7 +1204,7 @@ func (c *Bor) CommitStates(
 		}
 		stateSyncs = append(stateSyncs, &stateData)
 
-		if err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain, c); err != nil {
+		if err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain, c, syscall); err != nil {
 			return nil, err
 		}
 		lastStateID++
@@ -1182,9 +1233,10 @@ func (c *Bor) getNextHeimdallSpanForTest(
 	state *state.IntraBlockState,
 	header *types.Header,
 	chain chainContext,
+	syscall consensus.SystemCall,
 ) (*HeimdallSpan, error) {
 	headerNumber := header.Number.Uint64()
-	span, err := c.GetCurrentSpan(header, state, chain)
+	span, err := c.GetCurrentSpan(header, state, chain, syscall)
 	if err != nil {
 		return nil, err
 	}
diff --git a/consensus/bor/genesis_contracts_client.go b/consensus/bor/genesis_contracts_client.go
index 70867ebecd..055774335f 100644
--- a/consensus/bor/genesis_contracts_client.go
+++ b/consensus/bor/genesis_contracts_client.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/ledgerwatch/erigon/accounts/abi"
 	"github.com/ledgerwatch/erigon/common"
+	"github.com/ledgerwatch/erigon/consensus"
 	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/params"
@@ -46,6 +47,7 @@ func (gc *GenesisContractsClient) CommitState(
 	header *types.Header,
 	chCtx chainContext,
 	c *Bor,
+	syscall consensus.SystemCall,
 ) error {
 	eventRecord := event.BuildEventRecord()
 	recordBytes, err := rlp.EncodeToBytes(eventRecord)
@@ -60,7 +62,7 @@ func (gc *GenesisContractsClient) CommitState(
 		return err
 	}
 	log.Trace("→ committing new state", "eventRecord", event.String())
-	_, err = c.sysCall(common.HexToAddress(gc.StateReceiverContract), data)
+	_, err = syscall(common.HexToAddress(gc.StateReceiverContract), data)
 	if err != nil {
 		return err
 	}
@@ -71,6 +73,7 @@ func (gc *GenesisContractsClient) LastStateId(header *types.Header,
 	state *state.IntraBlockState,
 	chain chainContext,
 	c *Bor,
+	syscall consensus.SystemCall,
 ) (*big.Int, error) {
 	method := "lastStateId"
 	data, err := gc.stateReceiverABI.Pack(method)
@@ -79,7 +82,7 @@ func (gc *GenesisContractsClient) LastStateId(header *types.Header,
 		return nil, err
 	}
 
-	result, err := c.sysCall(common.HexToAddress(gc.StateReceiverContract), data)
+	result, err := syscall(common.HexToAddress(gc.StateReceiverContract), data)
 	if err != nil {
 		return nil, err
 	}
diff --git a/consensus/bor/rest.go b/consensus/bor/rest.go
index 603e1f68b5..7a5e2a5e89 100644
--- a/consensus/bor/rest.go
+++ b/consensus/bor/rest.go
@@ -48,7 +48,7 @@ func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*Event
 	eventRecords := make([]*EventRecordWithTime, 0)
 	for {
 		queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
-		log.Info("Fetching state sync events", "queryParams", queryParams)
+		log.Trace("Fetching state sync events", "queryParams", queryParams)
 		response, err := h.FetchWithRetry("clerk/event-record/list", queryParams)
 		if err != nil {
 			return nil, err
diff --git a/consensus/bor/span.go b/consensus/bor/span.go
index 2fd0cf1079..3c9887a774 100644
--- a/consensus/bor/span.go
+++ b/consensus/bor/span.go
@@ -1,5 +1,9 @@
 package bor
 
+import (
+	"github.com/google/btree"
+)
+
 // Span represents a current bor span
 type Span struct {
 	ID         uint64 `json:"span_id" yaml:"span_id"`
@@ -14,3 +18,12 @@ type HeimdallSpan struct {
 	SelectedProducers []Validator  `json:"selected_producers" yaml:"selected_producers"`
 	ChainID           string       `json:"bor_chain_id" yaml:"bor_chain_id"`
 }
+
+func (hs *HeimdallSpan) Less(other btree.Item) bool {
+	otherHs := other.(*HeimdallSpan)
+	if hs.EndBlock == 0 || otherHs.EndBlock == 0 {
+		// if endblock is not specified in one of the items, allow search by ID
+		return hs.ID < otherHs.ID
+	}
+	return hs.EndBlock < otherHs.EndBlock
+}
diff --git a/core/blockchain.go b/core/blockchain.go
index c5721d4ff3..d1d3ce7399 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -351,6 +351,20 @@ func SysCallContract(contract common.Address, data []byte, chainConfig params.Ch
 	// Create a new context to be used in the EVM environment
 	blockContext := NewEVMBlockContext(header, nil, engine, &state.SystemAddress, nil)
 	evm := vm.NewEVM(blockContext, NewEVMTxContext(msg), ibs, &chainConfig, vmConfig)
+	if chainConfig.Bor != nil {
+		ret, _, err := evm.Call(
+			vm.AccountRef(msg.From()),
+			*msg.To(),
+			msg.Data(),
+			msg.Gas(),
+			msg.Value(),
+			false,
+		)
+		if err != nil {
+			return nil, nil
+		}
+		return ret, nil
+	}
 	res, err := ApplyMessage(evm, msg, gp, true /* refunds */, false /* gasBailout */)
 	if err != nil {
 		return nil, err
diff --git a/core/state_transition.go b/core/state_transition.go
index 95051efc6a..3612ee4066 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -429,10 +429,15 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
 	amount.Mul(amount, effectiveTip) // gasUsed * effectiveTip = how much goes to the block producer (miner, validator)
 	if st.isParlia {
 		st.state.AddBalance(consensus.SystemAddress, amount)
-	} else if london && st.isBor {
-		burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context().BlockNumber))
-		burnAmount := new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), st.evm.Context().BaseFee)
-		st.state.AddBalance(burntContractAddress, burnAmount)
+	} else {
+		st.state.AddBalance(st.evm.Context().Coinbase, amount)
+	}
+	if st.isBor {
+		if london {
+			burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context().BlockNumber))
+			burnAmount := new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), st.evm.Context().BaseFee)
+			st.state.AddBalance(burntContractAddress, burnAmount)
+		}
 		// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
 		// add transfer log
 		output1 := input1.Clone()
@@ -449,8 +454,6 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
 			output1.Sub(output1, amount),
 			output2.Add(output2, amount),
 		)
-	} else {
-		st.state.AddBalance(st.evm.Context().Coinbase, amount)
 	}
 
 	return &ExecutionResult{
diff --git a/eth/stagedsync/stage_postexec.go b/eth/stagedsync/stage_postexec.go
new file mode 100644
index 0000000000..5a119e56de
--- /dev/null
+++ b/eth/stagedsync/stage_postexec.go
@@ -0,0 +1,84 @@
+package stagedsync
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/ledgerwatch/erigon-lib/kv"
+)
+
+// PostExec stage is run after execution stage to peform extra verifications that are only possible when state is available.
+// It is used for consensus engines which keep validators inside smart contracts (Bor, AuRa)
+
+type PostExecCfg struct {
+	db    kv.RwDB
+	borDb kv.RwDB
+}
+
+func StagePostExecCfg(db kv.RwDB, borDb kv.RwDB) PostExecCfg {
+	return PostExecCfg{
+		db:    db,
+		borDb: borDb,
+	}
+}
+
+func SpawnPostExecStage(s *StageState, tx kv.RwTx, cfg PostExecCfg, ctx context.Context) error {
+	useExternalTx := tx != nil
+	if !useExternalTx {
+		var err error
+		tx, err = cfg.db.BeginRw(context.Background())
+		if err != nil {
+			return err
+		}
+		defer tx.Rollback()
+	}
+
+	//logPrefix := s.LogPrefix()
+	to, err := s.ExecutionAt(tx)
+	if err != nil {
+		return err
+	}
+
+	if s.BlockNumber == to {
+		// we already did hash check for this block
+		// we don't do the obvious `if s.BlockNumber > to` to support reorgs more naturally
+		return nil
+	}
+	if s.BlockNumber > to {
+		return fmt.Errorf("hashstate: promotion backwards from %d to %d", s.BlockNumber, to)
+	}
+
+	if err = s.Update(tx, to); err != nil {
+		return err
+	}
+
+	if !useExternalTx {
+		if err := tx.Commit(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func UnwindPostExecStage(u *UnwindState, s *StageState, tx kv.RwTx, cfg PostExecCfg, ctx context.Context) (err error) {
+	useExternalTx := tx != nil
+	if !useExternalTx {
+		tx, err = cfg.db.BeginRw(ctx)
+		if err != nil {
+			return err
+		}
+		defer tx.Rollback()
+	}
+
+	//logPrefix := u.LogPrefix()
+
+	if err = u.Done(tx); err != nil {
+		return err
+	}
+	if !useExternalTx {
+		if err = tx.Commit(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/params/bootnodes.go b/params/bootnodes.go
index 336207a17b..73402ea500 100644
--- a/params/bootnodes.go
+++ b/params/bootnodes.go
@@ -164,18 +164,12 @@ func KnownDNSNetwork(genesis common.Hash, protocol string) string {
 	switch genesis {
 	case MainnetGenesisHash:
 		net = "mainnet"
-	case SepoliaGenesisHash:
-		net = "sepolia"
 	case RopstenGenesisHash:
 		net = "ropsten"
 	case RinkebyGenesisHash:
 		net = "rinkeby"
 	case GoerliGenesisHash:
 		net = "goerli"
-	case MumbaiGenesisHash:
-		net = "mumbai"
-	case BorMainnetGenesisHash:
-		net = "bor-mainnet"
 	default:
 		return ""
 	}
diff --git a/turbo/trie/hashbuilder.go b/turbo/trie/hashbuilder.go
index 5f1e28e522..ca199c5648 100644
--- a/turbo/trie/hashbuilder.go
+++ b/turbo/trie/hashbuilder.go
@@ -251,7 +251,6 @@ func (hb *HashBuilder) accountLeaf(length int, keyHex []byte, balance *uint256.I
 	hb.nodeStack[len(hb.nodeStack)-1] = s
 	if hb.trace {
 		fmt.Printf("Stack depth: %d\n", len(hb.nodeStack))
-
 	}
 	return nil
 }
@@ -321,11 +320,11 @@ func (hb *HashBuilder) accountLeafHashWithKey(key []byte, popped int) error {
 	if err != nil {
 		return err
 	}
-	//fmt.Printf("accountLeafHashWithKey [%x]=>[%x]\nHash [%x]\n", key, val, hb.hashBuf[:])
 	if popped > 0 {
 		hb.hashStack = hb.hashStack[:len(hb.hashStack)-popped*hashStackStride]
 		hb.nodeStack = hb.nodeStack[:len(hb.nodeStack)-popped]
 	}
+	//fmt.Printf("accountLeafHashWithKey [%x]=>[%x]\nHash [%x]\n", key, val, hb.hashBuf[:])
 	hb.hashStack = append(hb.hashStack, hb.hashBuf[:]...)
 	hb.nodeStack = append(hb.nodeStack, nil)
 	if hb.trace {
-- 
GitLab