From d046f90bd1052250e3eb7edc273227c15ab220b6 Mon Sep 17 00:00:00 2001 From: maskpp Date: Mon, 22 Jan 2024 19:58:59 +0800 Subject: [PATCH 1/3] Add blob tx --- pkg/rpc/txblob.go | 135 +++++++++++++++++++++++++++++++++++++++++ pkg/rpc/txblob_test.go | 39 ++++++++++++ pkg/rpc/utils.go | 3 + 3 files changed, 177 insertions(+) create mode 100644 pkg/rpc/txblob.go create mode 100644 pkg/rpc/txblob_test.go diff --git a/pkg/rpc/txblob.go b/pkg/rpc/txblob.go new file mode 100644 index 000000000..6f55e7f2e --- /dev/null +++ b/pkg/rpc/txblob.go @@ -0,0 +1,135 @@ +package rpc + +import ( + "errors" + "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/holiman/uint256" + "math/big" +) + +func (c *EthClient) TransactBlobTx(opts *bind.TransactOpts, data []byte) (*types.Transaction, error) { + // Sign the transaction and schedule it for execution + if opts.Signer == nil { + return nil, errors.New("no signer to authorize the transaction with") + } + // Create blob tx. + rawTx, err := c.createBlobTx(opts, data) + if err != nil { + return nil, err + } + signedTx, err := opts.Signer(opts.From, rawTx) + if err != nil { + return nil, err + } + if opts.NoSend { + return signedTx, nil + } + if err := c.SendTransaction(opts.Context, signedTx); err != nil { + return nil, err + } + return signedTx, nil +} + +func (c *EthClient) createBlobTx(opts *bind.TransactOpts, data []byte) (*types.Transaction, error) { + block, err := c.BlockByNumber(opts.Context, nil) + if err != nil { + return nil, err + } + // Estimate TipCap + gasTipCap := opts.GasTipCap + if gasTipCap == nil { + tip, err := c.SuggestGasTipCap(opts.Context) + if err != nil { + return nil, err + } + gasTipCap = tip + } + // Estimate FeeCap + gasFeeCap := opts.GasFeeCap + if gasFeeCap == nil { + gasFeeCap = new(big.Int).Add( + gasTipCap, + new(big.Int).Mul(block.Header().BaseFee, big.NewInt(2)), + ) + } + if gasFeeCap.Cmp(gasTipCap) < 0 { + return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap) + } + // Estimate GasLimit + gasLimit := opts.GasLimit + if opts.GasLimit == 0 { + var err error + gasLimit, err = c.EstimateGas(nil, ethereum.CallMsg{ + From: opts.From, + To: nil, + GasPrice: nil, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: nil, + Data: nil, + }) + if err != nil { + return nil, err + } + } + // create the transaction + nonce, err := c.getNonce(opts) + if err != nil { + return nil, err + } + chainID, err := c.ChainID(opts.Context) + if err != nil { + return nil, err + } + + sidecar, err := makeSidecarWithSingleBlob(data) + if err != nil { + return nil, err + } + + baseTx := &types.BlobTx{ + ChainID: uint256.NewInt(chainID.Uint64()), + Nonce: nonce, + GasTipCap: uint256.NewInt(gasTipCap.Uint64()), + GasFeeCap: uint256.NewInt(gasFeeCap.Uint64()), + Gas: gasLimit, + BlobFeeCap: uint256.MustFromBig(eip4844.CalcBlobFee(*block.ExcessBlobGas())), + BlobHashes: sidecar.BlobHashes(), + Sidecar: sidecar, + } + return types.NewTx(baseTx), nil +} + +func (c *EthClient) getNonce(opts *bind.TransactOpts) (uint64, error) { + if opts.Nonce == nil { + return c.PendingNonceAt(opts.Context, opts.From) + } else { + return opts.Nonce.Uint64(), nil + } +} + +func makeSidecarWithSingleBlob(data []byte) (*types.BlobTxSidecar, error) { + if len(data) > BlobBytes { + return nil, fmt.Errorf("data is bigger than 128k") + } + blob := kzg4844.Blob{} + copy(blob[:], data) + commitment, err := kzg4844.BlobToCommitment(blob) + if err != nil { + return nil, err + } + proof, err := kzg4844.ComputeBlobProof(blob, commitment) + if err != nil { + return nil, err + } + return &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{blob}, + Commitments: []kzg4844.Commitment{commitment}, + Proofs: []kzg4844.Proof{proof}, + }, nil +} diff --git a/pkg/rpc/txblob_test.go b/pkg/rpc/txblob_test.go new file mode 100644 index 000000000..fd3e1c584 --- /dev/null +++ b/pkg/rpc/txblob_test.go @@ -0,0 +1,39 @@ +package rpc + +import ( + "context" + "os" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" +) + +func TestBlockTx(t *testing.T) { + l1Client, err := NewEthClient(nil, os.Getenv("L1_NODE_WS_ENDPOINT"), time.Second*20) + assert.NoError(t, err) + + sk, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) + assert.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + chainID, err := l1Client.ChainID(ctx) + assert.NoError(t, err) + + opts, err := bind.NewKeyedTransactorWithChainID(sk, chainID) + assert.NoError(t, err) + opts.Context = ctx + + tx, err := l1Client.TransactBlobTx(opts, []byte("ssssssssss")) + assert.NoError(t, err) + + receipt, err := bind.WaitMined(ctx, l1Client, tx) + assert.NoError(t, err) + t.Log(receipt) + +} diff --git a/pkg/rpc/utils.go b/pkg/rpc/utils.go index 002328e48..e4d593ea4 100644 --- a/pkg/rpc/utils.go +++ b/pkg/rpc/utils.go @@ -14,6 +14,8 @@ import ( "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/taikoxyz/taiko-client/bindings" "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/internal/utils" @@ -23,6 +25,7 @@ var ( ZeroAddress common.Address waitReceiptPollingInterval = 3 * time.Second defaultWaitReceiptTimeout = 1 * time.Minute + BlobBytes = params.BlobTxBytesPerFieldElement * params.BlobTxFieldElementsPerBlob ) // GetProtocolStateVariables gets the protocol states from TaikoL1 contract. From 9a96b3f3623e7e8bdaed631e0a1a7a936dc968f6 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 23 Jan 2024 11:26:07 +0800 Subject: [PATCH 2/3] add blob tx --- pkg/rpc/txblob.go | 11 ++++++++--- pkg/rpc/txblob_test.go | 11 ++++++----- proposer/proposer.go | 36 ++++++++++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/pkg/rpc/txblob.go b/pkg/rpc/txblob.go index 6f55e7f2e..cf36ec4c1 100644 --- a/pkg/rpc/txblob.go +++ b/pkg/rpc/txblob.go @@ -36,7 +36,7 @@ func (c *EthClient) TransactBlobTx(opts *bind.TransactOpts, data []byte) (*types } func (c *EthClient) createBlobTx(opts *bind.TransactOpts, data []byte) (*types.Transaction, error) { - block, err := c.BlockByNumber(opts.Context, nil) + header, err := c.HeaderByNumber(opts.Context, nil) if err != nil { return nil, err } @@ -54,7 +54,7 @@ func (c *EthClient) createBlobTx(opts *bind.TransactOpts, data []byte) (*types.T if gasFeeCap == nil { gasFeeCap = new(big.Int).Add( gasTipCap, - new(big.Int).Mul(block.Header().BaseFee, big.NewInt(2)), + new(big.Int).Mul(header.BaseFee, big.NewInt(2)), ) } if gasFeeCap.Cmp(gasTipCap) < 0 { @@ -92,13 +92,18 @@ func (c *EthClient) createBlobTx(opts *bind.TransactOpts, data []byte) (*types.T return nil, err } + var blobFeeCap uint64 = 100066 + if header.ExcessBlobGas != nil { + blobFeeCap = *header.ExcessBlobGas + } + baseTx := &types.BlobTx{ ChainID: uint256.NewInt(chainID.Uint64()), Nonce: nonce, GasTipCap: uint256.NewInt(gasTipCap.Uint64()), GasFeeCap: uint256.NewInt(gasFeeCap.Uint64()), Gas: gasLimit, - BlobFeeCap: uint256.MustFromBig(eip4844.CalcBlobFee(*block.ExcessBlobGas())), + BlobFeeCap: uint256.MustFromBig(eip4844.CalcBlobFee(blobFeeCap)), BlobHashes: sidecar.BlobHashes(), Sidecar: sidecar, } diff --git a/pkg/rpc/txblob_test.go b/pkg/rpc/txblob_test.go index fd3e1c584..d61dad17d 100644 --- a/pkg/rpc/txblob_test.go +++ b/pkg/rpc/txblob_test.go @@ -13,23 +13,24 @@ import ( ) func TestBlockTx(t *testing.T) { - l1Client, err := NewEthClient(nil, os.Getenv("L1_NODE_WS_ENDPOINT"), time.Second*20) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + l1Client, err := NewEthClient(ctx, os.Getenv("L1_NODE_WS_ENDPOINT"), time.Second*20) assert.NoError(t, err) sk, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) assert.NoError(t, err) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - chainID, err := l1Client.ChainID(ctx) assert.NoError(t, err) opts, err := bind.NewKeyedTransactorWithChainID(sk, chainID) assert.NoError(t, err) opts.Context = ctx + //opts.NoSend = true - tx, err := l1Client.TransactBlobTx(opts, []byte("ssssssssss")) + tx, err := l1Client.TransactBlobTx(opts, []byte("s")) assert.NoError(t, err) receipt, err := bind.WaitMined(ctx, l1Client, tx) diff --git a/proposer/proposer.go b/proposer/proposer.go index 6255a9edb..084991c1a 100644 --- a/proposer/proposer.go +++ b/proposer/proposer.go @@ -307,10 +307,27 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { return nil } +func (p *Proposer) sendTxListByBlobTx(ctx context.Context, txListBytes []byte) (*types.Transaction, error) { + blobOpts, err := getTxOpts(ctx, p.rpc.L1, p.L1ProposerPrivKey, p.rpc.L1ChainID, nil) + if err != nil { + return nil, err + } + tx, err := p.rpc.L1.TransactBlobTx(blobOpts, txListBytes) + if err != nil { + return nil, err + } + // Wait until blob tx is mined. + if _, err := bind.WaitMined(ctx, p.rpc.L1, tx); err != nil { + return nil, err + } + + return tx, nil +} + // sendProposeBlockTx tries to send a TaikoL1.proposeBlock transaction. func (p *Proposer) sendProposeBlockTx( ctx context.Context, - txListBytes []byte, + blobHash common.Hash, nonce *uint64, assignment *encoding.ProverAssignment, assignedProver common.Address, @@ -376,7 +393,7 @@ func (p *Proposer) sendProposeBlockTx( ExtraData: rpc.StringToBytes32(p.ExtraData), TxListByteOffset: common.Big0, TxListByteSize: common.Big0, - BlobHash: [32]byte{}, + BlobHash: blobHash, CacheBlobForReuse: false, ParentMetaHash: parentMetaHash, HookCalls: hookCalls, @@ -388,8 +405,9 @@ func (p *Proposer) sendProposeBlockTx( proposeTx, err := p.rpc.TaikoL1.ProposeBlock( opts, encodedParams, - txListBytes, + nil, ) + if err != nil { return nil, encoding.TryParsingCustomError(err) } @@ -415,6 +433,7 @@ func (p *Proposer) ProposeTxList( var ( isReplacement bool + blobTx *types.Transaction tx *types.Transaction ) if err := backoff.Retry( @@ -422,9 +441,18 @@ func (p *Proposer) ProposeTxList( if ctx.Err() != nil { return nil } + + // Send tx list by blob tx. + if blobTx == nil { + blobTx, err = p.sendTxListByBlobTx(ctx, txListBytes) + if err != nil { + return nil + } + } + if tx, err = p.sendProposeBlockTx( ctx, - txListBytes, + blobTx.BlobHashes()[0], nonce, assignment, proverAddress, From 4614b95a03b6882bfaaee00662ef6462f29cb59d Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 23 Jan 2024 14:50:51 +0800 Subject: [PATCH 3/3] add blob tx --- pkg/rpc/txblob_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/rpc/txblob_test.go b/pkg/rpc/txblob_test.go index d61dad17d..713a72f86 100644 --- a/pkg/rpc/txblob_test.go +++ b/pkg/rpc/txblob_test.go @@ -16,10 +16,12 @@ func TestBlockTx(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - l1Client, err := NewEthClient(ctx, os.Getenv("L1_NODE_WS_ENDPOINT"), time.Second*20) + url := "https://rpc.ankr.com/eth_goerli" //os.Getenv("L1_NODE_WS_ENDPOINT") + l1Client, err := NewEthClient(ctx, url, time.Second*20) assert.NoError(t, err) - sk, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) + priv := os.Getenv("L1_PROPOSER_PRIVATE_KEY") + sk, err := crypto.ToECDSA(common.FromHex(priv)) assert.NoError(t, err) chainID, err := l1Client.ChainID(ctx) @@ -30,11 +32,14 @@ func TestBlockTx(t *testing.T) { opts.Context = ctx //opts.NoSend = true + balance, err := l1Client.BalanceAt(ctx, opts.From, nil) + assert.NoError(t, err) + t.Logf("address: %s, balance: %s", opts.From.String(), balance.String()) + tx, err := l1Client.TransactBlobTx(opts, []byte("s")) assert.NoError(t, err) receipt, err := bind.WaitMined(ctx, l1Client, tx) assert.NoError(t, err) t.Log(receipt) - }