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 <m.vanderwijden@live.de>
Co-authored-by: lightclient <lightclient@protonmail.com>
This commit is contained in:
rjl493456442 2025-08-28 01:03:29 +08:00 committed by GitHub
parent 52ec2b5f47
commit f90eb3e507
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 281 additions and 115 deletions

View file

@ -1397,6 +1397,31 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
return available
}
// convertSidecar converts the legacy sidecar in the submitted transactions
// if Osaka fork has been activated.
func (p *BlobPool) convertSidecar(txs []*types.Transaction) ([]*types.Transaction, []error) {
head := p.chain.CurrentBlock()
if !p.chain.Config().IsOsaka(head.Number, head.Time) {
return txs, make([]error, len(txs))
}
var errs []error
for _, tx := range txs {
sidecar := tx.BlobTxSidecar()
if sidecar == nil {
errs = append(errs, errors.New("missing sidecar in blob transaction"))
continue
}
if sidecar.Version == types.BlobSidecarVersion0 {
if err := sidecar.ToV1(); err != nil {
errs = append(errs, err)
continue
}
}
errs = append(errs, nil)
}
return txs, errs
}
// Add inserts a set of blob transactions into the pool if they pass validation (both
// consensus validity and pool restrictions).
//
@ -1404,10 +1429,14 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
// related to the add is finished. Only use this during tests for determinism.
func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error {
var (
errs []error
adds = make([]*types.Transaction, 0, len(txs))
errs = make([]error, len(txs))
)
txs, errs = p.convertSidecar(txs)
for i, tx := range txs {
if errs[i] != nil {
continue
}
errs[i] = p.add(tx)
if errs[i] == nil {
adds = append(adds, tx.WithoutBlobTxSidecar())

View file

@ -27,6 +27,7 @@ import (
"os"
"path/filepath"
"reflect"
"slices"
"sync"
"testing"
@ -47,11 +48,12 @@ import (
)
var (
testBlobs []*kzg4844.Blob
testBlobCommits []kzg4844.Commitment
testBlobProofs []kzg4844.Proof
testBlobVHashes [][32]byte
testBlobIndices = make(map[[32]byte]int)
testBlobs []*kzg4844.Blob
testBlobCommits []kzg4844.Commitment
testBlobProofs []kzg4844.Proof
testBlobCellProofs [][]kzg4844.Proof
testBlobVHashes [][32]byte
testBlobIndices = make(map[[32]byte]int)
)
const testMaxBlobsPerBlock = 6
@ -67,6 +69,9 @@ func init() {
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
testBlobProofs = append(testBlobProofs, testBlobProof)
testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob)
testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof)
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
testBlobIndices[testBlobVHash] = len(testBlobVHashes)
testBlobVHashes = append(testBlobVHashes, testBlobVHash)
@ -416,24 +421,40 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
hashes = append(hashes, tx.vhashes...)
}
}
blobs, _, proofs, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
blobs1, _, proofs1, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
if err != nil {
t.Fatal(err)
}
blobs2, _, proofs2, err := pool.GetBlobs(hashes, types.BlobSidecarVersion1)
if err != nil {
t.Fatal(err)
}
// Cross validate what we received vs what we wanted
if len(blobs) != len(hashes) || len(proofs) != len(hashes) {
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes))
if len(blobs1) != len(hashes) || len(proofs1) != len(hashes) {
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs1), len(proofs1), len(hashes))
return
}
if len(blobs2) != len(hashes) || len(proofs2) != len(hashes) {
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want blobs %d, want proofs: %d", len(blobs2), len(proofs2), len(hashes), len(hashes))
return
}
for i, hash := range hashes {
// If an item is missing, but shouldn't, error
if blobs[i] == nil || proofs[i] == nil {
if blobs1[i] == nil || proofs1[i] == nil {
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
continue
}
if blobs2[i] == nil || proofs2[i] == nil {
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
continue
}
// Item retrieved, make sure it matches the expectation
index := testBlobIndices[hash]
if *blobs[i] != *testBlobs[index] || proofs[i][0] != testBlobProofs[index] {
if *blobs1[i] != *testBlobs[index] || proofs1[i][0] != testBlobProofs[index] {
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
continue
}
if *blobs2[i] != *testBlobs[index] || !slices.Equal(proofs2[i], testBlobCellProofs[index]) {
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
continue
}
@ -1668,6 +1689,49 @@ func TestAdd(t *testing.T) {
}
}
// Tests that adding the transactions with legacy sidecar and expect them to
// be converted to new format correctly.
func TestAddLegacyBlobTx(t *testing.T) {
var (
key1, _ = crypto.GenerateKey()
key2, _ = crypto.GenerateKey()
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
)
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
statedb.Commit(0, true, false)
chain := &testBlockChain{
config: params.MergedTestChainConfig,
basefee: uint256.NewInt(1050),
blobfee: uint256.NewInt(105),
statedb: statedb,
}
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}
// Attempt to add legacy blob transactions.
var (
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0)
tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 6, key2, types.BlobSidecarVersion0)
tx3 = makeMultiBlobTx(1, 1, 800, 70, 6, 12, key2, types.BlobSidecarVersion1)
)
errs := pool.Add([]*types.Transaction{tx1, tx2, tx3}, true)
for _, err := range errs {
if err != nil {
t.Fatalf("failed to add tx: %v", err)
}
}
verifyPoolInternals(t, pool)
pool.Close()
}
func TestGetBlobs(t *testing.T) {
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))

