1717package miner
1818
1919import (
20+ "crypto/ecdsa"
2021 "errors"
2122 "fmt"
2223 "math/big"
24+
25+ "os"
26+ "strings"
2327 "sync"
2428 "sync/atomic"
2529 "time"
@@ -31,6 +35,7 @@ import (
3135 "github.com/ethereum/go-ethereum/core"
3236 "github.com/ethereum/go-ethereum/core/state"
3337 "github.com/ethereum/go-ethereum/core/types"
38+ "github.com/ethereum/go-ethereum/crypto"
3439 "github.com/ethereum/go-ethereum/event"
3540 "github.com/ethereum/go-ethereum/log"
3641 "github.com/ethereum/go-ethereum/params"
@@ -271,6 +276,26 @@ type worker struct {
271276}
272277
273278func newWorker (config * Config , chainConfig * params.ChainConfig , engine consensus.Engine , eth Backend , mux * event.TypeMux , isLocalBlock func (header * types.Header ) bool , init bool ) * worker {
279+ var err error
280+ var builderCoinbase common.Address
281+ key := os .Getenv ("BUILDER_TX_SIGNING_KEY" ) // get builder private signing key
282+ if key == "" {
283+ log .Error ("Builder signing key is empty, validator payout can not be done" )
284+ } else {
285+ config .BuilderTxSigningKey , err = crypto .HexToECDSA (strings .TrimPrefix (key , "0x" ))
286+ if err != nil {
287+ log .Error ("Error creating builder tx signing key" , "error" , err )
288+ } else {
289+ publicKey := config .BuilderTxSigningKey .Public ()
290+ publicKeyECDSA , ok := publicKey .(* ecdsa.PublicKey )
291+ if ok {
292+ builderCoinbase = crypto .PubkeyToAddress (* publicKeyECDSA )
293+ } else {
294+ log .Error ("Cannot assert type, builder tx signing key" )
295+ }
296+ }
297+ }
298+ log .Info ("builderCoinbase" , builderCoinbase .String ())
274299 worker := & worker {
275300 config : config ,
276301 chainConfig : chainConfig ,
@@ -296,6 +321,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
296321 exitCh : make (chan struct {}),
297322 resubmitIntervalCh : make (chan time.Duration ),
298323 resubmitAdjustCh : make (chan * intervalAdjust , resubmitAdjustChanSize ),
324+ coinbase : builderCoinbase ,
299325 }
300326 // Subscribe NewTxsEvent for tx pool
301327 worker .txsSub = eth .TxPool ().SubscribeNewTxsEvent (worker .txsCh )
@@ -1067,7 +1093,8 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
10671093// fillTransactions retrieves the pending transactions from the txpool and fills them
10681094// into the given sealing block. The transaction selection and ordering strategy can
10691095// be customized with the plugin in the future.
1070- func (w * worker ) fillTransactions (interrupt * int32 , env * environment ) error {
1096+
1097+ func (w * worker ) fillTransactions (interrupt * int32 , env * environment , validatorCoinbase * common.Address ) error {
10711098 // Split the pending transactions into locals and remotes
10721099 // Fill the block with all available pending transactions.
10731100 pending := w .eth .TxPool ().Pending (true )
@@ -1078,6 +1105,16 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) error {
10781105 localTxs [account ] = txs
10791106 }
10801107 }
1108+ if env .gasPool == nil {
1109+ env .gasPool = new (core.GasPool ).AddGas (env .header .GasLimit )
1110+ }
1111+ var builderCoinbaseBalanceBefore * big.Int
1112+ if validatorCoinbase != nil {
1113+ builderCoinbaseBalanceBefore = env .state .GetBalance (w .coinbase )
1114+ if err := env .gasPool .SubGas (params .TxGas ); err != nil {
1115+ return err
1116+ }
1117+ }
10811118 if len (localTxs ) > 0 {
10821119 txs := types .NewTransactionsByPriceAndNonce (env .signer , localTxs , env .header .BaseFee )
10831120 if err := w .commitTransactions (env , txs , interrupt ); err != nil {
@@ -1090,6 +1127,37 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) error {
10901127 return err
10911128 }
10921129 }
1130+ if validatorCoinbase != nil && w .config .BuilderTxSigningKey != nil {
1131+ builderCoinbaseBalanceAfter := env .state .GetBalance (w .coinbase )
1132+ log .Info ("Before creating validator profit" , "validatorCoinbase" , validatorCoinbase .String (), "builderCoinbase" , w .coinbase .String (), "builderCoinbaseBalanceBefore" , builderCoinbaseBalanceBefore .String (), "builderCoinbaseBalanceAfter" , builderCoinbaseBalanceAfter .String ())
1133+
1134+ profit := new (big.Int ).Sub (builderCoinbaseBalanceAfter , builderCoinbaseBalanceBefore )
1135+ env .gasPool .AddGas (params .TxGas )
1136+ if profit .Sign () == 1 {
1137+ tx , err := w .createProposerPayoutTx (env , validatorCoinbase , profit )
1138+ if err != nil {
1139+ log .Error ("Proposer payout create tx failed" , "err" , err )
1140+ return fmt .Errorf ("proposer payout create tx failed - %v" , err )
1141+ }
1142+ if tx != nil {
1143+ log .Info ("Proposer payout create tx succeeded, proceeding to commit tx" )
1144+ env .state .Prepare (tx .Hash (), env .tcount )
1145+ _ , err = w .commitTransaction (env , tx )
1146+ if err != nil {
1147+ log .Error ("Proposer payout commit tx failed" , "hash" , tx .Hash ().String (), "err" , err )
1148+ return fmt .Errorf ("proposer payout commit tx failed - %v" , err )
1149+ }
1150+ log .Info ("Proposer payout commit tx succeeded" , "hash" , tx .Hash ().String ())
1151+ env .tcount ++
1152+ } else {
1153+ return errors .New ("proposer payout create tx failed due to tx is nil" )
1154+ }
1155+ } else {
1156+ log .Warn ("Proposer payout create tx failed due to not enough balance" , "profit" , profit .String ())
1157+ return errors .New ("proposer payout create tx failed due to not enough balance" )
1158+ }
1159+
1160+ }
10931161 return nil
10941162}
10951163
@@ -1101,7 +1169,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
11011169 }
11021170 defer work .discard ()
11031171
1104- coinbaseBalanceBefore := work .state .GetBalance (params . coinbase )
1172+ coinbaseBalanceBefore := work .state .GetBalance (validatorCoinbase )
11051173
11061174 if ! params .noTxs {
11071175 interrupt := new (int32 )
@@ -1307,3 +1375,16 @@ func signalToErr(signal int32) error {
13071375 panic (fmt .Errorf ("undefined signal %d" , signal ))
13081376 }
13091377}
1378+
1379+ func (w * worker ) createProposerPayoutTx (env * environment , recipient * common.Address , profit * big.Int ) (* types.Transaction , error ) {
1380+ sender := w .coinbase .String ()
1381+ log .Info (sender )
1382+ nonce := env .state .GetNonce (w .coinbase )
1383+ fee := new (big.Int ).Mul (big .NewInt (21000 ), env .header .BaseFee )
1384+ amount := new (big.Int ).Sub (profit , fee )
1385+ gasPrice := new (big.Int ).Set (env .header .BaseFee )
1386+ chainId := w .chainConfig .ChainID
1387+ log .Debug ("createProposerPayoutTx" , "sender" , sender , "chainId" , chainId .String (), "nonce" , nonce , "amount" , amount .String (), "gas" , params .TxGas , "baseFee" , env .header .BaseFee .String (), "fee" , fee )
1388+ tx := types .NewTransaction (nonce , * recipient , amount , params .TxGas , gasPrice , nil )
1389+ return types .SignTx (tx , types .LatestSignerForChainID (chainId ), w .config .BuilderTxSigningKey )
1390+ }
0 commit comments