Skip to content

Commit 4553f5f

Browse files
authored
feat(ethclient & gethclient): support blob transaction (#661)
* feat: support blob transaction * sync tx encoding/decoding * more fixes * add uint236 support in rlp encoding decoding * revert a format change * trigger ci
1 parent ccec84c commit 4553f5f

14 files changed

+284
-20
lines changed

consensus/misc/eip4844.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2023 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package misc
18+
19+
import (
20+
"math/big"
21+
22+
"github.com/scroll-tech/go-ethereum/params"
23+
)
24+
25+
var (
26+
minBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
27+
blobGaspriceUpdateFraction = big.NewInt(params.BlobTxBlobGaspriceUpdateFraction)
28+
)
29+
30+
// CalcExcessBlobGas calculates the excess blob gas after applying the set of
31+
// blobs on top of the excess blob gas.
32+
func CalcExcessBlobGas(parentExcessBlobGas uint64, parentBlobGasUsed uint64) uint64 {
33+
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
34+
if excessBlobGas < params.BlobTxTargetBlobGasPerBlock {
35+
return 0
36+
}
37+
return excessBlobGas - params.BlobTxTargetBlobGasPerBlock
38+
}
39+
40+
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
41+
func CalcBlobFee(excessBlobGas uint64) *big.Int {
42+
return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(excessBlobGas), blobGaspriceUpdateFraction)
43+
}
44+
45+
// fakeExponential approximates factor * e ** (numerator / denominator) using
46+
// Taylor expansion.
47+
func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
48+
var (
49+
output = new(big.Int)
50+
accum = new(big.Int).Mul(factor, denominator)
51+
)
52+
for i := 1; accum.Sign() > 0; i++ {
53+
output.Add(output, accum)
54+
55+
accum.Mul(accum, numerator)
56+
accum.Div(accum, denominator)
57+
accum.Div(accum, big.NewInt(int64(i)))
58+
}
59+
return output.Div(output, denominator)
60+
}

core/types/access_list_tx.go

+10
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package types
1818

1919
import (
20+
"bytes"
2021
"math/big"
2122

2223
"github.com/scroll-tech/go-ethereum/common"
24+
"github.com/scroll-tech/go-ethereum/rlp"
2325
)
2426

2527
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
@@ -113,3 +115,11 @@ func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
113115
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
114116
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
115117
}
118+
119+
func (tx *AccessListTx) encode(b *bytes.Buffer) error {
120+
return rlp.Encode(b, tx)
121+
}
122+
123+
func (tx *AccessListTx) decode(input []byte) error {
124+
return rlp.DecodeBytes(input, tx)
125+
}

core/types/dynamic_fee_tx.go

+10
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package types
1818

1919
import (
20+
"bytes"
2021
"math/big"
2122

2223
"github.com/scroll-tech/go-ethereum/common"
24+
"github.com/scroll-tech/go-ethereum/rlp"
2325
)
2426

2527
type DynamicFeeTx struct {
@@ -101,3 +103,11 @@ func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
101103
func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) {
102104
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
103105
}
106+
107+
func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error {
108+
return rlp.Encode(b, tx)
109+
}
110+
111+
func (tx *DynamicFeeTx) decode(input []byte) error {
112+
return rlp.DecodeBytes(input, tx)
113+
}

core/types/l1_message_tx.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package types
22

33
import (
4+
"bytes"
45
"math/big"
56

67
"github.com/scroll-tech/go-ethereum/common"
8+
"github.com/scroll-tech/go-ethereum/rlp"
79
)
810

911
// payload, RLP encoded
@@ -52,3 +54,11 @@ func (tx *L1MessageTx) rawSignatureValues() (v, r, s *big.Int) {
5254
func (tx *L1MessageTx) setSignatureValues(chainID, v, r, s *big.Int) {
5355
// this is a noop for l1 message transactions
5456
}
57+
58+
func (tx *L1MessageTx) encode(b *bytes.Buffer) error {
59+
return rlp.Encode(b, tx)
60+
}
61+
62+
func (tx *L1MessageTx) decode(input []byte) error {
63+
return rlp.DecodeBytes(input, tx)
64+
}

core/types/legacy_tx.go

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package types
1818

1919
import (
20+
"bytes"
2021
"math/big"
2122

2223
"github.com/scroll-tech/go-ethereum/common"
@@ -110,3 +111,11 @@ func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
110111
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
111112
tx.V, tx.R, tx.S = v, r, s
112113
}
114+
115+
func (tx *LegacyTx) encode(*bytes.Buffer) error {
116+
panic("encode called on LegacyTx")
117+
}
118+
119+
func (tx *LegacyTx) decode([]byte) error {
120+
panic("decode called on LegacyTx)")
121+
}

core/types/transaction.go

+17-18
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
ErrTxTypeNotSupported = errors.New("transaction type not supported")
4242
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
4343
errEmptyTypedTx = errors.New("empty typed transaction bytes")
44+
errShortTypedTx = errors.New("typed transaction too short")
4445
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
4546
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
4647
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
@@ -94,6 +95,9 @@ type TxData interface {
9495

9596
rawSignatureValues() (v, r, s *big.Int)
9697
setSignatureValues(chainID, v, r, s *big.Int)
98+
99+
encode(*bytes.Buffer) error
100+
decode([]byte) error
97101
}
98102

99103
// EncodeRLP implements rlp.Encoder
@@ -114,7 +118,7 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error {
114118
// encodeTyped writes the canonical encoding of a typed transaction to w.
115119
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
116120
w.WriteByte(tx.Type())
117-
return rlp.Encode(w, tx.inner)
121+
return tx.inner.encode(w)
118122
}
119123

120124
// MarshalBinary returns the canonical encoding of the transaction.
@@ -143,7 +147,9 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
143147
tx.setDecoded(&inner, int(rlp.ListSize(size)))
144148
}
145149
return err
146-
case kind == rlp.String:
150+
case kind == rlp.Byte:
151+
return errShortTypedTx
152+
default:
147153
// It's an EIP-2718 typed TX envelope.
148154
var b []byte
149155
if b, err = s.Bytes(); err != nil {
@@ -154,8 +160,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
154160
tx.setDecoded(inner, len(b))
155161
}
156162
return err
157-
default:
158-
return rlp.ErrExpectedList
159163
}
160164
}
161165

