Skip to content

Commit e648d55

Browse files
authored
Merge pull request #84 from mantlenetworkio/feature/eigenda
[R4R] support blob transaction
2 parents 87c2ac2 + 5676c67 commit e648d55

File tree

7 files changed

+221
-2
lines changed

7 files changed

+221
-2
lines changed

consensus/misc/eip4844/eip4844.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 eip4844
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"math/big"
23+
24+
"github.com/ethereum/go-ethereum/core/types"
25+
)
26+
27+
var (
28+
BlobTxBytesPerFieldElement uint64 = 32 // Size in bytes of a field element
29+
BlobTxFieldElementsPerBlob uint64 = 4096 // Number of field elements stored in a single data blob
30+
BlobTxHashVersion uint64 = 0x01 // Version byte of the commitment hash
31+
BlobTxBlobGasPerBlob uint64 = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
32+
BlobTxMinBlobGasprice uint64 = 1 // Minimum gas price for data blobs
33+
BlobTxBlobGaspriceUpdateFraction uint64 = 3338477 // Controls the maximum rate of change for blob gas price
34+
BlobTxPointEvaluationPrecompileGas uint64 = 50000 // Gas price for the point evaluation precompile.
35+
36+
BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
37+
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block
38+
39+
minBlobGasPrice = big.NewInt(int64(BlobTxMinBlobGasprice))
40+
blobGaspriceUpdateFraction = big.NewInt(int64(BlobTxBlobGaspriceUpdateFraction))
41+
)
42+
43+
// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
44+
// if the current block contains no transactions, the excessBlobGas is updated
45+
// accordingly.
46+
func VerifyEIP4844Header(parent, header *types.Header) error {
47+
// Verify the header is not malformed
48+
if header.ExcessBlobGas == nil {
49+
return errors.New("header is missing excessBlobGas")
50+
}
51+
if header.BlobGasUsed == nil {
52+
return errors.New("header is missing blobGasUsed")
53+
}
54+
// Verify that the blob gas used remains within reasonable limits.
55+
if *header.BlobGasUsed > MaxBlobGasPerBlock {
56+
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, MaxBlobGasPerBlock)
57+
}
58+
if *header.BlobGasUsed%BlobTxBlobGasPerBlob != 0 {
59+
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, BlobTxBlobGasPerBlob)
60+
}
61+
// Verify the excessBlobGas is correct based on the parent header
62+
var (
63+
parentExcessBlobGas uint64
64+
parentBlobGasUsed uint64
65+
)
66+
if parent.ExcessBlobGas != nil {
67+
parentExcessBlobGas = *parent.ExcessBlobGas
68+
parentBlobGasUsed = *parent.BlobGasUsed
69+
}
70+
expectedExcessBlobGas := CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
71+
if *header.ExcessBlobGas != expectedExcessBlobGas {
72+
return fmt.Errorf("invalid excessBlobGas: have %d, want %d, parent excessBlobGas %d, parent blobDataUsed %d",
73+
*header.ExcessBlobGas, expectedExcessBlobGas, parentExcessBlobGas, parentBlobGasUsed)
74+
}
75+
return nil
76+
}
77+
78+
// CalcExcessBlobGas calculates the excess blob gas after applying the set of
79+
// blobs on top of the excess blob gas.
80+
func CalcExcessBlobGas(parentExcessBlobGas uint64, parentBlobGasUsed uint64) uint64 {
81+
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
82+
if excessBlobGas < BlobTxTargetBlobGasPerBlock {
83+
return 0
84+
}
85+
return excessBlobGas - BlobTxTargetBlobGasPerBlock
86+
}
87+
88+
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
89+
func CalcBlobFee(excessBlobGas uint64) *big.Int {
90+
return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(excessBlobGas), blobGaspriceUpdateFraction)
91+
}
92+
93+
// fakeExponential approximates factor * e ** (numerator / denominator) using
94+
// Taylor expansion.
95+
func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
96+
var (
97+
output = new(big.Int)
98+
accum = new(big.Int).Mul(factor, denominator)
99+
)
100+
for i := 1; accum.Sign() > 0; i++ {
101+
output.Add(output, accum)
102+
103+
accum.Mul(accum, numerator)
104+
accum.Div(accum, denominator)
105+
accum.Div(accum, big.NewInt(int64(i)))
106+
}
107+
return output.Div(output, denominator)
108+
}

core/types/deposit_tx.go

Lines changed: 10 additions & 0 deletions
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/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/rlp"
2325
)
2426

2527
const DepositTxType = 0x7E
@@ -103,3 +105,11 @@ func (tx *DepositTx) rawSignatureValues() (v, r, s *big.Int) {
103105
func (tx *DepositTx) setSignatureValues(chainID, v, r, s *big.Int) {
104106
// this is a noop for deposit transactions
105107
}
108+
109+
func (tx *DepositTx) encode(b *bytes.Buffer) error {
110+
return rlp.Encode(b, tx)
111+
}
112+
113+
func (tx *DepositTx) decode(input []byte) error {
114+
return rlp.DecodeBytes(input, tx)
115+
}

core/types/transaction.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type TxData interface {
104104
// copy of the computed value, i.e. callers are allowed to mutate the result.
105105
// Method implementations can use 'dst' to store the result.
106106
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
107+
108+
encode(*bytes.Buffer) error
109+
decode([]byte) error
107110
}
108111

109112
// EncodeRLP implements rlp.Encoder
@@ -124,7 +127,7 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error {
124127
// encodeTyped writes the canonical encoding of a typed transaction to w.
125128
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
126129
w.WriteByte(tx.Type())
127-
return rlp.Encode(w, tx.inner)
130+
return tx.inner.encode(w)
128131
}
129132

130133
// MarshalBinary returns the canonical encoding of the transaction.

core/types/transaction_signing.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
8787
if chainID == nil {
8888
return HomesteadSigner{}
8989
}
90-
return NewLondonSigner(chainID)
90+
return NewCancunSigner(chainID)
9191
}
9292

