Skip to content

Commit ae7cbae

Browse files
authored
feat(transactions): support blob tx only in encoding/decoding (#626)
* draft change * fix CI * remove CancunBlock params and change cancunSigner to londonSignerWithEIP4844 * support blob transactions in receipt * trigger ci * bump version
1 parent d86b42c commit ae7cbae

16 files changed

+5474
-150
lines changed

core/types/blob_tx.go

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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 types
18+
19+
import (
20+
"bytes"
21+
"crypto/sha256"
22+
"math/big"
23+
24+
"github.com/holiman/uint256"
25+
"github.com/scroll-tech/go-ethereum/common"
26+
"github.com/scroll-tech/go-ethereum/crypto/kzg4844"
27+
"github.com/scroll-tech/go-ethereum/params"
28+
"github.com/scroll-tech/go-ethereum/rlp"
29+
)
30+
31+
// BlobTx represents an EIP-4844 transaction.
32+
type BlobTx struct {
33+
ChainID *uint256.Int
34+
Nonce uint64
35+
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
36+
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
37+
Gas uint64
38+
To common.Address
39+
Value *uint256.Int
40+
Data []byte
41+
AccessList AccessList
42+
BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas
43+
BlobHashes []common.Hash
44+
45+
// A blob transaction can optionally contain blobs. This field must be set when BlobTx
46+
// is used to create a transaction for sigining.
47+
Sidecar *BlobTxSidecar `rlp:"-"`
48+
49+
// Signature values
50+
V *uint256.Int `json:"v" gencodec:"required"`
51+
R *uint256.Int `json:"r" gencodec:"required"`
52+
S *uint256.Int `json:"s" gencodec:"required"`
53+
}
54+
55+
// BlobTxSidecar contains the blobs of a blob transaction.
56+
type BlobTxSidecar struct {
57+
Blobs []kzg4844.Blob // Blobs needed by the blob pool
58+
Commitments []kzg4844.Commitment // Commitments needed by the blob pool
59+
Proofs []kzg4844.Proof // Proofs needed by the blob pool
60+
}
61+
62+
// BlobHashes computes the blob hashes of the given blobs.
63+
func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
64+
hasher := sha256.New()
65+
h := make([]common.Hash, len(sc.Commitments))
66+
for i := range sc.Blobs {
67+
h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i])
68+
}
69+
return h
70+
}
71+
72+
// encodedSize computes the RLP size of the sidecar elements. This does NOT return the
73+
// encoded size of the BlobTxSidecar, it's just a helper for tx.Size().
74+
func (sc *BlobTxSidecar) encodedSize() uint64 {
75+
var blobs, commitments, proofs uint64
76+
for i := range sc.Blobs {
77+
blobs += rlp.BytesSize(sc.Blobs[i][:])
78+
}
79+
for i := range sc.Commitments {
80+
commitments += rlp.BytesSize(sc.Commitments[i][:])
81+
}
82+
for i := range sc.Proofs {
83+
proofs += rlp.BytesSize(sc.Proofs[i][:])
84+
}
85+
return rlp.ListSize(blobs) + rlp.ListSize(commitments) + rlp.ListSize(proofs)
86+
}
87+
88+
// blobTxWithBlobs is used for encoding of transactions when blobs are present.
89+
type blobTxWithBlobs struct {
90+
BlobTx *BlobTx
91+
Blobs []kzg4844.Blob
92+
Commitments []kzg4844.Commitment
93+
Proofs []kzg4844.Proof
94+
}
95+
96+
// copy creates a deep copy of the transaction data and initializes all fields.
97+
func (tx *BlobTx) copy() TxData {
98+
cpy := &BlobTx{
99+
Nonce: tx.Nonce,
100+
To: tx.To,
101+
Data: common.CopyBytes(tx.Data),
102+
Gas: tx.Gas,
103+
// These are copied below.
104+
AccessList: make(AccessList, len(tx.AccessList)),
105+
BlobHashes: make([]common.Hash, len(tx.BlobHashes)),
106+
Value: new(uint256.Int),
107+
ChainID: new(uint256.Int),
108+
GasTipCap: new(uint256.Int),
109+
GasFeeCap: new(uint256.Int),
110+
BlobFeeCap: new(uint256.Int),
111+
V: new(uint256.Int),
112+
R: new(uint256.Int),
113+
S: new(uint256.Int),
114+
}
115+
copy(cpy.AccessList, tx.AccessList)
116+
copy(cpy.BlobHashes, tx.BlobHashes)
117+
118+
if tx.Value != nil {
119+
cpy.Value.Set(tx.Value)
120+
}
121+
if tx.ChainID != nil {
122+
cpy.ChainID.Set(tx.ChainID)
123+
}
124+
if tx.GasTipCap != nil {
125+
cpy.GasTipCap.Set(tx.GasTipCap)
126+
}
127+
if tx.GasFeeCap != nil {
128+
cpy.GasFeeCap.Set(tx.GasFeeCap)
129+
}
130+
if tx.BlobFeeCap != nil {
131+
cpy.BlobFeeCap.Set(tx.BlobFeeCap)
132+
}
133+
if tx.V != nil {
134+
cpy.V.Set(tx.V)
135+
}
136+
if tx.R != nil {
137+
cpy.R.Set(tx.R)
138+
}
139+
if tx.S != nil {
140+
cpy.S.Set(tx.S)
141+
}
142+
if tx.Sidecar != nil {
143+
cpy.Sidecar = &BlobTxSidecar{
144+
Blobs: append([]kzg4844.Blob(nil), tx.Sidecar.Blobs...),
145+
Commitments: append([]kzg4844.Commitment(nil), tx.Sidecar.Commitments...),
146+
Proofs: append([]kzg4844.Proof(nil), tx.Sidecar.Proofs...),
147+
}
148+
}
149+
return cpy
150+
}
151+
152+
// accessors for innerTx.
153+
func (tx *BlobTx) txType() byte { return BlobTxType }
154+
func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() }
155+
func (tx *BlobTx) accessList() AccessList { return tx.AccessList }
156+
func (tx *BlobTx) data() []byte { return tx.Data }
157+
func (tx *BlobTx) gas() uint64 { return tx.Gas }
158+
func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
159+
func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
160+
func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
161+
func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() }
162+
func (tx *BlobTx) nonce() uint64 { return tx.Nonce }
163+
func (tx *BlobTx) to() *common.Address { tmp := tx.To; return &tmp }
164+
func (tx *BlobTx) blobGas() uint64 { return params.BlobTxBlobGasPerBlob * uint64(len(tx.BlobHashes)) }
165+
166+
func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
167+
if baseFee == nil {
168+
return dst.Set(tx.GasFeeCap.ToBig())
169+
}
170+
tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
171+
if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
172+
tip.Set(tx.GasTipCap.ToBig())
173+
}
174+
return tip.Add(tip, baseFee)
175+
}
176+
177+
func (tx *BlobTx) rawSignatureValues() (v, r, s *big.Int) {
178+
return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
179+
}
180+
181+
func (tx *BlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
182+
tx.ChainID.SetFromBig(chainID)
183+
tx.V.SetFromBig(v)
184+
tx.R.SetFromBig(r)
185+
tx.S.SetFromBig(s)
186+
}
187+
188+
func (tx *BlobTx) withoutSidecar() *BlobTx {
189+
cpy := *tx
190+
cpy.Sidecar = nil
191+
return &cpy
192+
}
193+
194+
func (tx *BlobTx) encode(b *bytes.Buffer) error {
195+
if tx.Sidecar == nil {
196+
return rlp.Encode(b, tx)
197+
}
198+
inner := &blobTxWithBlobs{
199+
BlobTx: tx,
200+
Blobs: tx.Sidecar.Blobs,
201+
Commitments: tx.Sidecar.Commitments,
202+
Proofs: tx.Sidecar.Proofs,
203+
}
204+
return rlp.Encode(b, inner)
205+
}
206+
207+
func (tx *BlobTx) decode(input []byte) error {
208+
// Here we need to support two formats: the network protocol encoding of the tx (with
209+
// blobs) or the canonical encoding without blobs.
210+
//
211+
// The two encodings can be distinguished by checking whether the first element of the
212+
// input list is itself a list.
213+
214+
outerList, _, err := rlp.SplitList(input)
215+
if err != nil {
216+
return err
217+
}
218+
firstElemKind, _, _, err := rlp.Split(outerList)
219+
if err != nil {
220+
return err
221+
}
222+
223+
if firstElemKind != rlp.List {
224+
return rlp.DecodeBytes(input, tx)
225+
}
226+
// It's a tx with blobs.
227+
var inner blobTxWithBlobs
228+
if err := rlp.DecodeBytes(input, &inner); err != nil {
229+
return err
230+
}
231+
*tx = *inner.BlobTx
232+
tx.Sidecar = &BlobTxSidecar{
233+
Blobs: inner.Blobs,
234+
Commitments: inner.Commitments,
235+
Proofs: inner.Proofs,
236+
}
237+
return nil
238+
}

