Skip to content

Commit f90eb3e

Browse files
rjl493456442MariusVanDerWijdenlightclient
authored
core, internal, miner, signer: convert legacy sidecar in Osaka fork (#32347)
This pull request implements #32235 , constructing blob sidecar in new format (cell proof) if the Osaka has been activated. Apart from that, it introduces a pre-conversion step in the blob pool before adding the txs. This mechanism is essential for handling the remote **legacy** blob txs from the network. One thing is still missing and probably is worthy being highlighted here: the blobpool may contain several legacy blob txs before the Osaka and these txs should be converted once Osaka is activated. While the `GetBlob` API in blobpool is capable for generating cell proofs at the runtime, converting legacy txs at one time is much cheaper overall. --------- Co-authored-by: MariusVanDerWijden <[email protected]> Co-authored-by: lightclient <[email protected]>
1 parent 52ec2b5 commit f90eb3e

File tree

8 files changed

+279
-113
lines changed

8 files changed

+279
-113
lines changed

core/txpool/blobpool/blobpool.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,17 +1397,46 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
13971397
return available
13981398
}
13991399

1400+
// convertSidecar converts the legacy sidecar in the submitted transactions
1401+
// if Osaka fork has been activated.
1402+
func (p *BlobPool) convertSidecar(txs []*types.Transaction) ([]*types.Transaction, []error) {
1403+
head := p.chain.CurrentBlock()
1404+
if !p.chain.Config().IsOsaka(head.Number, head.Time) {
1405+
return txs, make([]error, len(txs))
1406+
}
1407+
var errs []error
1408+
for _, tx := range txs {
1409+
sidecar := tx.BlobTxSidecar()
1410+
if sidecar == nil {
1411+
errs = append(errs, errors.New("missing sidecar in blob transaction"))
1412+
continue
1413+
}
1414+
if sidecar.Version == types.BlobSidecarVersion0 {
1415+
if err := sidecar.ToV1(); err != nil {
1416+
errs = append(errs, err)
1417+
continue
1418+
}
1419+
}
1420+
errs = append(errs, nil)
1421+
}
1422+
return txs, errs
1423+
}
1424+
14001425
// Add inserts a set of blob transactions into the pool if they pass validation (both
14011426
// consensus validity and pool restrictions).
14021427
//
14031428
// Note, if sync is set the method will block until all internal maintenance
14041429
// related to the add is finished. Only use this during tests for determinism.
14051430
func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error {
14061431
var (
1432+
errs []error
14071433
adds = make([]*types.Transaction, 0, len(txs))
1408-
errs = make([]error, len(txs))
14091434
)
1435+
txs, errs = p.convertSidecar(txs)
14101436
for i, tx := range txs {
1437+
if errs[i] != nil {
1438+
continue
1439+
}
14111440
errs[i] = p.add(tx)
14121441
if errs[i] == nil {
14131442
adds = append(adds, tx.WithoutBlobTxSidecar())

core/txpool/blobpool/blobpool_test.go

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"os"
2828
"path/filepath"
2929
"reflect"
30+
"slices"
3031
"sync"
3132
"testing"
3233

@@ -47,11 +48,12 @@ import (
4748
)
4849

4950
var (
50-
testBlobs []*kzg4844.Blob
51-
testBlobCommits []kzg4844.Commitment
52-
testBlobProofs []kzg4844.Proof
53-
testBlobVHashes [][32]byte
54-
testBlobIndices = make(map[[32]byte]int)
51+
testBlobs []*kzg4844.Blob
52+
testBlobCommits []kzg4844.Commitment
53+
testBlobProofs []kzg4844.Proof
54+
testBlobCellProofs [][]kzg4844.Proof
55+
testBlobVHashes [][32]byte
56+
testBlobIndices = make(map[[32]byte]int)
5557
)
5658

5759
const testMaxBlobsPerBlock = 6
@@ -67,6 +69,9 @@ func init() {
6769
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
6870
testBlobProofs = append(testBlobProofs, testBlobProof)
6971

72+
testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob)
73+
testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof)
74+
7075
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
7176
testBlobIndices[testBlobVHash] = len(testBlobVHashes)
7277
testBlobVHashes = append(testBlobVHashes, testBlobVHash)
@@ -416,24 +421,40 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
416421
hashes = append(hashes, tx.vhashes...)
417422
}
418423
}
419-
blobs, _, proofs, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
424+
blobs1, _, proofs1, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
425+
if err != nil {
426+
t.Fatal(err)
427+
}
428+
blobs2, _, proofs2, err := pool.GetBlobs(hashes, types.BlobSidecarVersion1)
420429
if err != nil {
421430
t.Fatal(err)
422431
}
423432
// Cross validate what we received vs what we wanted
424-
if len(blobs) != len(hashes) || len(proofs) != len(hashes) {
425-
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes))
433+
if len(blobs1) != len(hashes) || len(proofs1) != len(hashes) {
434+
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs1), len(proofs1), len(hashes))
435+
return
436+
}
437+
if len(blobs2) != len(hashes) || len(proofs2) != len(hashes) {
438+
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want blobs %d, want proofs: %d", len(blobs2), len(proofs2), len(hashes), len(hashes))
426439
return
427440
}
428441
for i, hash := range hashes {
429442
// If an item is missing, but shouldn't, error
430-
if blobs[i] == nil || proofs[i] == nil {
443+
if blobs1[i] == nil || proofs1[i] == nil {
444+
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
445+
continue
446+
}
447+
if blobs2[i] == nil || proofs2[i] == nil {
431448
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
432449
continue
433450
}
434451
// Item retrieved, make sure it matches the expectation
435452
index := testBlobIndices[hash]
436-
if *blobs[i] != *testBlobs[index] || proofs[i][0] != testBlobProofs[index] {
453+
if *blobs1[i] != *testBlobs[index] || proofs1[i][0] != testBlobProofs[index] {
454+
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
455+
continue
456+
}
457+
if *blobs2[i] != *testBlobs[index] || !slices.Equal(proofs2[i], testBlobCellProofs[index]) {
437458
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
438459
continue
439460
}
@@ -1668,6 +1689,49 @@ func TestAdd(t *testing.T) {
16681689
}
16691690
}
16701691

1692+
// Tests that adding the transactions with legacy sidecar and expect them to
1693+
// be converted to new format correctly.
1694+
func TestAddLegacyBlobTx(t *testing.T) {
1695+
var (
1696+
key1, _ = crypto.GenerateKey()
1697+
key2, _ = crypto.GenerateKey()
1698+
1699+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
1700+
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
1701+
)
1702+
1703+
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
1704+
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
1705+
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
1706+
statedb.Commit(0, true, false)
1707+
1708+
chain := &testBlockChain{
1709+
config: params.MergedTestChainConfig,
1710+
basefee: uint256.NewInt(1050),
1711+
blobfee: uint256.NewInt(105),
1712+
statedb: statedb,
1713+
}
1714+
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
1715+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
1716+
t.Fatalf("failed to create blob pool: %v", err)
1717+
}
1718+
1719+
// Attempt to add legacy blob transactions.
1720+
var (
1721+
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0)
1722+
tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 6, key2, types.BlobSidecarVersion0)
1723+
tx3 = makeMultiBlobTx(1, 1, 800, 70, 6, 12, key2, types.BlobSidecarVersion1)
1724+
)
1725+
errs := pool.Add([]*types.Transaction{tx1, tx2, tx3}, true)
1726+
for _, err := range errs {
1727+
if err != nil {
1728+
t.Fatalf("failed to add tx: %v", err)
1729+
}
1730+
}
1731+
verifyPoolInternals(t, pool)
1732+
pool.Close()
1733+
}
1734+
16711735
func TestGetBlobs(t *testing.T) {
16721736
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
16731737

core/txpool/validation.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"math/big"
2323

2424
"github.com/ethereum/go-ethereum/common"
25-
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
2625
"github.com/ethereum/go-ethereum/core"
2726
"github.com/ethereum/go-ethereum/core/state"
2827
"github.com/ethereum/go-ethereum/core/types"
@@ -167,9 +166,8 @@ func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationO
167166
if len(hashes) == 0 {
168167
return errors.New("blobless blob transaction")
169168
}
170-
maxBlobs := eip4844.MaxBlobsPerBlock(opts.Config, head.Time)
171-
if len(hashes) > maxBlobs {
172-
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
169+
if len(hashes) > params.BlobTxMaxBlobs {
170+
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobs)
173171
}
174172
if len(sidecar.Blobs) != len(hashes) {
175173
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))