9393
// SignTx signs the transaction using the given signer and private key.
@@ -170,6 +170,75 @@ type Signer interface {
170170
Equal(Signer) bool
171171
}
172172

173+
type cancunSigner struct{ londonSigner }
174+
175+
// NewCancunSigner returns a signer that accepts
176+
// - EIP-4844 blob transactions
177+
// - EIP-1559 dynamic fee transactions
178+
// - EIP-2930 access list transactions,
179+
// - EIP-155 replay protected transactions, and
180+
// - legacy Homestead transactions.
181+
func NewCancunSigner(chainId *big.Int) Signer {
182+
return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}}
183+
}
184+
185+
func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) {
186+
if tx.Type() != BlobTxType {
187+
return s.londonSigner.Sender(tx)
188+
}
189+
V, R, S := tx.RawSignatureValues()
190+
// Blob txs are defined to use 0 and 1 as their recovery
191+
// id, add 27 to become equivalent to unprotected Homestead signatures.
192+
V = new(big.Int).Add(V, big.NewInt(27))
193+
if tx.ChainId().Cmp(s.chainId) != 0 {
194+
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
195+
}
196+
return recoverPlain(s.Hash(tx), R, S, V, true)
197+
}
198+
199+
func (s cancunSigner) Equal(s2 Signer) bool {
200+
x, ok := s2.(cancunSigner)
201+
return ok && x.chainId.Cmp(s.chainId) == 0
202+
}
203+
204+
func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
205+
txdata, ok := tx.inner.(*BlobTx)
206+
if !ok {
207+
return s.londonSigner.SignatureValues(tx, sig)
208+
}
209+
// Check that chain ID of tx matches the signer. We also accept ID zero here,
210+
// because it indicates that the chain ID was not specified in the tx.
211+
if txdata.ChainID.Sign() != 0 && txdata.ChainID.ToBig().Cmp(s.chainId) != 0 {
212+
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
213+
}
214+
R, S, _ = decodeSignature(sig)
215+
V = big.NewInt(int64(sig[64]))
216+
return R, S, V, nil
217+
}
218+
219+
// Hash returns the hash to be signed by the sender.
220+
// It does not uniquely identify the transaction.
221+
func (s cancunSigner) Hash(tx *Transaction) common.Hash {
222+
if tx.Type() != BlobTxType {
223+
return s.londonSigner.Hash(tx)
224+
}
225+
return prefixedRlpHash(
226+
tx.Type(),
227+
[]interface{}{
228+
s.chainId,
229+
tx.Nonce(),
230+
tx.GasTipCap(),
231+
tx.GasFeeCap(),
232+
tx.Gas(),
233+
tx.To(),
234+
tx.Value(),
235+
tx.Data(),
236+
tx.AccessList(),
237+
tx.BlobGasFeeCap(),
238+
tx.BlobHashes(),
239+
})
240+
}
241+
173242
type londonSigner struct{ eip2930Signer }
174243

175244
// NewLondonSigner returns a signer that accepts

core/types/tx_access_list.go

Lines changed: 10 additions & 0 deletions
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/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/rlp"
2325
)
2426

2527
//go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go
@@ -118,3 +120,11 @@ func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
118120
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
119121
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
120122
}
123+
124+
func (tx *AccessListTx) encode(b *bytes.Buffer) error {
125+
return rlp.Encode(b, tx)
126+
}
127+
128+
func (tx *AccessListTx) decode(input []byte) error {
129+
return rlp.DecodeBytes(input, tx)
130+
}

core/types/tx_dynamic_fee.go

Lines changed: 10 additions & 0 deletions
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/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/rlp"
2325
)
2426

2527
type DynamicFeeTx struct {
@@ -113,3 +115,11 @@ func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
113115
func (tx *DynamicFeeTx) 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 *DynamicFeeTx) encode(b *bytes.Buffer) error {
120+
return rlp.Encode(b, tx)
121+
}
122+
123+
func (tx *DynamicFeeTx) decode(input []byte) error {
124+
return rlp.DecodeBytes(input, tx)
125+
}

core/types/tx_legacy.go

Lines changed: 9 additions & 0 deletions
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/ethereum/go-ethereum/common"
@@ -115,3 +116,11 @@ func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
115116
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
116117
tx.V, tx.R, tx.S = v, r, s
117118
}
119+
120+
func (tx *LegacyTx) encode(*bytes.Buffer) error {
121+
panic("encode called on LegacyTx")
122+
}
123+
124+
func (tx *LegacyTx) decode([]byte) error {
125+
panic("decode called on LegacyTx)")
126+
}

0 commit comments

Comments
 (0)