Improve things

This commit is contained in:
Ferran Borreguero 2024-02-21 10:38:29 +00:00
parent 1a5fe3fec1
commit 798d625a6d
4 changed files with 198 additions and 127 deletions

View file

@ -5,9 +5,12 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
//go:generate go run github.com/fjl/gencodec -type BuildBlockArgs -field-override buildBlockArgsMarshaling -out gen_buildblockargs_json.go
type Bundle struct {
BlockNumber *big.Int `json:"blockNumber,omitempty"` // if BlockNumber is set it must match DecryptionCondition!
MaxBlock *big.Int `json:"maxBlock,omitempty"`
@ -17,16 +20,24 @@ type Bundle struct {
}
type BuildBlockArgs struct {
Slot uint64
ProposerPubkey []byte
Parent common.Hash
Timestamp uint64
FeeRecipient common.Address
GasLimit uint64
Random common.Hash
Withdrawals []*types.Withdrawal
Extra []byte
FillPending bool
Slot uint64 `json:"slot"`
ProposerPubkey []byte `json:"proposerPubkey"`
Parent common.Hash `json:"parent"`
Timestamp uint64 `json:"timestamp"`
FeeRecipient common.Address `json:"feeRecipient"`
GasLimit uint64 `json:"gasLimit"`
Random common.Hash `json:"random"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
Extra []byte `json:"extra"`
}
// field type overrides for gencodec
type buildBlockArgsMarshaling struct {
Slot hexutil.Uint64
ProposerPubkey hexutil.Bytes
Timestamp hexutil.Uint64
GasLimit hexutil.Uint64
Extra hexutil.Bytes
}
type API interface {

View file

@ -0,0 +1,86 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package api
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = (*buildBlockArgsMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (b BuildBlockArgs) MarshalJSON() ([]byte, error) {
type BuildBlockArgs struct {
Slot hexutil.Uint64 `json:"slot"`
ProposerPubkey hexutil.Bytes `json:"proposerPubkey"`
Parent common.Hash `json:"parent"`
Timestamp hexutil.Uint64 `json:"timestamp"`
FeeRecipient common.Address `json:"feeRecipient"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
Random common.Hash `json:"random"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
Extra hexutil.Bytes `json:"extra"`
}
var enc BuildBlockArgs
enc.Slot = hexutil.Uint64(b.Slot)
enc.ProposerPubkey = b.ProposerPubkey
enc.Parent = b.Parent
enc.Timestamp = hexutil.Uint64(b.Timestamp)
enc.FeeRecipient = b.FeeRecipient
enc.GasLimit = hexutil.Uint64(b.GasLimit)
enc.Random = b.Random
enc.Withdrawals = b.Withdrawals
enc.Extra = b.Extra
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (b *BuildBlockArgs) UnmarshalJSON(input []byte) error {
type BuildBlockArgs struct {
Slot *hexutil.Uint64 `json:"slot"`
ProposerPubkey *hexutil.Bytes `json:"proposerPubkey"`
Parent *common.Hash `json:"parent"`
Timestamp *hexutil.Uint64 `json:"timestamp"`
FeeRecipient *common.Address `json:"feeRecipient"`
GasLimit *hexutil.Uint64 `json:"gasLimit"`
Random *common.Hash `json:"random"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
Extra *hexutil.Bytes `json:"extra"`
}
var dec BuildBlockArgs
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Slot != nil {
b.Slot = uint64(*dec.Slot)
}
if dec.ProposerPubkey != nil {
b.ProposerPubkey = *dec.ProposerPubkey
}
if dec.Parent != nil {
b.Parent = *dec.Parent
}
if dec.Timestamp != nil {
b.Timestamp = uint64(*dec.Timestamp)
}
if dec.FeeRecipient != nil {
b.FeeRecipient = *dec.FeeRecipient
}
if dec.GasLimit != nil {
b.GasLimit = uint64(*dec.GasLimit)
}
if dec.Random != nil {
b.Random = *dec.Random
}
if dec.Withdrawals != nil {
b.Withdrawals = dec.Withdrawals
}
if dec.Extra != nil {
b.Extra = *dec.Extra
}
return nil
}