View file

@ -22,7 +22,6 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@ -167,9 +166,8 @@ func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationO
if len(hashes) == 0 {
return errors.New("blobless blob transaction")
}
maxBlobs := eip4844.MaxBlobsPerBlock(opts.Config, head.Time)
if len(hashes) > maxBlobs {
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
if len(hashes) > params.BlobTxMaxBlobs {
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobs)
}
if len(sidecar.Blobs) != len(hashes) {
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))

View file

@ -1497,6 +1497,8 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
//
// This API is not capable for submitting blob transaction with sidecar.
func (api *TransactionAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) {
// Look up the wallet containing the requested signer
account := accounts.Account{Address: args.from()}
@ -1517,7 +1519,7 @@ func (api *TransactionAPI) SendTransaction(ctx context.Context, args Transaction
}
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, api.b, false); err != nil {
if err := args.setDefaults(ctx, api.b, sidecarConfig{}); err != nil {
return common.Hash{}, err
}
// Assemble the transaction and sign with the wallet
@ -1534,10 +1536,19 @@ func (api *TransactionAPI) SendTransaction(ctx context.Context, args Transaction
// on a given unsigned transaction, and returns it to the caller for further
// processing (signing + broadcast).
func (api *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
args.blobSidecarAllowed = true
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, api.b, false); err != nil {
sidecarVersion := types.BlobSidecarVersion0
if len(args.Blobs) > 0 {
h := api.b.CurrentHeader()
if api.b.ChainConfig().IsOsaka(h.Number, h.Time) {
sidecarVersion = types.BlobSidecarVersion1
}
}
config := sidecarConfig{
blobSidecarAllowed: true,
blobSidecarVersion: sidecarVersion,
}
if err := args.setDefaults(ctx, api.b, config); err != nil {
return nil, err
}
// Assemble the transaction and obtain rlp
@ -1594,8 +1605,6 @@ type SignTransactionResult struct {
// The node needs to have the private key of the account corresponding with
// the given from address and it needs to be unlocked.
func (api *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
args.blobSidecarAllowed = true
if args.Gas == nil {
return nil, errors.New("gas not specified")
}
@ -1605,7 +1614,19 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction
if args.Nonce == nil {
return nil, errors.New("nonce not specified")
}
if err := args.setDefaults(ctx, api.b, false); err != nil {
sidecarVersion := types.BlobSidecarVersion0
if len(args.Blobs) > 0 {
h := api.b.CurrentHeader()
if api.b.ChainConfig().IsOsaka(h.Number, h.Time) {
sidecarVersion = types.BlobSidecarVersion1
}
}
config := sidecarConfig{
blobSidecarAllowed: true,
blobSidecarVersion: sidecarVersion,
}
if err := args.setDefaults(ctx, api.b, config); err != nil {
return nil, err
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
@ -1621,7 +1642,7 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction
// no longer retains the blobs, only the blob hashes. In this step, we need
// to put back the blob(s).
if args.IsEIP4844() {
signed = signed.WithBlobTxSidecar(types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs))
signed = signed.WithBlobTxSidecar(types.NewBlobTxSidecar(sidecarVersion, args.Blobs, args.Commitments, args.Proofs))
}
data, err := signed.MarshalBinary()
if err != nil {
@ -1656,11 +1677,13 @@ func (api *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) {
// Resend accepts an existing transaction and a new gas price and limit. It will remove
// the given transaction from the pool and reinsert it with the new gas price and limit.
//
// This API is not capable for submitting blob transaction with sidecar.
func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
if sendArgs.Nonce == nil {
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
}
if err := sendArgs.setDefaults(ctx, api.b, false); err != nil {
if err := sendArgs.setDefaults(ctx, api.b, sidecarConfig{}); err != nil {
return common.Hash{}, err
}
matchTx := sendArgs.ToTransaction(types.LegacyTxType)

View file

@ -34,11 +34,9 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@ -56,6 +54,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/blocktest"
"github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
@ -2671,19 +2670,53 @@ func TestSendBlobTransaction(t *testing.T) {
func TestFillBlobTransaction(t *testing.T) {
t.Parallel()
testFillBlobTransaction(t, false)
testFillBlobTransaction(t, true)
}
func testFillBlobTransaction(t *testing.T, osaka bool) {
// Initialize test accounts
config := *params.MergedTestChainConfig
if !osaka {
config.OsakaTime = nil
}
var (
key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
to = crypto.PubkeyToAddress(key.PublicKey)
genesis = &core.Genesis{
Config: params.MergedTestChainConfig,
Config: &config,
Alloc: types.GenesisAlloc{},
}
emptyBlob = new(kzg4844.Blob)
emptyBlobs = []kzg4844.Blob{*emptyBlob}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
emptyBlob = new(kzg4844.Blob)
emptyBlobs = []kzg4844.Blob{*emptyBlob}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
emptyBlobCellProofs, _ = kzg4844.ComputeCellProofs(emptyBlob)
emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
fillEmptyKZGProofs = func(blobs int) []kzg4844.Proof {
if osaka {
return make([]kzg4844.Proof, blobs*kzg4844.CellProofsPerBlob)
}
return make([]kzg4844.Proof, blobs)
}
expectSidecar = func() *types.BlobTxSidecar {
if osaka {
return types.NewBlobTxSidecar(
types.BlobSidecarVersion1,
emptyBlobs,
[]kzg4844.Commitment{emptyBlobCommit},
emptyBlobCellProofs,
)
}
return types.NewBlobTxSidecar(
types.BlobSidecarVersion0,
emptyBlobs,
[]kzg4844.Commitment{emptyBlobCommit},
[]kzg4844.Proof{emptyBlobProof},
)
}
)
b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
b.SetPoS()
@ -2743,7 +2776,7 @@ func TestFillBlobTransaction(t *testing.T) {
Commitments: []kzg4844.Commitment{{}, {}},
Proofs: []kzg4844.Proof{{}},
},
err: `number of blobs and proofs mismatch (have=1, want=2)`,
err: fmt.Sprintf(`number of blobs and proofs mismatch (have=1, want=%d)`, len(fillEmptyKZGProofs(2))),
},
{
name: "TestInvalidProofVerification",
@ -2753,7 +2786,7 @@ func TestFillBlobTransaction(t *testing.T) {
Value: (*hexutil.Big)(big.NewInt(1)),
Blobs: []kzg4844.Blob{{}, {}},
Commitments: []kzg4844.Commitment{{}, {}},
Proofs: []kzg4844.Proof{{}, {}},
Proofs: fillEmptyKZGProofs(2),
},
err: `failed to verify blob proof: short buffer`,
},
@ -2769,7 +2802,7 @@ func TestFillBlobTransaction(t *testing.T) {
},
want: &result{
Hashes: []common.Hash{emptyBlobHash},
Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}),
Sidecar: expectSidecar(),
},
},
{
@ -2785,7 +2818,7 @@ func TestFillBlobTransaction(t *testing.T) {
},
want: &result{
Hashes: []common.Hash{emptyBlobHash},
Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}),
Sidecar: expectSidecar(),
},
},
{
@ -2811,7 +2844,7 @@ func TestFillBlobTransaction(t *testing.T) {
},
want: &result{
Hashes: []common.Hash{emptyBlobHash},
Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}),
Sidecar: expectSidecar(),
},
},
}

View file

@ -70,9 +70,6 @@ type TransactionArgs struct {
// For SetCodeTxType
AuthorizationList []types.SetCodeAuthorization `json:"authorizationList"`
// This configures whether blobs are allowed to be passed.
blobSidecarAllowed bool
}
// from retrieves the transaction sender address.
@ -94,9 +91,17 @@ func (args *TransactionArgs) data() []byte {
return nil
}
// sidecarConfig defines the options for deriving missing fields of transactions.
type sidecarConfig struct {
// This configures whether blobs are allowed to be passed and
// the associated sidecar version should be attached.
blobSidecarAllowed bool
blobSidecarVersion byte
}
// setDefaults fills in default values for unspecified tx fields.
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error {
if err := args.setBlobTxSidecar(ctx); err != nil {
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config sidecarConfig) error {
if err := args.setBlobTxSidecar(ctx, config); err != nil {
return err
}
if err := args.setFeeDefaults(ctx, b, b.CurrentHeader()); err != nil {
@ -119,11 +124,10 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
// BlobTx fields
if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
return errors.New(`need at least 1 blob for a blob transaction`)
return errors.New("need at least 1 blob for a blob transaction")
}
maxBlobs := eip4844.MaxBlobsPerBlock(b.ChainConfig(), b.CurrentHeader().Time)
if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobs {
return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobs)
if args.BlobHashes != nil && len(args.BlobHashes) > params.BlobTxMaxBlobs {
return fmt.Errorf("too many blobs in transaction (have=%d, max=%d)", len(args.BlobHashes), params.BlobTxMaxBlobs)
}
// create check
@ -137,36 +141,28 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
}
if args.Gas == nil {
if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls.
gas := hexutil.Uint64(b.RPCGasCap())
if gas == 0 {
gas = hexutil.Uint64(math.MaxUint64 / 2)
}
args.Gas = &gas
} else { // Estimate the gas usage otherwise.
// These fields are immutable during the estimation, safe to
// pass the pointer directly.
data := args.data()
callArgs := TransactionArgs{
From: args.From,
To: args.To,
GasPrice: args.GasPrice,
MaxFeePerGas: args.MaxFeePerGas,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
Value: args.Value,
Data: (*hexutil.Bytes)(&data),
AccessList: args.AccessList,
BlobFeeCap: args.BlobFeeCap,
BlobHashes: args.BlobHashes,
}
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, nil, b.RPCGasCap())
if err != nil {
return err
}
args.Gas = &estimated
log.Trace("Estimate gas usage automatically", "gas", args.Gas)
// These fields are immutable during the estimation, safe to
// pass the pointer directly.
data := args.data()
callArgs := TransactionArgs{
From: args.From,
To: args.To,
GasPrice: args.GasPrice,
MaxFeePerGas: args.MaxFeePerGas,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
Value: args.Value,
Data: (*hexutil.Bytes)(&data),
AccessList: args.AccessList,
BlobFeeCap: args.BlobFeeCap,
BlobHashes: args.BlobHashes,
}
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, nil, b.RPCGasCap())
if err != nil {
return err
}
args.Gas = &estimated
log.Trace("Estimated gas usage automatically", "gas", args.Gas)
}
// If chain id is provided, ensure it matches the local chain id. Otherwise, set the local
@ -283,18 +279,17 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ
}
// setBlobTxSidecar adds the blob tx
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context) error {
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sidecarConfig) error {
// No blobs, we're done.
if args.Blobs == nil {
return nil
}
// Passing blobs is not allowed in all contexts, only in specific methods.
if !args.blobSidecarAllowed {
if !config.blobSidecarAllowed {
return errors.New(`"blobs" is not supported for this RPC method`)
}
n := len(args.Blobs)
// Assume user provides either only blobs (w/o hashes), or
// blobs together with commitments and proofs.
if args.Commitments == nil && args.Proofs != nil {
@ -303,43 +298,77 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context) error {
return errors.New(`blob commitments provided while proofs were not`)
}
// len(blobs) == len(commitments) == len(proofs) == len(hashes)
if args.Commitments != nil && len(args.Commitments) != n {
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
}
if args.Proofs != nil && len(args.Proofs) != n {
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n)
}
// len(blobs) == len(commitments) == len(hashes)
n := len(args.Blobs)
if args.BlobHashes != nil && len(args.BlobHashes) != n {
return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)
}
if args.Commitments != nil && len(args.Commitments) != n {
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
}
// if V0: len(blobs) == len(proofs)
// if V1: len(blobs) == len(proofs) * 128
proofLen := n
if config.blobSidecarVersion == types.BlobSidecarVersion1 {
proofLen = n * kzg4844.CellProofsPerBlob
}
if args.Proofs != nil && len(args.Proofs) != proofLen {
if len(args.Proofs) != n {
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), proofLen)
}
// Unset the commitments and proofs, as they may be submitted in the legacy format
log.Debug("Unset legacy commitments and proofs", "blobs", n, "proofs", len(args.Proofs))
args.Commitments, args.Proofs = nil, nil
}
// Generate commitments and proofs if they are missing, or validate them if they
// are provided.
if args.Commitments == nil {
// Generate commitment and proof.
commitments := make([]kzg4844.Commitment, n)
proofs := make([]kzg4844.Proof, n)
var (
commitments = make([]kzg4844.Commitment, n)
proofs = make([]kzg4844.Proof, 0, proofLen)
)
for i, b := range args.Blobs {
c, err := kzg4844.BlobToCommitment(&b)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
}
commitments[i] = c
p, err := kzg4844.ComputeBlobProof(&b, c)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
switch config.blobSidecarVersion {
case types.BlobSidecarVersion0:
p, err := kzg4844.ComputeBlobProof(&b, c)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
}
proofs = append(proofs, p)
case types.BlobSidecarVersion1:
ps, err := kzg4844.ComputeCellProofs(&b)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
}
proofs = append(proofs, ps...)
}
proofs[i] = p
}
args.Commitments = commitments
args.Proofs = proofs
} else {
for i, b := range args.Blobs {
if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil {
switch config.blobSidecarVersion {
case types.BlobSidecarVersion0:
for i, b := range args.Blobs {
if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil {
return fmt.Errorf("failed to verify blob proof: %v", err)
}
}
case types.BlobSidecarVersion1:
if err := kzg4844.VerifyCellProofs(args.Blobs, args.Commitments, args.Proofs); err != nil {
return fmt.Errorf("failed to verify blob proof: %v", err)
}
}
}
// Generate blob hashes if they are missing, or validate them if they are provided.
hashes := make([]common.Hash, n)
hasher := sha256.New()
for i, c := range args.Commitments {
@ -527,8 +556,11 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction {
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
}
if args.Blobs != nil {
// TODO(rjl493456442, marius) support V1
data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs)
version := types.BlobSidecarVersion0
if len(args.Proofs) == len(args.Blobs)*kzg4844.CellProofsPerBlob {
version = types.BlobSidecarVersion1
}
data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(version, args.Blobs, args.Commitments, args.Proofs)
}
case types.DynamicFeeTxType:

View file

@ -344,7 +344,6 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
var (
isOsaka = miner.chainConfig.IsOsaka(env.header.Number, env.header.Time)
isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
gasLimit = env.header.GasLimit
)
@ -425,21 +424,6 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
if !env.txFitsSize(tx) {
break
}
// Make sure all transactions after osaka have cell proofs
if isOsaka {
if sidecar := tx.BlobTxSidecar(); sidecar != nil {
if sidecar.Version == types.BlobSidecarVersion0 {
log.Info("Including blob tx with v0 sidecar, recomputing proofs", "hash", ltx.Hash)
if err := sidecar.ToV1(); err != nil {
txs.Pop()
log.Warn("Failed to recompute cell proofs", "hash", ltx.Hash, "err", err)
continue
}
}
}
}
// Error may be ignored here. The error has already been checked
// during transaction acceptance in the transaction pool.
from, _ := types.Sender(env.signer, tx)

View file

@ -167,8 +167,11 @@ func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) {
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
}
if args.Blobs != nil {
// TODO(rjl493456442, marius) support V1
data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs)
version := types.BlobSidecarVersion0
if len(args.Proofs) == len(args.Blobs)*kzg4844.CellProofsPerBlob {
version = types.BlobSidecarVersion1
}
data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(version, args.Blobs, args.Commitments, args.Proofs)
}
case args.MaxFeePerGas != nil: