miner: add to build block with EIP-4844 blobs (#27875)
--------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
d1f6735171
commit
feb8f416ac
@ -23,7 +23,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -237,7 +236,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
|
|||||||
|
|
||||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
// fields from the given block. It assumes the given block is post-merge block.
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *ExecutionPayloadEnvelope {
|
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope {
|
||||||
data := &ExecutableData{
|
data := &ExecutableData{
|
||||||
BlockHash: block.Hash(),
|
BlockHash: block.Hash(),
|
||||||
ParentHash: block.ParentHash(),
|
ParentHash: block.ParentHash(),
|
||||||
@ -258,17 +257,19 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Bl
|
|||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
// TODO BeaconRoot
|
// TODO BeaconRoot
|
||||||
}
|
}
|
||||||
blobsBundle := BlobsBundleV1{
|
bundle := BlobsBundleV1{
|
||||||
Commitments: make([]hexutil.Bytes, 0),
|
Commitments: make([]hexutil.Bytes, 0),
|
||||||
Blobs: make([]hexutil.Bytes, 0),
|
Blobs: make([]hexutil.Bytes, 0),
|
||||||
Proofs: make([]hexutil.Bytes, 0),
|
Proofs: make([]hexutil.Bytes, 0),
|
||||||
}
|
}
|
||||||
for i := range blobs {
|
for _, sidecar := range sidecars {
|
||||||
blobsBundle.Blobs = append(blobsBundle.Blobs, hexutil.Bytes(blobs[i][:]))
|
for j := range sidecar.Blobs {
|
||||||
blobsBundle.Commitments = append(blobsBundle.Commitments, hexutil.Bytes(commitments[i][:]))
|
bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:]))
|
||||||
blobsBundle.Proofs = append(blobsBundle.Proofs, hexutil.Bytes(proofs[i][:]))
|
bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:]))
|
||||||
|
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
||||||
}
|
}
|
||||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &blobsBundle}
|
}
|
||||||
|
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||||
|
@ -42,8 +42,8 @@ func VerifyEIP4844Header(parent, header *types.Header) error {
|
|||||||
return errors.New("header is missing blobGasUsed")
|
return errors.New("header is missing blobGasUsed")
|
||||||
}
|
}
|
||||||
// Verify that the blob gas used remains within reasonable limits.
|
// Verify that the blob gas used remains within reasonable limits.
|
||||||
if *header.BlobGasUsed > params.BlobTxMaxBlobGasPerBlock {
|
if *header.BlobGasUsed > params.MaxBlobGasPerBlock {
|
||||||
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.BlobTxMaxBlobGasPerBlock)
|
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.MaxBlobGasPerBlock)
|
||||||
}
|
}
|
||||||
if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
|
if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
|
||||||
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
|
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
|
||||||
|
@ -53,7 +53,7 @@ const (
|
|||||||
// maxBlobsPerTransaction is the maximum number of blobs a single transaction
|
// maxBlobsPerTransaction is the maximum number of blobs a single transaction
|
||||||
// is allowed to contain. Whilst the spec states it's unlimited, the block
|
// is allowed to contain. Whilst the spec states it's unlimited, the block
|
||||||
// data slots are protocol bound, which implicitly also limit this.
|
// data slots are protocol bound, which implicitly also limit this.
|
||||||
maxBlobsPerTransaction = params.BlobTxMaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
|
maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
|
||||||
|
|
||||||
// txAvgSize is an approximate byte size of a transaction metadata to avoid
|
// txAvgSize is an approximate byte size of a transaction metadata to avoid
|
||||||
// tiny overflows causing all txs to move a shelf higher, wasting disk space.
|
// tiny overflows causing all txs to move a shelf higher, wasting disk space.
|
||||||
|
@ -120,8 +120,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||||||
if len(hashes) == 0 {
|
if len(hashes) == 0 {
|
||||||
return fmt.Errorf("blobless blob transaction")
|
return fmt.Errorf("blobless blob transaction")
|
||||||
}
|
}
|
||||||
if len(hashes) > params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
if len(hashes) > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
||||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
||||||
}
|
}
|
||||||
if err := validateBlobSidecar(hashes, sidecar); err != nil {
|
if err := validateBlobSidecar(hashes, sidecar); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1521,13 +1521,16 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
txs = append(txs, types.NewTx(&inner))
|
txs = append(txs, types.NewTx(&inner))
|
||||||
|
sidecars := []*types.BlobTxSidecar{
|
||||||
blobs := make([]kzg4844.Blob, 1)
|
{
|
||||||
commitments := make([]kzg4844.Commitment, 1)
|
Blobs: make([]kzg4844.Blob, 1),
|
||||||
proofs := make([]kzg4844.Proof, 1)
|
Commitments: make([]kzg4844.Commitment, 1),
|
||||||
|
Proofs: make([]kzg4844.Proof, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
block := types.NewBlock(&header, txs, nil, nil, trie.NewStackTrie(nil))
|
block := types.NewBlock(&header, txs, nil, nil, trie.NewStackTrie(nil))
|
||||||
envelope := engine.BlockToExecutableData(block, nil, blobs, commitments, proofs)
|
envelope := engine.BlockToExecutableData(block, nil, sidecars)
|
||||||
var want int
|
var want int
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
want += len(tx.BlobHashes())
|
want += len(tx.BlobHashes())
|
||||||
|
@ -65,6 +65,7 @@ type Payload struct {
|
|||||||
id engine.PayloadID
|
id engine.PayloadID
|
||||||
empty *types.Block
|
empty *types.Block
|
||||||
full *types.Block
|
full *types.Block
|
||||||
|
sidecars []*types.BlobTxSidecar
|
||||||
fullFees *big.Int
|
fullFees *big.Int
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
@ -84,7 +85,7 @@ func newPayload(empty *types.Block, id engine.PayloadID) *Payload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update updates the full-block with latest built version.
|
// update updates the full-block with latest built version.
|
||||||
func (payload *Payload) update(block *types.Block, fees *big.Int, elapsed time.Duration) {
|
func (payload *Payload) update(r *newPayloadResult, elapsed time.Duration) {
|
||||||
payload.lock.Lock()
|
payload.lock.Lock()
|
||||||
defer payload.lock.Unlock()
|
defer payload.lock.Unlock()
|
||||||
|
|
||||||
@ -96,14 +97,23 @@ func (payload *Payload) update(block *types.Block, fees *big.Int, elapsed time.D
|
|||||||
// Ensure the newly provided full block has a higher transaction fee.
|
// Ensure the newly provided full block has a higher transaction fee.
|
||||||
// In post-merge stage, there is no uncle reward anymore and transaction
|
// In post-merge stage, there is no uncle reward anymore and transaction
|
||||||
// fee(apart from the mev revenue) is the only indicator for comparison.
|
// fee(apart from the mev revenue) is the only indicator for comparison.
|
||||||
if payload.full == nil || fees.Cmp(payload.fullFees) > 0 {
|
if payload.full == nil || r.fees.Cmp(payload.fullFees) > 0 {
|
||||||
payload.full = block
|
payload.full = r.block
|
||||||
payload.fullFees = fees
|
payload.fullFees = r.fees
|
||||||
|
payload.sidecars = r.sidecars
|
||||||
|
|
||||||
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
|
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(r.fees), big.NewFloat(params.Ether))
|
||||||
log.Info("Updated payload", "id", payload.id, "number", block.NumberU64(), "hash", block.Hash(),
|
log.Info("Updated payload",
|
||||||
"txs", len(block.Transactions()), "withdrawals", len(block.Withdrawals()), "gas", block.GasUsed(),
|
"id", payload.id,
|
||||||
"fees", feesInEther, "root", block.Root(), "elapsed", common.PrettyDuration(elapsed))
|
"number", r.block.NumberU64(),
|
||||||
|
"hash", r.block.Hash(),
|
||||||
|
"txs", len(r.block.Transactions()),
|
||||||
|
"withdrawals", len(r.block.Withdrawals()),
|
||||||
|
"gas", r.block.GasUsed(),
|
||||||
|
"fees", feesInEther,
|
||||||
|
"root", r.block.Root(),
|
||||||
|
"elapsed", common.PrettyDuration(elapsed),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
payload.cond.Broadcast() // fire signal for notifying full block
|
payload.cond.Broadcast() // fire signal for notifying full block
|
||||||
}
|
}
|
||||||
@ -120,9 +130,9 @@ func (payload *Payload) Resolve() *engine.ExecutionPayloadEnvelope {
|
|||||||
close(payload.stop)
|
close(payload.stop)
|
||||||
}
|
}
|
||||||
if payload.full != nil {
|
if payload.full != nil {
|
||||||
return engine.BlockToExecutableData(payload.full, payload.fullFees, nil, nil, nil)
|
return engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars)
|
||||||
}
|
}
|
||||||
return engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil, nil, nil)
|
return engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveEmpty is basically identical to Resolve, but it expects empty block only.
|
// ResolveEmpty is basically identical to Resolve, but it expects empty block only.
|
||||||
@ -131,7 +141,7 @@ func (payload *Payload) ResolveEmpty() *engine.ExecutionPayloadEnvelope {
|
|||||||
payload.lock.Lock()
|
payload.lock.Lock()
|
||||||
defer payload.lock.Unlock()
|
defer payload.lock.Unlock()
|
||||||
|
|
||||||
return engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil, nil, nil)
|
return engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveFull is basically identical to Resolve, but it expects full block only.
|
// ResolveFull is basically identical to Resolve, but it expects full block only.
|
||||||
@ -157,7 +167,7 @@ func (payload *Payload) ResolveFull() *engine.ExecutionPayloadEnvelope {
|
|||||||
default:
|
default:
|
||||||
close(payload.stop)
|
close(payload.stop)
|
||||||
}
|
}
|
||||||
return engine.BlockToExecutableData(payload.full, payload.fullFees, nil, nil, nil)
|
return engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars)
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPayload builds the payload according to the provided parameters.
|
// buildPayload builds the payload according to the provided parameters.
|
||||||
@ -165,12 +175,12 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
// Build the initial version with no transaction included. It should be fast
|
// Build the initial version with no transaction included. It should be fast
|
||||||
// enough to run. The empty payload can at least make sure there is something
|
// enough to run. The empty payload can at least make sure there is something
|
||||||
// to deliver for not missing slot.
|
// to deliver for not missing slot.
|
||||||
empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, true)
|
empty := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, true)
|
||||||
if err != nil {
|
if empty.err != nil {
|
||||||
return nil, err
|
return nil, empty.err
|
||||||
}
|
}
|
||||||
// Construct a payload object for return.
|
// Construct a payload object for return.
|
||||||
payload := newPayload(empty, args.Id())
|
payload := newPayload(empty.block, args.Id())
|
||||||
|
|
||||||
// Spin up a routine for updating the payload in background. This strategy
|
// Spin up a routine for updating the payload in background. This strategy
|
||||||
// can maximum the revenue for including transactions with highest fee.
|
// can maximum the revenue for including transactions with highest fee.
|
||||||
@ -189,9 +199,9 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, false)
|
r := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, false)
|
||||||
if err == nil {
|
if r.err == nil {
|
||||||
payload.update(block, fees, time.Since(start))
|
payload.update(r, time.Since(start))
|
||||||
}
|
}
|
||||||
timer.Reset(w.recommit)
|
timer.Reset(w.recommit)
|
||||||
case <-payload.stop:
|
case <-payload.stop:
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
@ -89,6 +90,8 @@ type environment struct {
|
|||||||
header *types.Header
|
header *types.Header
|
||||||
txs []*types.Transaction
|
txs []*types.Transaction
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
|
sidecars []*types.BlobTxSidecar
|
||||||
|
blobs int
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy creates a deep copy of environment.
|
// copy creates a deep copy of environment.
|
||||||
@ -107,6 +110,10 @@ func (env *environment) copy() *environment {
|
|||||||
}
|
}
|
||||||
cpy.txs = make([]*types.Transaction, len(env.txs))
|
cpy.txs = make([]*types.Transaction, len(env.txs))
|
||||||
copy(cpy.txs, env.txs)
|
copy(cpy.txs, env.txs)
|
||||||
|
|
||||||
|
cpy.sidecars = make([]*types.BlobTxSidecar, len(env.sidecars))
|
||||||
|
copy(cpy.sidecars, env.sidecars)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,11 +148,12 @@ type newWorkReq struct {
|
|||||||
timestamp int64
|
timestamp int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPayloadResult represents a result struct corresponds to payload generation.
|
// newPayloadResult is the result of payload generation.
|
||||||
type newPayloadResult struct {
|
type newPayloadResult struct {
|
||||||
err error
|
err error
|
||||||
block *types.Block
|
block *types.Block
|
||||||
fees *big.Int
|
fees *big.Int // total block fees
|
||||||
|
sidecars []*types.BlobTxSidecar // collected blobs of blob transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
// getWorkReq represents a request for getting a new sealing work with provided parameters.
|
// getWorkReq represents a request for getting a new sealing work with provided parameters.
|
||||||
@ -516,12 +524,7 @@ func (w *worker) mainLoop() {
|
|||||||
w.commitWork(req.interrupt, req.timestamp)
|
w.commitWork(req.interrupt, req.timestamp)
|
||||||
|
|
||||||
case req := <-w.getWorkCh:
|
case req := <-w.getWorkCh:
|
||||||
block, fees, err := w.generateWork(req.params)
|
req.result <- w.generateWork(req.params)
|
||||||
req.result <- &newPayloadResult{
|
|
||||||
err: err,
|
|
||||||
block: block,
|
|
||||||
fees: fees,
|
|
||||||
}
|
|
||||||
|
|
||||||
case ev := <-w.txsCh:
|
case ev := <-w.txsCh:
|
||||||
// Apply transactions to the pending state if we're not sealing
|
// Apply transactions to the pending state if we're not sealing
|
||||||
@ -739,15 +742,29 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*
|
|||||||
snap = env.state.Snapshot()
|
snap = env.state.Snapshot()
|
||||||
gp = env.gasPool.Gas()
|
gp = env.gasPool.Gas()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Checking against blob gas limit: It's kind of ugly to perform this check here, but there
|
||||||
|
// isn't really a better place right now. The blob gas limit is checked at block validation time
|
||||||
|
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
||||||
|
// tx has too many blobs. So we have to explicitly check it here.
|
||||||
|
if (env.blobs+len(tx.BlobHashes()))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
|
||||||
|
return nil, errors.New("max data blobs reached")
|
||||||
|
}
|
||||||
|
|
||||||
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
env.state.RevertToSnapshot(snap)
|
||||||
env.gasPool.SetGas(gp)
|
env.gasPool.SetGas(gp)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
env.txs = append(env.txs, tx)
|
env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
|
||||||
env.receipts = append(env.receipts, receipt)
|
env.receipts = append(env.receipts, receipt)
|
||||||
|
|
||||||
|
if sc := tx.BlobTxSidecar(); sc != nil {
|
||||||
|
env.sidecars = append(env.sidecars, sc)
|
||||||
|
env.blobs += len(sc.Blobs)
|
||||||
|
}
|
||||||
|
|
||||||
return receipt.Logs, nil
|
return receipt.Logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -895,6 +912,16 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||||||
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
|
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if w.chainConfig.IsCancun(header.Number, header.Time) {
|
||||||
|
var excessBlobGas uint64
|
||||||
|
if w.chainConfig.IsCancun(parent.Number, parent.Time) {
|
||||||
|
excessBlobGas = eip4844.CalcExcessBlobGas(*parent.ExcessBlobGas, *parent.BlobGasUsed)
|
||||||
|
} else {
|
||||||
|
// For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0
|
||||||
|
excessBlobGas = eip4844.CalcExcessBlobGas(0, 0)
|
||||||
|
}
|
||||||
|
header.ExcessBlobGas = &excessBlobGas
|
||||||
|
}
|
||||||
// Run the consensus preparation with the default or customized consensus engine.
|
// Run the consensus preparation with the default or customized consensus engine.
|
||||||
if err := w.engine.Prepare(w.chain, header); err != nil {
|
if err := w.engine.Prepare(w.chain, header); err != nil {
|
||||||
log.Error("Failed to prepare header for sealing", "err", err)
|
log.Error("Failed to prepare header for sealing", "err", err)
|
||||||
@ -915,10 +942,9 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||||||
// into the given sealing block. The transaction selection and ordering strategy can
|
// into the given sealing block. The transaction selection and ordering strategy can
|
||||||
// be customized with the plugin in the future.
|
// be customized with the plugin in the future.
|
||||||
func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) error {
|
func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) error {
|
||||||
// Split the pending transactions into locals and remotes
|
|
||||||
// Fill the block with all available pending transactions.
|
|
||||||
pending := w.eth.TxPool().Pending(true)
|
pending := w.eth.TxPool().Pending(true)
|
||||||
|
|
||||||
|
// Split the pending transactions into locals and remotes.
|
||||||
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
||||||
for _, account := range w.eth.TxPool().Locals() {
|
for _, account := range w.eth.TxPool().Locals() {
|
||||||
if txs := remoteTxs[account]; len(txs) > 0 {
|
if txs := remoteTxs[account]; len(txs) > 0 {
|
||||||
@ -926,6 +952,8 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
|
|||||||
localTxs[account] = txs
|
localTxs[account] = txs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill the block with all available pending transactions.
|
||||||
if len(localTxs) > 0 {
|
if len(localTxs) > 0 {
|
||||||
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
|
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
|
||||||
if err := w.commitTransactions(env, txs, interrupt); err != nil {
|
if err := w.commitTransactions(env, txs, interrupt); err != nil {
|
||||||
@ -942,10 +970,10 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateWork generates a sealing block based on the given parameters.
|
// generateWork generates a sealing block based on the given parameters.
|
||||||
func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, error) {
|
func (w *worker) generateWork(params *generateParams) *newPayloadResult {
|
||||||
work, err := w.prepareWork(params)
|
work, err := w.prepareWork(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
defer work.discard()
|
defer work.discard()
|
||||||
|
|
||||||
@ -963,9 +991,13 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
|
|||||||
}
|
}
|
||||||
block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, nil, work.receipts, params.withdrawals)
|
block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, nil, work.receipts, params.withdrawals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return &newPayloadResult{err: err}
|
||||||
|
}
|
||||||
|
return &newPayloadResult{
|
||||||
|
block: block,
|
||||||
|
fees: totalFees(block, work.receipts),
|
||||||
|
sidecars: work.sidecars,
|
||||||
}
|
}
|
||||||
return block, totalFees(block, work.receipts), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// commitWork generates several new sealing tasks based on the parent block
|
// commitWork generates several new sealing tasks based on the parent block
|
||||||
@ -1074,7 +1106,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
|||||||
// getSealingBlock generates the sealing block based on the given parameters.
|
// getSealingBlock generates the sealing block based on the given parameters.
|
||||||
// The generation result will be passed back via the given channel no matter
|
// The generation result will be passed back via the given channel no matter
|
||||||
// the generation itself succeeds or not.
|
// the generation itself succeeds or not.
|
||||||
func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) (*types.Block, *big.Int, error) {
|
func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) *newPayloadResult {
|
||||||
req := &getWorkReq{
|
req := &getWorkReq{
|
||||||
params: &generateParams{
|
params: &generateParams{
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
@ -1089,13 +1121,9 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase
|
|||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case w.getWorkCh <- req:
|
case w.getWorkCh <- req:
|
||||||
result := <-req.result
|
return <-req.result
|
||||||
if result.err != nil {
|
|
||||||
return nil, nil, result.err
|
|
||||||
}
|
|
||||||
return result.block, result.fees, nil
|
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
return nil, nil, errors.New("miner closed")
|
return &newPayloadResult{err: errors.New("miner closed")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,32 +452,32 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
|
|
||||||
// This API should work even when the automatic sealing is not enabled
|
// This API should work even when the automatic sealing is not enabled
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
r := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
||||||
if c.expectErr {
|
if c.expectErr {
|
||||||
if err == nil {
|
if r.err == nil {
|
||||||
t.Error("Expect error but get nil")
|
t.Error("Expect error but get nil")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if r.err != nil {
|
||||||
t.Errorf("Unexpected error %v", err)
|
t.Errorf("Unexpected error %v", r.err)
|
||||||
}
|
}
|
||||||
assertBlock(block, c.expectNumber, c.coinbase, c.random)
|
assertBlock(r.block, c.expectNumber, c.coinbase, c.random)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This API should work even when the automatic sealing is enabled
|
// This API should work even when the automatic sealing is enabled
|
||||||
w.start()
|
w.start()
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
r := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
||||||
if c.expectErr {
|
if c.expectErr {
|
||||||
if err == nil {
|
if r.err == nil {
|
||||||
t.Error("Expect error but get nil")
|
t.Error("Expect error but get nil")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if r.err != nil {
|
||||||
t.Errorf("Unexpected error %v", err)
|
t.Errorf("Unexpected error %v", r.err)
|
||||||
}
|
}
|
||||||
assertBlock(block, c.expectNumber, c.coinbase, c.random)
|
assertBlock(r.block, c.expectNumber, c.coinbase, c.random)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ const (
|
|||||||
BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element
|
BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element
|
||||||
BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob
|
BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob
|
||||||
BlobTxHashVersion = 0x01 // Version byte of the commitment hash
|
BlobTxHashVersion = 0x01 // Version byte of the commitment hash
|
||||||
BlobTxMaxBlobGasPerBlock = 1 << 19 // Maximum consumable blob gas for data blobs per block
|
MaxBlobGasPerBlock = 1 << 19 // Maximum consumable blob gas for data blobs per block
|
||||||
BlobTxTargetBlobGasPerBlock = 1 << 18 // Target consumable blob gas for data blobs per block (for 1559-like pricing)
|
BlobTxTargetBlobGasPerBlock = 1 << 18 // Target consumable blob gas for data blobs per block (for 1559-like pricing)
|
||||||
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
|
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
|
||||||
BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs
|
BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs
|
||||||
|
Loading…
Reference in New Issue
Block a user