internal/ethapi/api.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,8 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
14971497

14981498
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
14991499
// transaction pool.
1500+
//
1501+
// This API is not capable for submitting blob transaction with sidecar.
15001502
func (api *TransactionAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) {
15011503
// Look up the wallet containing the requested signer
15021504
account := accounts.Account{Address: args.from()}
@@ -1517,7 +1519,7 @@ func (api *TransactionAPI) SendTransaction(ctx context.Context, args Transaction
15171519
}
15181520

15191521
// Set some sanity defaults and terminate on failure
1520-
if err := args.setDefaults(ctx, api.b, false); err != nil {
1522+
if err := args.setDefaults(ctx, api.b, sidecarConfig{}); err != nil {
15211523
return common.Hash{}, err
15221524
}
15231525
// Assemble the transaction and sign with the wallet
@@ -1534,10 +1536,19 @@ func (api *TransactionAPI) SendTransaction(ctx context.Context, args Transaction
15341536
// on a given unsigned transaction, and returns it to the caller for further
15351537
// processing (signing + broadcast).
15361538
func (api *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
1537-
args.blobSidecarAllowed = true
1538-
15391539
// Set some sanity defaults and terminate on failure
1540-
if err := args.setDefaults(ctx, api.b, false); err != nil {
1540+
sidecarVersion := types.BlobSidecarVersion0
1541+
if len(args.Blobs) > 0 {
1542+
h := api.b.CurrentHeader()
1543+
if api.b.ChainConfig().IsOsaka(h.Number, h.Time) {
1544+
sidecarVersion = types.BlobSidecarVersion1
1545+
}
1546+
}
1547+
config := sidecarConfig{
1548+
blobSidecarAllowed: true,
1549+
blobSidecarVersion: sidecarVersion,
1550+
}
1551+
if err := args.setDefaults(ctx, api.b, config); err != nil {
15411552
return nil, err
15421553
}
15431554
// Assemble the transaction and obtain rlp
@@ -1594,8 +1605,6 @@ type SignTransactionResult struct {
15941605
// The node needs to have the private key of the account corresponding with
15951606
// the given from address and it needs to be unlocked.
15961607
func (api *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
1597-
args.blobSidecarAllowed = true
1598-
15991608
if args.Gas == nil {
16001609
return nil, errors.New("gas not specified")
16011610
}
@@ -1605,7 +1614,19 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction
16051614
if args.Nonce == nil {
16061615
return nil, errors.New("nonce not specified")
16071616
}
1608-
if err := args.setDefaults(ctx, api.b, false); err != nil {
1617+
sidecarVersion := types.BlobSidecarVersion0
1618+
if len(args.Blobs) > 0 {
1619+
h := api.b.CurrentHeader()
1620+
if api.b.ChainConfig().IsOsaka(h.Number, h.Time) {
1621+
sidecarVersion = types.BlobSidecarVersion1
1622+
}
1623+
}
1624+
1625+
config := sidecarConfig{
1626+
blobSidecarAllowed: true,
1627+
blobSidecarVersion: sidecarVersion,
1628+
}
1629+
if err := args.setDefaults(ctx, api.b, config); err != nil {
16091630
return nil, err
16101631
}
16111632
// Before actually sign the transaction, ensure the transaction fee is reasonable.
@@ -1621,7 +1642,7 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction
16211642
// no longer retains the blobs, only the blob hashes. In this step, we need
16221643
// to put back the blob(s).
16231644
if args.IsEIP4844() {
1624-
signed = signed.WithBlobTxSidecar(types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs))
1645+
signed = signed.WithBlobTxSidecar(types.NewBlobTxSidecar(sidecarVersion, args.Blobs, args.Commitments, args.Proofs))
16251646
}
16261647
data, err := signed.MarshalBinary()
16271648
if err != nil {
@@ -1656,11 +1677,13 @@ func (api *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) {
16561677

16571678
// Resend accepts an existing transaction and a new gas price and limit. It will remove
16581679
// the given transaction from the pool and reinsert it with the new gas price and limit.
1680+
//
1681+
// This API is not capable for submitting blob transaction with sidecar.
16591682
func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
16601683
if sendArgs.Nonce == nil {
16611684
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
16621685
}
1663-
if err := sendArgs.setDefaults(ctx, api.b, false); err != nil {
1686+
if err := sendArgs.setDefaults(ctx, api.b, sidecarConfig{}); err != nil {
16641687
return common.Hash{}, err
16651688
}
16661689
matchTx := sendArgs.ToTransaction(types.LegacyTxType)

0 commit comments

Comments
 (0)