@@ -183,29 +187,24 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
183187

184188
// decodeTyped decodes a typed transaction from the canonical format.
185189
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
186-
if len(b) == 0 {
187-
return nil, errEmptyTypedTx
190+
if len(b) <= 1 {
191+
return nil, errShortTypedTx
188192
}
193+
var inner TxData
189194
switch b[0] {
190195
case AccessListTxType:
191-
var inner AccessListTx
192-
err := rlp.DecodeBytes(b[1:], &inner)
193-
return &inner, err
196+
inner = new(AccessListTx)
194197
case DynamicFeeTxType:
195-
var inner DynamicFeeTx
196-
err := rlp.DecodeBytes(b[1:], &inner)
197-
return &inner, err
198+
inner = new(DynamicFeeTx)
198199
case BlobTxType:
199-
var inner BlobTx
200-
err := rlp.DecodeBytes(b[1:], &inner)
201-
return &inner, err
200+
inner = new(BlobTx)
202201
case L1MessageTxType:
203-
var inner L1MessageTx
204-
err := rlp.DecodeBytes(b[1:], &inner)
205-
return &inner, err
202+
inner = new(L1MessageTx)
206203
default:
207204
return nil, ErrTxTypeNotSupported
208205
}
206+
err := inner.decode(b[1:])
207+
return inner, err
209208
}
210209

211210
// setDecoded sets the inner transaction and size after decoding.

core/types/transaction_marshalling.go

+11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/scroll-tech/go-ethereum/common"
2727
"github.com/scroll-tech/go-ethereum/common/hexutil"
28+
"github.com/scroll-tech/go-ethereum/crypto/kzg4844"
2829
)
2930

3031
// txJSON is the JSON representation of transactions.
@@ -48,6 +49,11 @@ type txJSON struct {
4849
S *hexutil.Big `json:"s"`
4950
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
5051

52+
// Blob transaction sidecar encoding:
53+
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
54+
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
55+
Proofs []kzg4844.Proof `json:"proofs,omitempty"`
56+
5157
// Only used for encoding:
5258
Hash common.Hash `json:"hash"`
5359

@@ -155,6 +161,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
155161
enc.S = (*hexutil.Big)(itx.S.ToBig())
156162
yparity := itx.V.Uint64()
157163
enc.YParity = (*hexutil.Uint64)(&yparity)
164+
if sidecar := itx.Sidecar; sidecar != nil {
165+
enc.Blobs = itx.Sidecar.Blobs
166+
enc.Commitments = itx.Sidecar.Commitments
167+
enc.Proofs = itx.Sidecar.Proofs
168+
}
158169
}
159170
return json.Marshal(&enc)
160171
}

ethclient/ethclient.go

+15
Original file line numberDiff line numberDiff line change
@@ -719,5 +719,20 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
719719
if msg.GasPrice != nil {
720720
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
721721
}
722+
if msg.GasFeeCap != nil {
723+
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
724+
}
725+
if msg.GasTipCap != nil {
726+
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
727+
}
728+
if msg.AccessList != nil {
729+
arg["accessList"] = msg.AccessList
730+
}
731+
if msg.BlobGasFeeCap != nil {
732+
arg["maxFeePerBlobGas"] = (*hexutil.Big)(msg.BlobGasFeeCap)
733+
}
734+
if msg.BlobHashes != nil {
735+
arg["blobVersionedHashes"] = msg.BlobHashes
736+
}
722737
return arg
723738
}

ethclient/gethclient/gethclient.go

+15
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,21 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
213213
if msg.GasPrice != nil {
214214
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
215215
}
216+
if msg.GasFeeCap != nil {
217+
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
218+
}
219+
if msg.GasTipCap != nil {
220+
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
221+
}
222+
if msg.AccessList != nil {
223+
arg["accessList"] = msg.AccessList
224+
}
225+
if msg.BlobGasFeeCap != nil {
226+
arg["maxFeePerBlobGas"] = (*hexutil.Big)(msg.BlobGasFeeCap)
227+
}
228+
if msg.BlobHashes != nil {
229+
arg["blobVersionedHashes"] = msg.BlobHashes
230+
}
216231
return arg
217232
}
218233

interfaces.go

+4
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ type CallMsg struct {
123123
Data []byte // input data, usually an ABI-encoded contract method invocation
124124

125125
AccessList types.AccessList // EIP-2930 access list.
126+
127+
// For BlobTxType
128+
BlobGasFeeCap *big.Int
129+
BlobHashes []common.Hash
126130
}
127131

128132
// A ContractCaller provides contract calls, essentially transactions that are executed by

params/protocol_params.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,11 @@ const (
160160
RefundQuotient uint64 = 2
161161
RefundQuotientEIP3529 uint64 = 5
162162

163-
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
163+
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
164+
BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs
165+
BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price
166+
167+
BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
164168
)
165169

166170
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations

params/version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 5 // Major version component of the current release
2626
VersionMinor = 1 // Minor version component of the current release
27-
VersionPatch = 22 // Patch version component of the current release
27+
VersionPatch = 23 // Patch version component of the current release
2828
VersionMeta = "mainnet" // Version metadata to append to the version string
2929
)
3030

0 commit comments

Comments
 (0)