mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
AA-381: Create a "pull bundle" block building mode and a 'getRip7560Bundle' method (#18)
This commit is contained in:
parent
2ee6caf9c5
commit
0d8f19200d
9 changed files with 131 additions and 10 deletions
5
circleciconfig.toml
Normal file
5
circleciconfig.toml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
[Eth]
|
||||||
|
Rip7560MaxBundleSize = 0
|
||||||
|
Rip7560MaxBundleGas = 0
|
||||||
|
Rip7560PullUrls = ["http://localhost:3001/rpc"]
|
||||||
|
Rip7560AcceptPush = false
|
||||||
|
|
@ -1,21 +1,30 @@
|
||||||
package rip7560pool
|
package rip7560pool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MaxBundleSize uint
|
MaxBundleSize *uint64
|
||||||
MaxBundleGas uint
|
MaxBundleGas *uint64
|
||||||
|
PullUrls []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rip7560BundlerPool is the transaction pool dedicated to RIP-7560 AA transactions.
|
// Rip7560BundlerPool is the transaction pool dedicated to RIP-7560 AA transactions.
|
||||||
|
|
@ -189,7 +198,10 @@ func (pool *Rip7560BundlerPool) PendingRip7560Bundle() (*types.ExternallyReceive
|
||||||
defer pool.mu.Unlock()
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
bundle := pool.selectExternalBundle()
|
bundle := pool.selectExternalBundle()
|
||||||
return bundle, nil
|
if bundle != nil {
|
||||||
|
return bundle, nil
|
||||||
|
}
|
||||||
|
return pool.fetchBundleFromBundler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeTransactions is not needed for the External Bundler AA sub pool and 'ch' will never be sent anything.
|
// SubscribeTransactions is not needed for the External Bundler AA sub pool and 'ch' will never be sent anything.
|
||||||
|
|
@ -261,6 +273,64 @@ func (pool *Rip7560BundlerPool) GetRip7560BundleStatus(hash common.Hash) (*types
|
||||||
return pool.includedBundles[hash], nil
|
return pool.includedBundles[hash], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetRip7560BundleArgs struct {
|
||||||
|
MinBaseFee uint64
|
||||||
|
MaxBundleGas uint64
|
||||||
|
MaxBundleSize uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetRip7560BundleResult struct {
|
||||||
|
Bundle []ethapi.TransactionArgs
|
||||||
|
ValidForBlock *hexutil.Big
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *Rip7560BundlerPool) fetchBundleFromBundler() (*types.ExternallyReceivedBundle, error) {
|
||||||
|
if len(pool.config.PullUrls) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
currentHead := pool.currentHead.Load()
|
||||||
|
chosenBundle := make([]ethapi.TransactionArgs, 0)
|
||||||
|
pullErrors := make([]error, 0)
|
||||||
|
for _, url := range pool.config.PullUrls {
|
||||||
|
client := rpc.WithHTTPClient(&http.Client{Timeout: 500 * time.Millisecond})
|
||||||
|
cl, err := rpc.DialOptions(context.Background(), url, client)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to dial RIP-7560 bundler URL (%s): %v", url, err))
|
||||||
|
}
|
||||||
|
maxBundleGas := min(*pool.config.MaxBundleGas, currentHead.GasLimit)
|
||||||
|
args := &GetRip7560BundleArgs{
|
||||||
|
MinBaseFee: currentHead.BaseFee.Uint64(), // todo: adjust to account for possible change!
|
||||||
|
MaxBundleGas: maxBundleGas,
|
||||||
|
MaxBundleSize: *pool.config.MaxBundleSize,
|
||||||
|
}
|
||||||
|
result := &GetRip7560BundleResult{
|
||||||
|
Bundle: make([]ethapi.TransactionArgs, 0),
|
||||||
|
}
|
||||||
|
err = cl.Call(result, "aa_getRip7560Bundle", args)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to fetch RIP-7560 bundle from URL (%s): %v", url, err))
|
||||||
|
pullErrors = append(pullErrors, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
chosenBundle = result.Bundle
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(pullErrors) == len(pool.config.PullUrls) {
|
||||||
|
return nil, errors.New("failed to fetch a new RIP-7560 bundle from any bundler")
|
||||||
|
}
|
||||||
|
txs := make([]*types.Transaction, len(chosenBundle))
|
||||||
|
for i, tx := range chosenBundle {
|
||||||
|
txs[i] = tx.ToTransaction()
|
||||||
|
}
|
||||||
|
bundleHash := ethapi.CalculateBundleHash(txs)
|
||||||
|
return &types.ExternallyReceivedBundle{
|
||||||
|
BundlerId: "result.String",
|
||||||
|
BundleHash: bundleHash,
|
||||||
|
ValidForBlock: big.NewInt(0),
|
||||||
|
Transactions: txs,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// return first bundle
|
// return first bundle
|
||||||
func (pool *Rip7560BundlerPool) selectExternalBundle() *types.ExternallyReceivedBundle {
|
func (pool *Rip7560BundlerPool) selectExternalBundle() *types.ExternallyReceivedBundle {
|
||||||
if len(pool.pendingBundles) == 0 {
|
if len(pool.pendingBundles) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubmitBundle inserts the entire bundle of Type 4 transactions into the relevant pool.
|
// SubmitRip7560Bundle inserts the entire bundle of Type 4 transactions into the relevant pool.
|
||||||
func (p *TxPool) SubmitRip7560Bundle(bundle *types.ExternallyReceivedBundle) error {
|
func (p *TxPool) SubmitRip7560Bundle(bundle *types.ExternallyReceivedBundle) error {
|
||||||
// todo: we cannot 'filter-out' the AA pool so just passing to all pools - only AA pool has code in SubmitBundle
|
// todo: we cannot 'filter-out' the AA pool so just passing to all pools - only AA pool has code in SubmitBundle
|
||||||
for _, subpool := range p.subpools {
|
for _, subpool := range p.subpools {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import (
|
||||||
|
|
||||||
// EthAPIBackend implements ethapi.Backend and tracers.Backend for full nodes
|
// EthAPIBackend implements ethapi.Backend and tracers.Backend for full nodes
|
||||||
type EthAPIBackend struct {
|
type EthAPIBackend struct {
|
||||||
|
rip7560AcceptPush bool
|
||||||
extRPCEnabled bool
|
extRPCEnabled bool
|
||||||
allowUnprotectedTxs bool
|
allowUnprotectedTxs bool
|
||||||
eth *Ethereum
|
eth *Ethereum
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,15 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *EthAPIBackend) SubmitRip7560Bundle(bundle *types.ExternallyReceivedBundle) error {
|
func (b *EthAPIBackend) SubmitRip7560Bundle(bundle *types.ExternallyReceivedBundle) error {
|
||||||
|
if !b.rip7560AcceptPush {
|
||||||
|
return errors.New("illegal call to eth_sendRip7560TransactionsBundle: Config.Eth.Rip7560AcceptPush is not set")
|
||||||
|
}
|
||||||
return b.eth.txPool.SubmitRip7560Bundle(bundle)
|
return b.eth.txPool.SubmitRip7560Bundle(bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -238,8 +238,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
legacyPool := legacypool.New(config.TxPool, eth.blockchain)
|
legacyPool := legacypool.New(config.TxPool, eth.blockchain)
|
||||||
|
|
||||||
rip7560PoolConfig := rip7560pool.Config{
|
rip7560PoolConfig := rip7560pool.Config{
|
||||||
MaxBundleGas: 10000000,
|
MaxBundleGas: config.Rip7560MaxBundleGas,
|
||||||
MaxBundleSize: 100,
|
MaxBundleSize: config.Rip7560MaxBundleSize,
|
||||||
|
PullUrls: config.Rip7560PullUrls,
|
||||||
}
|
}
|
||||||
rip7560 := rip7560pool.New(rip7560PoolConfig, eth.blockchain, config.Miner.Etherbase)
|
rip7560 := rip7560pool.New(rip7560PoolConfig, eth.blockchain, config.Miner.Etherbase)
|
||||||
|
|
||||||
|
|
@ -266,7 +267,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
eth.miner = miner.New(eth, config.Miner, eth.engine)
|
eth.miner = miner.New(eth, config.Miner, eth.engine)
|
||||||
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
||||||
|
|
||||||
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
eth.APIBackend = &EthAPIBackend{config.Rip7560AcceptPush, stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
||||||
if eth.APIBackend.allowUnprotectedTxs {
|
if eth.APIBackend.allowUnprotectedTxs {
|
||||||
log.Info("Unprotected transactions allowed")
|
log.Info("Unprotected transactions allowed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,18 @@ type Config struct {
|
||||||
|
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Rip7560MaxBundleGas is the maximum amount of gas that can be used by an RIP-7560 bundle
|
||||||
|
Rip7560MaxBundleGas *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Rip7560MaxBundleSize is the maximum number of transactions an RIP-7560 bundle can contain
|
||||||
|
Rip7560MaxBundleSize *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Rip7560PullUrls provides a list of bundlers the node will ask for new bundles for each block
|
||||||
|
Rip7560PullUrls []string
|
||||||
|
|
||||||
|
// Rip7560AcceptPush when set to "true" the node will accept incoming 'eth_sendRip7560TransactionsBundle'
|
||||||
|
Rip7560AcceptPush bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
RPCTxFeeCap float64
|
RPCTxFeeCap float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560MaxBundleGas *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560MaxBundleSize *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560PullUrls []string
|
||||||
|
Rip7560AcceptPush bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
var enc Config
|
var enc Config
|
||||||
enc.Genesis = c.Genesis
|
enc.Genesis = c.Genesis
|
||||||
|
|
@ -101,6 +105,10 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
||||||
enc.OverrideCancun = c.OverrideCancun
|
enc.OverrideCancun = c.OverrideCancun
|
||||||
enc.OverrideVerkle = c.OverrideVerkle
|
enc.OverrideVerkle = c.OverrideVerkle
|
||||||
|
enc.Rip7560MaxBundleGas = c.Rip7560MaxBundleGas
|
||||||
|
enc.Rip7560MaxBundleSize = c.Rip7560MaxBundleSize
|
||||||
|
enc.Rip7560PullUrls = c.Rip7560PullUrls
|
||||||
|
enc.Rip7560AcceptPush = c.Rip7560AcceptPush
|
||||||
return &enc, nil
|
return &enc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,6 +156,10 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
RPCTxFeeCap *float64
|
RPCTxFeeCap *float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560MaxBundleGas *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560MaxBundleSize *uint64 `toml:",omitempty"`
|
||||||
|
Rip7560PullUrls []string
|
||||||
|
Rip7560AcceptPush *bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
var dec Config
|
var dec Config
|
||||||
if err := unmarshal(&dec); err != nil {
|
if err := unmarshal(&dec); err != nil {
|
||||||
|
|
@ -276,5 +288,17 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.OverrideVerkle != nil {
|
if dec.OverrideVerkle != nil {
|
||||||
c.OverrideVerkle = dec.OverrideVerkle
|
c.OverrideVerkle = dec.OverrideVerkle
|
||||||
}
|
}
|
||||||
|
if dec.Rip7560MaxBundleGas != nil {
|
||||||
|
c.Rip7560MaxBundleGas = dec.Rip7560MaxBundleGas
|
||||||
|
}
|
||||||
|
if dec.Rip7560MaxBundleSize != nil {
|
||||||
|
c.Rip7560MaxBundleSize = dec.Rip7560MaxBundleSize
|
||||||
|
}
|
||||||
|
if dec.Rip7560PullUrls != nil {
|
||||||
|
c.Rip7560PullUrls = dec.Rip7560PullUrls
|
||||||
|
}
|
||||||
|
if dec.Rip7560AcceptPush != nil {
|
||||||
|
c.Rip7560AcceptPush = *dec.Rip7560AcceptPush
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ func (s *TransactionAPI) SendRip7560TransactionsBundle(ctx context.Context, args
|
||||||
ValidForBlock: creationBlock,
|
ValidForBlock: creationBlock,
|
||||||
Transactions: txs,
|
Transactions: txs,
|
||||||
}
|
}
|
||||||
bundleHash := calculateBundleHash(txs)
|
bundleHash := CalculateBundleHash(txs)
|
||||||
bundle.BundleHash = bundleHash
|
bundle.BundleHash = bundleHash
|
||||||
err := SubmitRip7560Bundle(ctx, s.b, bundle)
|
err := SubmitRip7560Bundle(ctx, s.b, bundle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -37,15 +37,19 @@ func (s *TransactionAPI) GetRip7560BundleStatus(ctx context.Context, hash common
|
||||||
return bundleStats, err
|
return bundleStats, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateBundleHash
|
||||||
// TODO: If this code is indeed necessary, keep it in utils; better - remove altogether.
|
// TODO: If this code is indeed necessary, keep it in utils; better - remove altogether.
|
||||||
func calculateBundleHash(txs []*types.Transaction) common.Hash {
|
func CalculateBundleHash(txs []*types.Transaction) common.Hash {
|
||||||
appendedTxIds := make([]byte, 0)
|
appendedTxIds := make([]byte, 0)
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
txHash := tx.Hash()
|
txHash := tx.Hash()
|
||||||
appendedTxIds = append(appendedTxIds, txHash[:]...)
|
appendedTxIds = append(appendedTxIds, txHash[:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rlpHash(appendedTxIds)
|
bundleHash := rlpHash(appendedTxIds)
|
||||||
|
println("calculateBundleHash")
|
||||||
|
println(bundleHash.String())
|
||||||
|
return bundleHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue