Merge pull request #16 from 0glabs/tps

build block with gas limit
This commit is contained in:
MiniFrenchBread 2025-08-09 16:03:13 +08:00 committed by GitHub
commit a93a750e50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 127 additions and 44 deletions

View file

@ -119,13 +119,14 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// ValidateState validates the various changes that happen after a state transition,
// such as amount of used gas, the receipt roots and the state root itself.
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) error {
// Returns a new header with the actual state root instead of comparing with the original header.
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) (*types.Header, error) {
if res == nil {
return errors.New("nil ProcessResult value")
return nil, errors.New("nil ProcessResult value")
}
header := block.Header()
if block.GasUsed() != res.GasUsed {
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), res.GasUsed)
return nil, fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), res.GasUsed)
}
// Validate the received block's bloom with the one derived from the generated receipts.
// For valid blocks this should always validate to true.
@ -135,33 +136,35 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
// everything.
rbloom := types.MergeBloom(res.Receipts)
if rbloom != header.Bloom {
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
return nil, fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
}
// In stateless mode, return early because the receipt and state root are not
// provided through the witness, rather the cross validator needs to return it.
if stateless {
return nil
return nil, nil
}
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
receiptSha := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
return nil, fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
}
// Validate the parsed requests match the expected header value.
if header.RequestsHash != nil {
reqhash := types.CalcRequestsHash(res.Requests)
if reqhash != *header.RequestsHash {
return fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash)
return nil, fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash)
}
} else if res.Requests != nil {
return errors.New("block has requests before prague fork")
return nil, errors.New("block has requests before prague fork")
}
// Validate the state root against the received state root and throw
// an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
}
return nil
// Get the actual state root from the statedb
actualRoot := statedb.IntermediateRoot(v.config.IsEIP158(header.Number))
// Create a new header with the actual state root
newHeader := *header
newHeader.Root = actualRoot
return &newHeader, nil
}
// CalcGasLimit computes the gas limit of the next block after parent. It aims

View file

@ -1966,12 +1966,16 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
ptime := time.Since(pstart)
vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
newHeader, err := bc.validator.ValidateState(block, statedb, res, false)
if err != nil {
bc.reportBlock(block, res, err)
return nil, err
}
vtime := time.Since(vstart)
// Create a new block with the updated header
newBlock := types.NewBlockWithHeader(newHeader).WithBody(*block.Body())
// If witnesses was generated and stateless self-validation requested, do
// that now. Self validation should *never* run in production, it's more of
// a tight integration to enable running *all* consensus tests through the
@ -1979,25 +1983,25 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
// various invalid chain states/behaviors being contained in those tests.
xvstart := time.Now()
if witness := statedb.Witness(); witness != nil && bc.vmConfig.StatelessSelfValidation {
log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash())
log.Warn("Running stateless self-validation", "block", newBlock.Number(), "hash", newBlock.Hash())
// Remove critical computed fields from the block to force true recalculation
context := block.Header()
context := newBlock.Header()
context.Root = common.Hash{}
context.ReceiptHash = common.Hash{}
task := types.NewBlockWithHeader(context).WithBody(*block.Body())
task := types.NewBlockWithHeader(context).WithBody(*newBlock.Body())
// Run the stateless self-cross-validation
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, bc.vmConfig, task, witness)
if err != nil {
return nil, fmt.Errorf("stateless self-validation failed: %v", err)
}
if crossStateRoot != block.Root() {
return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root())
if crossStateRoot != newBlock.Root() {
return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, newBlock.Root())
}
if crossReceiptRoot != block.ReceiptHash() {
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash())
if crossReceiptRoot != newBlock.ReceiptHash() {
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, newBlock.ReceiptHash())
}
}
xvtime := time.Since(xvstart)
@ -2028,9 +2032,9 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
)
if !setHead {
// Don't set the head, only insert the block
err = bc.writeBlockWithState(block, res.Receipts, statedb)
err = bc.writeBlockWithState(newBlock, res.Receipts, statedb)
} else {
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
status, err = bc.writeBlockAndSetHead(newBlock, res.Receipts, res.Logs, statedb, false)
}
if err != nil {
return nil, err
@ -2418,14 +2422,77 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
// The key difference between the InsertChain is it won't do the canonical chain
// updating. It relies on the additional SetCanonical call to finalize the entire
// procedure.
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
// Returns the new header with actual state root and witness.
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*types.Header, *stateless.Witness, error) {
if !bc.chainmu.TryLock() {
return nil, errChainStopped
return nil, nil, errChainStopped
}
defer bc.chainmu.Unlock()
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
return witness, err
// Process the block to get the new header with actual state root
parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, nil, consensus.ErrUnknownAncestor
}
statedb, err := state.New(parent.Root, bc.statedb)
if err != nil {
return nil, nil, err
}
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil {
return nil, nil, err
}
newHeader, err := bc.validator.ValidateState(block, statedb, res, false)
log.Info("New Header", "stateroot", newHeader.Root)
if err != nil {
return nil, nil, err
}
// Create a new block with the updated header that contains the correct state root
log.Info("Block", "hash", block.Hash(), "number", block.Number())
updatedBlock := block.WithSeal(newHeader)
// Print the hash of the updated block for debugging purposes
log.Info("Updated Block Hash", "hash", updatedBlock.Hash())
// If the block hash has changed, we need to update the hash-to-number mapping
if updatedBlock.Hash() != block.Hash() {
log.Info("Block hash changed, updating hash-to-number mapping",
"old_hash", block.Hash(), "new_hash", updatedBlock.Hash(), "number", updatedBlock.Number())
// Write the updated hash-to-number mapping and remove the old one
batch := bc.db.NewBatch()
rawdb.WriteHeaderNumber(batch, updatedBlock.Hash(), updatedBlock.NumberU64())
rawdb.DeleteHeaderNumber(batch, block.Hash())
// Also clean up old receipts and body data if they exist
rawdb.DeleteReceipts(batch, block.Hash(), block.NumberU64())
rawdb.DeleteBody(batch, block.Hash(), block.NumberU64())
rawdb.DeleteHeader(batch, block.Hash(), block.NumberU64())
if err := batch.Write(); err != nil {
return nil, nil, fmt.Errorf("failed to update hash-to-number mapping: %w", err)
}
}
// Write the block with state
err = bc.writeBlockWithState(updatedBlock, res.Receipts, statedb)
if err != nil {
return nil, nil, err
}
// Generate witness if requested
var witness *stateless.Witness
if makeWitness && bc.chainConfig.IsByzantium(block.Number()) {
witness, err = stateless.NewWitness(updatedBlock.Header(), bc)
if err != nil {
return nil, nil, err
}
}
return newHeader, witness, nil
}
// SetCanonical rewinds the chain to set the new head block as the specified