View file

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
@ -44,11 +45,12 @@ type SessionManager struct {
sessions map[string]*miner.Builder
sessionTimers map[string]*time.Timer
sessionsLock sync.RWMutex
blockchain blockchain
blockchain *core.BlockChain
pool *txpool.TxPool
config *Config
}
func NewSessionManager(blockchain blockchain, config *Config) *SessionManager {
func NewSessionManager(blockchain *core.BlockChain, pool *txpool.TxPool, config *Config) *SessionManager {
if config.GasCeil == 0 {
config.GasCeil = 1000000000000000000
}
@ -70,12 +72,24 @@ func NewSessionManager(blockchain blockchain, config *Config) *SessionManager {
sessionTimers: make(map[string]*time.Timer),
blockchain: blockchain,
config: config,
pool: pool,
}
return s
}
func (s *SessionManager) BlockChain() *core.BlockChain {
return s.blockchain
}
func (s *SessionManager) TxPool() *txpool.TxPool {
return s.pool
}
// NewSession creates a new builder session and returns the session id
func (s *SessionManager) NewSession(ctx context.Context, args *api.BuildBlockArgs) (string, error) {
if args == nil {
return "", fmt.Errorf("args cannot be nil")
}
// Wait for session to become available
select {
case <-s.sem:
@ -88,11 +102,17 @@ func (s *SessionManager) NewSession(ctx context.Context, args *api.BuildBlockArg
builderCfg := &miner.BuilderConfig{
ChainConfig: s.blockchain.Config(),
Engine: s.blockchain.Engine(),
// TODO
Chain: s.blockchain,
EthBackend: s,
GasCeil: s.config.GasCeil,
}
builderArgs := &miner.BuilderArgs{
ParentHash: args.Parent,
ParentHash: args.Parent,
FeeRecipient: args.FeeRecipient,
ProposerPubkey: args.ProposerPubkey,
Extra: args.Extra,
Slot: args.Slot,
}
id := uuid.New().String()[:7]

View file

@ -2,18 +2,22 @@ package builder
import (
"context"
"crypto/ecdsa"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/suave/builder/api"
"github.com/stretchr/testify/require"
)
@ -22,7 +26,9 @@ func TestSessionManager_SessionTimeout(t *testing.T) {
SessionIdleTimeout: 500 * time.Millisecond,
})
id, err := mngr.NewSession(context.TODO(), nil)
args := &api.BuildBlockArgs{}
id, err := mngr.NewSession(context.TODO(), args)
require.NoError(t, err)
time.Sleep(1 * time.Second)
@ -35,6 +41,7 @@ func TestSessionManager_MaxConcurrentSessions(t *testing.T) {
t.Parallel()
const d = time.Millisecond * 100
args := &api.BuildBlockArgs{}
mngr, _ := newSessionManager(t, &Config{
MaxConcurrentSessions: 1,
@ -42,7 +49,7 @@ func TestSessionManager_MaxConcurrentSessions(t *testing.T) {
})
t.Run("SessionAvailable", func(t *testing.T) {
sess, err := mngr.NewSession(context.TODO(), nil)
sess, err := mngr.NewSession(context.TODO(), args)
require.NoError(t, err)
require.NotZero(t, sess)
})
@ -53,7 +60,7 @@ func TestSessionManager_MaxConcurrentSessions(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
sess, err := mngr.NewSession(ctx, nil)
sess, err := mngr.NewSession(ctx, args)
require.Zero(t, sess)
require.ErrorIs(t, err, context.Canceled)
})
@ -62,7 +69,7 @@ func TestSessionManager_MaxConcurrentSessions(t *testing.T) {
time.Sleep(d) // Wait for the session to expire.
// We should be able to open a session again.
sess, err := mngr.NewSession(context.TODO(), nil)
sess, err := mngr.NewSession(context.TODO(), args)
require.NoError(t, err)
require.NotZero(t, sess)
})
@ -73,7 +80,8 @@ func TestSessionManager_SessionRefresh(t *testing.T) {
SessionIdleTimeout: 500 * time.Millisecond,
})
id, err := mngr.NewSession(context.TODO(), nil)
args := &api.BuildBlockArgs{}
id, err := mngr.NewSession(context.TODO(), args)
require.NoError(t, err)
// if we query the session under the idle timeout,
@ -98,124 +106,70 @@ func TestSessionManager_StartSession(t *testing.T) {
// test that the session starts and it can simulate transactions
mngr, bMock := newSessionManager(t, &Config{})
id, err := mngr.NewSession(context.TODO(), nil)
args := &api.BuildBlockArgs{}
id, err := mngr.NewSession(context.TODO(), args)
require.NoError(t, err)
txn := bMock.state.newTransfer(t, common.Address{}, big.NewInt(1))
txn := bMock.newTransfer(t, common.Address{}, big.NewInt(1))
receipt, err := mngr.AddTransaction(id, txn)
require.NoError(t, err)
require.NotNil(t, receipt)
}
func newSessionManager(t *testing.T, cfg *Config) (*SessionManager, *blockchainMock) {
func newSessionManager(t *testing.T, cfg *Config) (*SessionManager, *testBackend) {
backend := newTestBackend(t)
if cfg == nil {
cfg = &Config{}
}
state := newMockState(t)
bMock := &blockchainMock{
state: state,
}
return NewSessionManager(bMock, cfg), bMock
return NewSessionManager(backend.chain, backend.pool, cfg), backend
}
type blockchainMock struct {
state *mockState
var (
testBankKey, _ = crypto.GenerateKey()
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
)
type testBackend struct {
chain *core.BlockChain
pool *txpool.TxPool
}
func (b *blockchainMock) Engine() consensus.Engine {
panic("TODO")
}
func (b *blockchainMock) GetHeader(common.Hash, uint64) *types.Header {
panic("TODO")
}
func (b *blockchainMock) Config() *params.ChainConfig {
return b.state.chainConfig
}
func (b *blockchainMock) CurrentHeader() *types.Header {
return &types.Header{
Number: big.NewInt(1),
Difficulty: big.NewInt(1),
Root: b.state.stateRoot,
}
}
func (b *blockchainMock) StateAt(root common.Hash) (*state.StateDB, error) {
return b.state.stateAt(root)
}
type mockState struct {
stateRoot common.Hash
statedb state.Database
premineKey *ecdsa.PrivateKey
premineKeyAdd common.Address
nextNonce uint64 // figure out a better way
signer types.Signer
chainConfig *params.ChainConfig
}
func newMockState(t *testing.T) *mockState {
premineKey, _ := crypto.GenerateKey() // TODO: it would be nice to have it deterministic
premineKeyAddr := crypto.PubkeyToAddress(premineKey.PublicKey)
// create a state reference with at least one premined account
// In order to test the statedb in isolation, we are going
// to commit this pre-state to a memory database
db := state.NewDatabase(rawdb.NewMemoryDatabase())
preState, err := state.New(types.EmptyRootHash, db, nil)
require.NoError(t, err)
preState.AddBalance(premineKeyAddr, big.NewInt(1000000000000000000))
root, err := preState.Commit(1, true)
require.NoError(t, err)
// for the sake of this test, we only need all the forks enabled
chainConfig := params.TestChainConfig
// Disable london so that we do not check gasFeeCap (TODO: Fix)
chainConfig.LondonBlock = big.NewInt(100)
return &mockState{
statedb: db,
stateRoot: root,
premineKey: premineKey,
premineKeyAdd: premineKeyAddr,
signer: types.NewEIP155Signer(chainConfig.ChainID),
chainConfig: chainConfig,
}
}
func (m *mockState) stateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, m.statedb, nil)
}
func (m *mockState) getNonce() uint64 {
next := m.nextNonce
m.nextNonce++
return next
}
func (m *mockState) newTransfer(t *testing.T, to common.Address, amount *big.Int) *types.Transaction {
tx := types.NewTransaction(m.getNonce(), to, amount, 1000000, big.NewInt(1), nil)
return m.newTxn(t, tx)
}
func (m *mockState) newTxn(t *testing.T, tx *types.Transaction) *types.Transaction {
// sign the transaction
signature, err := crypto.Sign(m.signer.Hash(tx).Bytes(), m.premineKey)
require.NoError(t, err)
// include the signature in the transaction
tx, err = tx.WithSignature(m.signer, signature)
require.NoError(t, err)
func (tb *testBackend) newTransfer(t *testing.T, to common.Address, amount *big.Int) *types.Transaction {
gasPrice := big.NewInt(10 * params.InitialBaseFee)
tx, _ := types.SignTx(types.NewTransaction(tb.pool.Nonce(testBankAddress), to, amount, params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey)
return tx
}
func newTestBackend(t *testing.T) *testBackend {
// code based on miner 'newTestWorker'
testTxPoolConfig := legacypool.DefaultConfig
testTxPoolConfig.Journal = ""
var (
db = rawdb.NewMemoryDatabase()
config = *params.AllCliqueProtocolChanges
)
config.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
engine := clique.New(config.Clique, db)
var gspec = &core.Genesis{
Config: &config,
Alloc: core.GenesisAlloc{testBankAddress: {Balance: big.NewInt(1000000000000000000)}},
}
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
engine.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), testBankKey)
})
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("core.NewBlockChain failed: %v", err)
}
pool := legacypool.New(testTxPoolConfig, chain)
txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), chain, []txpool.SubPool{pool})
return &testBackend{chain: chain, pool: txpool}
}