core/types/receipt.go

+12-16
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ type Receipt struct {
6161

6262
// Implementation fields: These fields are added by geth when processing a transaction.
6363
// They are stored in the chain database.
64-
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
65-
ContractAddress common.Address `json:"contractAddress"`
66-
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
64+
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
65+
ContractAddress common.Address `json:"contractAddress"`
66+
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
67+
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility
68+
BlobGasUsed uint64 `json:"blobGasUsed,omitempty"`
69+
BlobGasPrice *big.Int `json:"blobGasPrice,omitempty"`
6770

6871
// Inclusion information: These fields provide information about the inclusion of the
6972
// transaction corresponding to this receipt.
@@ -84,6 +87,7 @@ type receiptMarshaling struct {
8487
Status hexutil.Uint64
8588
CumulativeGasUsed hexutil.Uint64
8689
GasUsed hexutil.Uint64
90+
EffectiveGasPrice *hexutil.Big
8791
BlockNumber *hexutil.Big
8892
TransactionIndex hexutil.Uint
8993
L1Fee *hexutil.Big
@@ -203,18 +207,7 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
203207
if err != nil {
204208
return err
205209
}
206-
if len(b) == 0 {
207-
return errEmptyTypedReceipt
208-
}
209-
r.Type = b[0]
210-
if r.Type == AccessListTxType || r.Type == DynamicFeeTxType || r.Type == L1MessageTxType {
211-
var dec receiptRLP
212-
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
213-
return err
214-
}
215-
return r.setFromRLP(dec)
216-
}
217-
return ErrTxTypeNotSupported
210+
return r.decodeTyped(b)
218211
default:
219212
return rlp.ErrExpectedList
220213
}
@@ -243,7 +236,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
243236
return errEmptyTypedReceipt
244237
}
245238
switch b[0] {
246-
case DynamicFeeTxType, AccessListTxType, L1MessageTxType:
239+
case DynamicFeeTxType, AccessListTxType, BlobTxType, L1MessageTxType:
247240
var data receiptRLP
248241
err := rlp.DecodeBytes(b[1:], &data)
249242
if err != nil {
@@ -435,6 +428,9 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
435428
case DynamicFeeTxType:
436429
w.WriteByte(DynamicFeeTxType)
437430
rlp.Encode(w, data)
431+
case BlobTxType:
432+
w.WriteByte(BlobTxType)
433+
rlp.Encode(w, data)
438434
case L1MessageTxType:
439435
w.WriteByte(L1MessageTxType)
440436
rlp.Encode(w, data)

0 commit comments

Comments
 (0)