Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.

Commit e4dfa84

Browse files
dvushavalonche
authored andcommitted
New payment tx (#40)
* Rework payment tx * move env access from worker * add builder.dry-run * Move proposer tx from fillTransactions * Use one flag for validation blocklist
1 parent eb3dfc8 commit e4dfa84

14 files changed

+305
-122
lines changed

builder/builder.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package builder
33
import (
44
"context"
55
"errors"
6+
blockvalidation "github.com/ethereum/go-ethereum/eth/block-validation"
67
"golang.org/x/time/rate"
78
"math/big"
89
_ "os"
@@ -49,6 +50,8 @@ type Builder struct {
4950
ds flashbotsextra.IDatabaseService
5051
relay IRelay
5152
eth IEthereumService
53+
dryRun bool
54+
validator *blockvalidation.BlockValidationAPI
5255
builderSecretKey *bls.SecretKey
5356
builderPublicKey boostTypes.PublicKey
5457
builderSigningDomain boostTypes.Domain
@@ -62,7 +65,7 @@ type Builder struct {
6265
slotCtxCancel context.CancelFunc
6366
}
6467

65-
func NewBuilder(sk *bls.SecretKey, ds flashbotsextra.IDatabaseService, relay IRelay, builderSigningDomain boostTypes.Domain, eth IEthereumService) *Builder {
68+
func NewBuilder(sk *bls.SecretKey, ds flashbotsextra.IDatabaseService, relay IRelay, builderSigningDomain boostTypes.Domain, eth IEthereumService, dryRun bool, validator *blockvalidation.BlockValidationAPI) *Builder {
6669
pkBytes := bls.PublicKeyFromSecretKey(sk).Compress()
6770
pk := boostTypes.PublicKey{}
6871
pk.FromSlice(pkBytes)
@@ -72,6 +75,8 @@ func NewBuilder(sk *bls.SecretKey, ds flashbotsextra.IDatabaseService, relay IRe
7275
ds: ds,
7376
relay: relay,
7477
eth: eth,
78+
dryRun: dryRun,
79+
validator: validator,
7580
builderSecretKey: sk,
7681
builderPublicKey: pk,
7782
builderSigningDomain: builderSigningDomain,
@@ -118,8 +123,6 @@ func (b *Builder) onSealedBlock(block *types.Block, bundles []types.SimulatedBun
118123
Value: *value,
119124
}
120125

121-
go b.ds.ConsumeBuiltBlock(block, bundles, &blockBidMsg)
122-
123126
signature, err := boostTypes.SignMessage(&blockBidMsg, b.builderSigningDomain, b.builderSecretKey)
124127
if err != nil {
125128
log.Error("could not sign builder bid", "err", err)
@@ -132,10 +135,18 @@ func (b *Builder) onSealedBlock(block *types.Block, bundles []types.SimulatedBun
132135
ExecutionPayload: payload,
133136
}
134137

135-
err = b.relay.SubmitBlock(&blockSubmitReq)
136-
if err != nil {
137-
log.Error("could not submit block", "err", err, "bundles", len(bundles))
138-
return err
138+
if b.dryRun {
139+
err = b.validator.ValidateBuilderSubmissionV1(&blockSubmitReq)
140+
if err != nil {
141+
log.Error("could not validate block", "err", err)
142+
}
143+
} else {
144+
go b.ds.ConsumeBuiltBlock(block, bundles, &blockBidMsg)
145+
err = b.relay.SubmitBlock(&blockSubmitReq)
146+
if err != nil {
147+
log.Error("could not submit block", "err", err, "bundles", len(bundles))
148+
return err
149+
}
139150
}
140151

141152
log.Info("submitted block", "slot", blockBidMsg.Slot, "value", blockBidMsg.Value.String(), "parent", blockBidMsg.ParentHash, "hash", block.Hash(), "bundles", len(bundles))

builder/builder_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestOnPayloadAttributes(t *testing.T) {
7575

7676
testEthService := &testEthereumService{synced: true, testExecutableData: testExecutableData, testBlock: testBlock}
7777

78-
builder := NewBuilder(sk, flashbotsextra.NilDbService{}, &testRelay, bDomain, testEthService)
78+
builder := NewBuilder(sk, flashbotsextra.NilDbService{}, &testRelay, bDomain, testEthService, false, nil)
7979
builder.Start()
8080
defer builder.Stop()
8181

builder/local_relay_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func newTestBackend(t *testing.T, forkchoiceData *beacon.ExecutableDataV1, block
3131
beaconClient := &testBeaconClient{validator: validator}
3232
localRelay := NewLocalRelay(sk, beaconClient, bDomain, cDomain, ForkData{}, true)
3333
ethService := &testEthereumService{synced: true, testExecutableData: forkchoiceData, testBlock: block}
34-
backend := NewBuilder(sk, flashbotsextra.NilDbService{}, localRelay, bDomain, ethService)
34+
backend := NewBuilder(sk, flashbotsextra.NilDbService{}, localRelay, bDomain, ethService, false, nil)
3535
// service := NewService("127.0.0.1:31545", backend)
3636

3737
backend.limiter = rate.NewLimiter(rate.Inf, 0)

builder/service.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package builder
33
import (
44
"errors"
55
"fmt"
6+
blockvalidation "github.com/ethereum/go-ethereum/eth/block-validation"
67
"net/http"
78
"os"
89

@@ -106,6 +107,7 @@ type BuilderConfig struct {
106107
Enabled bool
107108
EnableValidatorChecks bool
108109
EnableLocalRelay bool
110+
DryRun bool
109111
BuilderSecretKey string
110112
RelaySecretKey string
111113
ListenAddr string
@@ -114,6 +116,7 @@ type BuilderConfig struct {
114116
GenesisValidatorsRoot string
115117
BeaconEndpoint string
116118
RemoteRelayEndpoint string
119+
ValidationBlocklist string
117120
}
118121

119122
func Register(stack *node.Node, backend *eth.Ethereum, cfg *BuilderConfig) error {
@@ -172,6 +175,18 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *BuilderConfig) error
172175
return errors.New("neither local nor remote relay specified")
173176
}
174177

178+
var validator *blockvalidation.BlockValidationAPI
179+
if cfg.DryRun {
180+
var accessVerifier *blockvalidation.AccessVerifier
181+
if cfg.ValidationBlocklist != "" {
182+
accessVerifier, err = blockvalidation.NewAccessVerifierFromFile(cfg.ValidationBlocklist)
183+
if err != nil {
184+
return fmt.Errorf("failed to load validation blocklist %w", err)
185+
}
186+
}
187+
validator = blockvalidation.NewBlockValidationAPI(backend, accessVerifier)
188+
}
189+
175190
// TODO: move to proper flags
176191
var ds flashbotsextra.IDatabaseService
177192
dbDSN := os.Getenv("FLASHBOTS_POSTGRES_DSN")
@@ -193,7 +208,7 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *BuilderConfig) error
193208
go bundleFetcher.Run()
194209

195210
ethereumService := NewEthereumService(backend)
196-
builderBackend := NewBuilder(builderSk, ds, relay, builderSigningDomain, ethereumService)
211+
builderBackend := NewBuilder(builderSk, ds, relay, builderSigningDomain, ethereumService, cfg.DryRun, validator)
197212
builderService := NewService(cfg.ListenAddr, localRelay, builderBackend)
198213

199214
stack.RegisterAPIs([]rpc.API{

cmd/geth/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
168168
// Configure log filter RPC API.
169169
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
170170

171-
if err := blockvalidationapi.Register(stack, eth, ctx); err != nil {
171+
if err := blockvalidationapi.Register(stack, eth, ctx.String(utils.BuilderBlockValidationBlacklistSourceFilePath.Name)); err != nil {
172172
utils.Fatalf("Failed to register the Block Validation API: %v", err)
173173
}
174174

cmd/geth/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ var (
158158
utils.BuilderEnabled,
159159
utils.BuilderEnableValidatorChecks,
160160
utils.BuilderEnableLocalRelay,
161+
utils.BuilderDryRun,
161162
utils.BuilderSecretKey,
162163
utils.BuilderRelaySecretKey,
163164
utils.BuilderListenAddr,

cmd/utils/flags.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,10 @@ var (
698698
Name: "builder.local_relay",
699699
Usage: "Enable the local relay",
700700
}
701+
BuilderDryRun = &cli.BoolFlag{
702+
Name: "builder.dry-run",
703+
Usage: "Builder only validates blocks without submission to the relay",
704+
}
701705
BuilderSecretKey = &cli.StringFlag{
702706
Name: "builder.secret_key",
703707
Usage: "Builder key used for signing blocks",
@@ -1077,8 +1081,8 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
10771081
// Builder API flags
10781082
BuilderBlockValidationBlacklistSourceFilePath = &cli.StringFlag{
10791083
Name: "builder.validation_blacklist",
1080-
Usage: "Path to file containing blacklisted addresses, json-encoded list of strings. Default assumes CWD is repo's root",
1081-
Value: "ofac_blacklist.json",
1084+
Usage: "Path to file containing blacklisted addresses, json-encoded list of strings. Default assumes no blacklist",
1085+
Value: "",
10821086
Category: flags.EthCategory,
10831087
}
10841088
)

eth/block-validation/api.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"math/big"
88
"os"
99

10-
"github.com/ethereum/go-ethereum/cmd/utils"
1110
"github.com/ethereum/go-ethereum/common"
1211
"github.com/ethereum/go-ethereum/core/beacon"
1312
"github.com/ethereum/go-ethereum/core/types"
@@ -17,8 +16,6 @@ import (
1716
"github.com/ethereum/go-ethereum/log"
1817
"github.com/ethereum/go-ethereum/node"
1918
"github.com/ethereum/go-ethereum/rpc"
20-
"github.com/urfave/cli/v2"
21-
2219
boostTypes "github.com/flashbots/go-boost-utils/types"
2320
)
2421

@@ -81,11 +78,11 @@ func NewAccessVerifierFromFile(path string) (*AccessVerifier, error) {
8178
}
8279

8380
// Register adds catalyst APIs to the full node.
84-
func Register(stack *node.Node, backend *eth.Ethereum, ctx *cli.Context) error {
81+
func Register(stack *node.Node, backend *eth.Ethereum, blockValidationBlocklistFile string) error {
8582
var accessVerifier *AccessVerifier
86-
if ctx.IsSet(utils.BuilderBlockValidationBlacklistSourceFilePath.Name) {
83+
if blockValidationBlocklistFile != "" {
8784
var err error
88-
accessVerifier, err = NewAccessVerifierFromFile(ctx.String(utils.BuilderBlockValidationBlacklistSourceFilePath.Name))
85+
accessVerifier, err = NewAccessVerifierFromFile(blockValidationBlocklistFile)
8986
if err != nil {
9087
return err
9188
}

eth/block-validation/api_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func TestValidateBuilderSubmissionV1(t *testing.T) {
101101
}
102102
blockRequest.Message.Value = boostTypes.IntToU256(190526394825529)
103103
require.ErrorContains(t, api.ValidateBuilderSubmissionV1(blockRequest), "inaccurate payment")
104-
blockRequest.Message.Value = boostTypes.IntToU256(190215802060530)
104+
blockRequest.Message.Value = boostTypes.IntToU256(190526394825530)
105105
require.NoError(t, api.ValidateBuilderSubmissionV1(blockRequest))
106106

107107
// TODO: test with contract calling blacklisted address
@@ -142,7 +142,7 @@ func TestValidateBuilderSubmissionV1(t *testing.T) {
142142
invalidPayload.LogsBloom = boostTypes.Bloom{}
143143
copy(invalidPayload.ReceiptsRoot[:], hexutil.MustDecode("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")[:32])
144144
blockRequest.ExecutionPayload = invalidPayload
145-
copy(blockRequest.Message.BlockHash[:], hexutil.MustDecode("0x65cded68b85277f489f22497731d8cece9e42f0429a250a5022a9417408f3998")[:32])
145+
copy(blockRequest.Message.BlockHash[:], hexutil.MustDecode("0x272872d14b2a8a0454e747ed472d82d8d5ce342cfafd65fa7b77aa6de1c061d4")[:32])
146146
require.ErrorContains(t, api.ValidateBuilderSubmissionV1(blockRequest), "could not apply tx 3", "insufficient funds for gas * price + value")
147147
}
148148

miner/algo_common.go

+112
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package miner
22

33
import (
4+
"crypto/ecdsa"
45
"errors"
6+
"fmt"
57
"math/big"
68
"sync/atomic"
79

@@ -20,6 +22,8 @@ const (
2022
popTx = 2
2123
)
2224

25+
var emptyCodeHash = common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
26+
2327
var errInterrupt = errors.New("miner worker interrupted")
2428

2529
type environmentDiff struct {
@@ -263,3 +267,111 @@ func (envDiff *environmentDiff) commitBundle(bundle *types.SimulatedBundle, chDa
263267
*envDiff = *tmpEnvDiff
264268
return nil
265269
}
270+
271+
func estimatePayoutTxGas(env *environment, sender, receiver common.Address, prv *ecdsa.PrivateKey, chData chainData) (uint64, bool, error) {
272+
if codeHash := env.state.GetCodeHash(receiver); codeHash == (common.Hash{}) || codeHash == emptyCodeHash {
273+
return params.TxGas, true, nil
274+
}
275+
gasLimit := env.gasPool.Gas()
276+
277+
balance := new(big.Int).SetBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
278+
value := new(big.Int).SetBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
279+
280+
diff := newEnvironmentDiff(env)
281+
diff.state.SetBalance(sender, balance)
282+
receipt, err := diff.commitPayoutTx(value, sender, receiver, gasLimit, prv, chData)
283+
if err != nil {
284+
return 0, false, err
285+
}
286+
return receipt.GasUsed, false, nil
287+
}
288+
289+
func insertPayoutTx(env *environment, sender, receiver common.Address, gas uint64, isEOA bool, availableFunds *big.Int, prv *ecdsa.PrivateKey, chData chainData) (*types.Receipt, error) {
290+
diff := newEnvironmentDiff(env)
291+
applyTx := func(gas uint64) (*types.Receipt, error) {
292+
fee := new(big.Int).Mul(env.header.BaseFee, new(big.Int).SetUint64(gas))
293+
amount := new(big.Int).Sub(availableFunds, fee)
294+
if amount.Sign() < 0 {
295+
return nil, errors.New("not enough funds available")
296+
}
297+
rec, err := diff.commitPayoutTx(amount, sender, receiver, gas, prv, chData)
298+
if err != nil {
299+
return nil, fmt.Errorf("failed to commit payment tx: %w", err)
300+
}
301+
if rec.Status != types.ReceiptStatusSuccessful {
302+
return nil, fmt.Errorf("payment tx failed")
303+
}
304+
return rec, nil
305+
}
306+
307+
if isEOA {
308+
rec, err := applyTx(gas)
309+
if err != nil {
310+
return nil, err
311+
}
312+
diff.applyToBaseEnv()
313+
return rec, nil
314+
}
315+
316+
var (
317+
rec *types.Receipt
318+
err error
319+
)
320+
for i := 0; i < 6; i++ {
321+
rec, err = applyTx(gas)
322+
if err != nil {
323+
gas += 1000
324+
} else {
325+
break
326+
}
327+
}
328+
329+
if err != nil {
330+
return nil, err
331+
}
332+
diff.applyToBaseEnv()
333+
return rec, nil
334+
}
335+
336+
func (envDiff *environmentDiff) commitPayoutTx(amount *big.Int, sender, receiver common.Address, gas uint64, prv *ecdsa.PrivateKey, chData chainData) (*types.Receipt, error) {
337+
senderBalance := envDiff.state.GetBalance(sender)
338+
339+
if gas < params.TxGas {
340+
return nil, errors.New("not enough gas for intrinsic gas cost")
341+
}
342+
343+
requiredBalance := new(big.Int).Mul(envDiff.header.BaseFee, new(big.Int).SetUint64(gas))
344+
requiredBalance = requiredBalance.Add(requiredBalance, amount)
345+
if requiredBalance.Cmp(senderBalance) > 0 {
346+
return nil, errors.New("not enough balance")
347+
}
348+
349+
signer := envDiff.baseEnvironment.signer
350+
tx, err := types.SignNewTx(prv, signer, &types.DynamicFeeTx{
351+
ChainID: chData.chainConfig.ChainID,
352+
Nonce: envDiff.state.GetNonce(sender),
353+
GasTipCap: new(big.Int),
354+
GasFeeCap: envDiff.header.BaseFee,
355+
Gas: gas,
356+
To: &receiver,
357+
Value: amount,
358+
})
359+
if err != nil {
360+
return nil, err
361+
}
362+
363+
txSender, err := signer.Sender(tx)
364+
if err != nil {
365+
return nil, err
366+
}
367+
if txSender != sender {
368+
return nil, errors.New("incorrect sender private key")
369+
}
370+
371+
receipt, _, err := envDiff.commitTx(tx, chData)
372+
if err != nil {
373+
return nil, err
374+
}
375+
376+
return receipt, nil
377+
}

0 commit comments

Comments
 (0)