View file

@ -161,7 +161,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.reportBlock(block, res, err)
return err
}
err = blockchain.validator.ValidateState(block, statedb, res, false)
_, err = blockchain.validator.ValidateState(block, statedb, res, false)
if err != nil {
blockchain.reportBlock(block, res, err)
return err
@ -3425,7 +3425,7 @@ func testSetCanonical(t *testing.T, scheme string) {
gen.AddTx(tx)
})
for _, block := range side {
_, err := chain.InsertBlockWithoutSetHead(block, false)
_, _, err := chain.InsertBlockWithoutSetHead(block, false)
if err != nil {
t.Fatalf("Failed to insert into chain: %v", err)
}

View file

@ -528,6 +528,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
peakGasUsed = floorDataGas
}
}
minGasUsed := st.initialGas * 80 / 100
if st.gasUsed() < minGasUsed {
prev := st.gasRemaining
st.gasRemaining = st.initialGas - minGasUsed
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeUnspecified)
}
}
st.returnGas()
effectiveTip := msg.GasPrice

View file

@ -70,11 +70,12 @@ func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *typ
if err != nil {
return common.Hash{}, common.Hash{}, err
}
if err = validator.ValidateState(block, db, res, true); err != nil {
newHeader, err := validator.ValidateState(block, db, res, true)
if err != nil {
return common.Hash{}, common.Hash{}, err
}
// Almost everything validated, but receipt and state root needs to be returned
receiptRoot := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
stateRoot := db.IntermediateRoot(config.IsEIP158(block.Number()))
stateRoot := newHeader.Root
return stateRoot, receiptRoot, nil
}

View file

@ -32,7 +32,8 @@ type Validator interface {
ValidateBody(block *types.Block) error
// ValidateState validates the given statedb and optionally the process result.
ValidateState(block *types.Block, state *state.StateDB, res *ProcessResult, stateless bool) error
// Returns a new header with the actual state root instead of comparing with the original header.
ValidateState(block *types.Block, state *state.StateDB, res *ProcessResult, stateless bool) (*types.Header, error)
}
// Prefetcher is an interface for pre-caching transaction signatures and state.

View file

@ -312,7 +312,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
api.forkchoiceLock.Lock()
defer api.forkchoiceLock.Unlock()
log.Trace("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash)
log.Info("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash)
if update.HeadBlockHash == (common.Hash{}) {
log.Warn("Forkchoice requested update to zero hash")
return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
@ -836,7 +836,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
api.newPayloadLock.Lock()
defer api.newPayloadLock.Unlock()
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
log.Info("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requests)
if err != nil {
bgu := "nil"
@ -876,11 +876,11 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// If we already have the block locally, ignore the entire execution and just
// return a fake success.
if block := api.eth.BlockChain().GetBlockByHash(params.BlockHash); block != nil {
/*if block := api.eth.BlockChain().GetBlockByHash(params.BlockHash); block != nil {
log.Warn("Ignoring already known beacon payload", "number", params.Number, "hash", params.BlockHash, "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
hash := block.Hash()
return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil
}
}*/
// If this block was rejected previously, keep rejecting it
if res := api.checkInvalidAncestor(block.Hash(), block.Hash()); res != nil {
return *res, nil
@ -911,8 +911,8 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
log.Warn("State not available, ignoring new payload")
return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil
}
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness)
log.Info("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
newHeader, proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness)
if err != nil {
log.Warn("NewPayload: inserting block failed", "error", err)
@ -923,7 +923,8 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
return api.invalid(err, parent.Header()), nil
}
hash := block.Hash()
hash := newHeader.Hash()
log.Info("NewHeader", "hash", hash, "root", newHeader.Root)
// If witness collection was requested, inject that into the result too
var ow *hexutil.Bytes

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
ethparams "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
)
@ -101,6 +102,8 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo
if err != nil {
return &newPayloadResult{err: err}
}
// Use gas limit for block creation instead of executing transactions
if !params.noTxs {
interrupt := new(atomic.Int32)
timer := time.AfterFunc(miner.config.Recommit, func() {
@ -173,10 +176,9 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo
work.header.RequestsHash = &reqHash
}
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
if err != nil {
return &newPayloadResult{err: err}
}
// Directly construct the block without calling FinalizeAndAssemble
// This returns a block with the current header, body, and receipts
block := types.NewBlock(work.header, &body, work.receipts, trie.NewStackTrie(nil))
return &newPayloadResult{
block: block,
fees: totalFees(block, work.receipts),