From d299d770b097c937684d7a2f8854afcaf5a2e658 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 18 Jan 2024 18:12:10 +0330 Subject: [PATCH 01/13] add json decoding for kzg types --- crypto/kzg4844/kzg4844.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 5969d1c2cee1..54d15ef8b04f 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -20,21 +20,45 @@ package kzg4844 import ( "embed" "errors" + "reflect" "sync/atomic" + + "github.com/ethereum/go-ethereum/common/hexutil" ) //go:embed trusted_setup.json var content embed.FS +var ( + blobT = reflect.TypeOf(Blob{}) + commitmentT = reflect.TypeOf(Commitment{}) + proofT = reflect.TypeOf(Proof{}) +) + // Blob represents a 4844 data blob. type Blob [131072]byte +// UnmarshalJSON parses a blob in hex syntax. +func (b *Blob) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(blobT, input, b[:]) +} + // Commitment is a serialized commitment to a polynomial. type Commitment [48]byte +// UnmarshalJSON parses a commitment in hex syntax. +func (c *Commitment) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:]) +} + // Proof is a serialized commitment to the quotient polynomial. type Proof [48]byte +// UnmarshalJSON parses a proof in hex syntax. +func (p *Proof) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(proofT, input, p[:]) +} + // Point is a BLS field element. type Point [32]byte From e469b41a450445c312aeec715a377f62bb52cb2f Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 18 Jan 2024 20:04:53 +0330 Subject: [PATCH 02/13] internal/ethapi: fill blob sidecar --- core/types/transaction.go | 20 ++++++ core/types/tx_blob.go | 10 ++- internal/ethapi/api.go | 2 +- internal/ethapi/transaction_args.go | 99 +++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 9ec0199a0383..4e361bc81f32 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -424,6 +424,26 @@ func (tx *Transaction) BlobGasFeeCapIntCmp(other *big.Int) int { return tx.BlobGasFeeCap().Cmp(other) } +// WithBlobTxSidecar returns a copy of tx with the blob sidecar. +func (tx *Transaction) WithBlobTxSidecar(sidecar *BlobTxSidecar) *Transaction { + blobtx, ok := tx.inner.(*BlobTx) + if !ok { + return tx + } + cpy := &Transaction{ + inner: blobtx.withSidecar(sidecar), + time: tx.time, + } + // Note: tx.size cache not carried over because the sidecar was not included in size! + if h := tx.hash.Load(); h != nil { + cpy.hash.Store(h) + } + if f := tx.from.Load(); f != nil { + cpy.from.Store(f) + } + return cpy +} + // WithoutBlobTxSidecar returns a copy of tx with the blob sidecar removed. func (tx *Transaction) WithoutBlobTxSidecar() *Transaction { blobtx, ok := tx.inner.(*BlobTx) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index da4a9b72f17a..c6c31d3488e2 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -63,7 +63,7 @@ type BlobTxSidecar struct { func (sc *BlobTxSidecar) BlobHashes() []common.Hash { h := make([]common.Hash, len(sc.Commitments)) for i := range sc.Blobs { - h[i] = blobHash(&sc.Commitments[i]) + h[i] = BlobHash(&sc.Commitments[i]) } return h } @@ -190,6 +190,12 @@ func (tx *BlobTx) withoutSidecar() *BlobTx { return &cpy } +func (tx *BlobTx) withSidecar(sidecar *BlobTxSidecar) *BlobTx { + cpy := *tx + tx.Sidecar = sidecar + return &cpy +} + func (tx *BlobTx) encode(b *bytes.Buffer) error { if tx.Sidecar == nil { return rlp.Encode(b, tx) @@ -236,7 +242,7 @@ func (tx *BlobTx) decode(input []byte) error { return nil } -func blobHash(commit *kzg4844.Commitment) common.Hash { +func BlobHash(commit *kzg4844.Commitment) common.Hash { hasher := sha256.New() hasher.Write(commit[:]) var vhash common.Hash diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index ee479d7139ab..598d0406d786 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1839,7 +1839,7 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr // FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields) // on a given unsigned transaction, and returns it to the caller for further // processing (signing + broadcast). -func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { +func (s *TransactionAPI) FillTransaction(ctx context.Context, args BlobTransactionArgs) (*SignTransactionResult, error) { // Set some sanity defaults and terminate on failure if err := args.setDefaults(ctx, s.b); err != nil { return nil, err diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 75dbe38a59e8..3279d6c412fe 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -29,11 +29,107 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" ) +var ( + maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob +) + +// BlobTransactionArgs represents the arguments to construct a new +// blob transaction. It includes the blob side-car. +type BlobTransactionArgs struct { + TransactionArgs + Blobs []kzg4844.Blob `json:"blobs"` + Commitments []kzg4844.Commitment `json:"commitments"` + Proofs []kzg4844.Proof `json:"proofs"` +} + +func (args *BlobTransactionArgs) setDefaults(ctx context.Context, b Backend) error { + // First validate and fill in other fields to avoid + // the expensive blob proof computation for an invalid transaction. + if err := args.TransactionArgs.setDefaults(ctx, b); err != nil { + return err + } + // No blobs, we're done. + if args.Blobs == nil { + return nil + } + 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 { + return errors.New(`blob proofs provided while commitments were not`) + } else if args.Commitments != nil && args.Proofs == nil { + 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) + } + 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 { + // Generate commitment and proof. + commitments := make([]kzg4844.Commitment, n) + proofs := make([]kzg4844.Proof, n) + for i, b := range args.Blobs { + c, err := kzg4844.BlobToCommitment(b) + if err != nil { + return err + } + commitments[i] = c + p, err := kzg4844.ComputeBlobProof(b, c) + if err != nil { + return err + } + 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 { + return fmt.Errorf("failed to verify blob proof: %v", err) + } + } + } + hashes := make([]common.Hash, n) + for i, c := range args.Commitments { + hashes[i] = types.BlobHash(&c) + } + if args.BlobHashes != nil { + for i, h := range hashes { + if h != args.BlobHashes[i] { + return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h) + } + } + } else { + args.BlobHashes = hashes + } + return nil +} + +func (args *BlobTransactionArgs) toTransaction() *types.Transaction { + tx := args.TransactionArgs.toTransaction() + if args.Blobs != nil && tx.Type() == types.BlobTxType { + return tx.WithBlobTxSidecar(&types.BlobTxSidecar{ + Blobs: args.Blobs, + Commitments: args.Commitments, + Proofs: args.Proofs, + }) + } + return tx +} + // TransactionArgs represents the arguments to construct a new transaction // or a message call. type TransactionArgs struct { @@ -104,6 +200,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.BlobHashes != nil && len(args.BlobHashes) == 0 { return errors.New(`need at least 1 blob for a blob transaction`) } + if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction { + return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction) + } if args.To == nil && len(args.data()) == 0 { return errors.New(`contract creation without any data provided`) } From 26776a933685253176992396b80b4ef63fc2b389 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 19 Jan 2024 13:51:12 +0330 Subject: [PATCH 03/13] fix existing tests --- internal/ethapi/api_test.go | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index fd6865019365..6fbf8dd0225d 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -989,10 +989,12 @@ func TestSignTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), + res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1027,11 +1029,13 @@ func TestSignBlobTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{{0x01, 0x22}}, + res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, + }, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1061,11 +1065,13 @@ func TestSendBlobTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + }, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) From 272dc9d6aa73057067abf028fac151698a24891d Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 19 Jan 2024 15:40:02 +0330 Subject: [PATCH 04/13] fix withBlobSidecar --- core/types/tx_blob.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index c6c31d3488e2..b40bef8b40aa 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -192,7 +192,7 @@ func (tx *BlobTx) withoutSidecar() *BlobTx { func (tx *BlobTx) withSidecar(sidecar *BlobTxSidecar) *BlobTx { cpy := *tx - tx.Sidecar = sidecar + cpy.Sidecar = sidecar return &cpy } From 49cde31078b99946310990df792b568251809eae Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 19 Jan 2024 15:40:35 +0330 Subject: [PATCH 05/13] add test cases --- crypto/kzg4844/kzg4844.go | 15 +++ internal/ethapi/api.go | 1 - internal/ethapi/api_test.go | 208 ++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 54d15ef8b04f..558f2b4de3e8 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -43,6 +43,11 @@ func (b *Blob) UnmarshalJSON(input []byte) error { return hexutil.UnmarshalFixedJSON(blobT, input, b[:]) } +// MarshalText returns the hex representation of b. +func (b Blob) MarshalText() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalText() +} + // Commitment is a serialized commitment to a polynomial. type Commitment [48]byte @@ -51,6 +56,11 @@ func (c *Commitment) UnmarshalJSON(input []byte) error { return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:]) } +// MarshalText returns the hex representation of c. +func (c Commitment) MarshalText() ([]byte, error) { + return hexutil.Bytes(c[:]).MarshalText() +} + // Proof is a serialized commitment to the quotient polynomial. type Proof [48]byte @@ -59,6 +69,11 @@ func (p *Proof) UnmarshalJSON(input []byte) error { return hexutil.UnmarshalFixedJSON(proofT, input, p[:]) } +// MarshalText returns the hex representation of p. +func (p Proof) MarshalText() ([]byte, error) { + return hexutil.Bytes(p[:]).MarshalText() +} + // Point is a BLS field element. type Point [32]byte diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 598d0406d786..b2b7e5c4b0c9 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1846,7 +1846,6 @@ func (s *TransactionAPI) FillTransaction(ctx context.Context, args BlobTransacti } // Assemble the transaction and obtain rlp tx := args.toTransaction() - // TODO(s1na): fill in blob proofs, commitments data, err := tx.MarshalBinary() if err != nil { return nil, err diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 6fbf8dd0225d..6e3bbcac65e2 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -45,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/blocktest" @@ -1085,6 +1086,213 @@ func TestSendBlobTransaction(t *testing.T) { } } +func TestFillBlobTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + to = crypto.PubkeyToAddress(key.PublicKey) + genesis = &core.Genesis{ + Config: params.MergedTestChainConfig, + Alloc: core.GenesisAlloc{}, + } + emptyBlob = kzg4844.Blob{} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) + emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) + emptyBlobHash = types.BlobHash(&emptyBlobCommit) + ) + b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) { + b.SetPoS() + }) + api := NewTransactionAPI(b, nil) + type result struct { + Hashes []common.Hash + Sidecar *types.BlobTxSidecar + } + suite := []struct { + name string + args BlobTransactionArgs + err string + want *result + }{ + { + name: "TestInvalidParamsCombination1", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{{}}, + Proofs: []kzg4844.Proof{{}}, + }, + err: `blob proofs provided while commitments were not`, + }, + { + name: "TestInvalidParamsCombination2", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{{}}, + Commitments: []kzg4844.Commitment{{}}, + }, + err: `blob commitments provided while proofs were not`, + }, + { + name: "TestInvalidParamsCount1", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{{}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}, {}}, + }, + err: `number of blobs and commitments mismatch (have=2, want=1)`, + }, + { + name: "TestInvalidParamsCount2", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{{}, {}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}}, + }, + err: `number of blobs and proofs mismatch (have=1, want=2)`, + }, + { + name: "TestInvalidProofVerification", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{{}, {}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}, {}}, + }, + err: `failed to verify blob proof: short buffer`, + }, + { + name: "TestGenerateBlobHashes", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + { + name: "TestValidBlobHashes", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{emptyBlobHash}, + }, + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + { + name: "TestInvalidBlobHashes", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, + }, + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + err: fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", common.Hash{0x01, 0x22}, emptyBlobHash), + }, + { + name: "TestGenerateBlobProofs", + args: BlobTransactionArgs{ + TransactionArgs: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + Blobs: []kzg4844.Blob{emptyBlob}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + } + for _, tc := range suite { + t.Run(tc.name, func(t *testing.T) { + res, err := api.FillTransaction(context.Background(), tc.args) + if len(tc.err) > 0 { + if err == nil { + t.Fatalf("missing error. want: %s", tc.err) + } else if err != nil && err.Error() != tc.err { + t.Fatalf("error mismatch. want: %s, have: %s", tc.err, err.Error()) + } + return + } + if err != nil && len(tc.err) == 0 { + t.Fatalf("expected no error. have: %s", err) + } + if res == nil { + t.Fatal("result missing") + } + want, err := json.Marshal(tc.want) + if err != nil { + t.Fatalf("failed to encode expected: %v", err) + } + have, err := json.Marshal(result{Hashes: res.Tx.BlobHashes(), Sidecar: res.Tx.BlobTxSidecar()}) + if err != nil { + t.Fatalf("failed to encode computed sidecar: %v", err) + } + if bytes.Compare(have, want) != 0 { + t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want) + } + }) + } +} + func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs { var ( gas = tx.Gas() From 8cba8452762c81fc92e13d118327d3fd885384af Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 22 Jan 2024 16:10:13 +0330 Subject: [PATCH 06/13] fix lint issue --- internal/ethapi/api_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 8db3bd21013b..8f3b96856ab1 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -1287,7 +1287,7 @@ func TestFillBlobTransaction(t *testing.T) { if err != nil { t.Fatalf("failed to encode computed sidecar: %v", err) } - if bytes.Compare(have, want) != 0 { + if !bytes.Equal(have, want) { t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want) } }) From 51cdfd184eb9fc1428d36939ee7210fd78d677c1 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 22 Jan 2024 18:44:37 +0330 Subject: [PATCH 07/13] add blob fields to tx marshalling --- core/types/transaction_marshalling.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 08ce80b07c6a..782cd4180e15 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -49,6 +49,12 @@ type txJSON struct { // Only used for encoding: Hash common.Hash `json:"hash"` + + // Only used for eth_fillTransaction ing a + // blob transaction. + Blobs []hexutil.Bytes `json:"blobs,omitempty"` + Commitments []hexutil.Bytes `json:"commitments,omitempty"` + Proofs []hexutil.Bytes `json:"proofs,omitempty"` } // yParityValue returns the YParity value from JSON. For backwards-compatibility reasons, @@ -142,6 +148,19 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.S = (*hexutil.Big)(itx.S.ToBig()) yparity := itx.V.Uint64() enc.YParity = (*hexutil.Uint64)(&yparity) + if sidecar := itx.Sidecar; sidecar != nil { + if len(sidecar.Blobs) != len(sidecar.Commitments) && len(sidecar.Blobs) != len(sidecar.Proofs) { + return nil, errors.New("blob fields count mismatch") + } + enc.Blobs = make([]hexutil.Bytes, len(sidecar.Blobs)) + enc.Commitments = make([]hexutil.Bytes, len(sidecar.Commitments)) + enc.Proofs = make([]hexutil.Bytes, len(sidecar.Proofs)) + for i, b := range sidecar.Blobs { + enc.Blobs[i] = b[:] + enc.Commitments[i] = sidecar.Commitments[i][:] + enc.Proofs[i] = sidecar.Proofs[i][:] + } + } } return json.Marshal(&enc) } From 34afde7bf2bf2d630494be799c59a6c86c7565fb Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 12:17:46 +0100 Subject: [PATCH 08/13] core/types: simplify blob tx sidecar encoding --- core/types/transaction_marshalling.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 782cd4180e15..4d5b2bcdd4ce 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/holiman/uint256" ) @@ -47,14 +48,13 @@ type txJSON struct { S *hexutil.Big `json:"s"` YParity *hexutil.Uint64 `json:"yParity,omitempty"` + // Blob transaction sidecar encoding: + Blobs []kzg4844.Blob `json:"blobs,omitempty"` + Commitments []kzg4844.Commitment `json:"commitments,omitempty"` + Proofs []kzg4844.Proof `json:"proofs,omitempty"` + // Only used for encoding: Hash common.Hash `json:"hash"` - - // Only used for eth_fillTransaction ing a - // blob transaction. - Blobs []hexutil.Bytes `json:"blobs,omitempty"` - Commitments []hexutil.Bytes `json:"commitments,omitempty"` - Proofs []hexutil.Bytes `json:"proofs,omitempty"` } // yParityValue returns the YParity value from JSON. For backwards-compatibility reasons, @@ -149,17 +149,9 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { yparity := itx.V.Uint64() enc.YParity = (*hexutil.Uint64)(&yparity) if sidecar := itx.Sidecar; sidecar != nil { - if len(sidecar.Blobs) != len(sidecar.Commitments) && len(sidecar.Blobs) != len(sidecar.Proofs) { - return nil, errors.New("blob fields count mismatch") - } - enc.Blobs = make([]hexutil.Bytes, len(sidecar.Blobs)) - enc.Commitments = make([]hexutil.Bytes, len(sidecar.Commitments)) - enc.Proofs = make([]hexutil.Bytes, len(sidecar.Proofs)) - for i, b := range sidecar.Blobs { - enc.Blobs[i] = b[:] - enc.Commitments[i] = sidecar.Commitments[i][:] - enc.Proofs[i] = sidecar.Proofs[i][:] - } + enc.Blobs = itx.Sidecar.Blobs + enc.Commitments = itx.Sidecar.Commitments + enc.Proofs = itx.Sidecar.Proofs } } return json.Marshal(&enc) From d5da34b0c9d30be3bef02b8578744ad9fa45fa27 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 16:52:30 +0100 Subject: [PATCH 09/13] internal/ethapi: integrate blob fields into TransactionArgs --- internal/ethapi/api.go | 4 +- internal/ethapi/api_test.go | 130 ++++++++---------- internal/ethapi/transaction_args.go | 204 +++++++++++++++------------- 3 files changed, 162 insertions(+), 176 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b2b7e5c4b0c9..359f51acc2ef 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1839,7 +1839,9 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr // FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields) // on a given unsigned transaction, and returns it to the caller for further // processing (signing + broadcast). -func (s *TransactionAPI) FillTransaction(ctx context.Context, args BlobTransactionArgs) (*SignTransactionResult, error) { +func (s *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, s.b); err != nil { return nil, err diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 8f3b96856ab1..cdeeace5e068 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -991,12 +991,10 @@ func TestSignTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1031,13 +1029,11 @@ func TestSignBlobTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{{0x01, 0x22}}, - }, + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1067,13 +1063,11 @@ func TestSendBlobTransaction(t *testing.T) { b.SetPoS() }) api := NewTransactionAPI(b, nil) - res, err := api.FillTransaction(context.Background(), BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, - }, + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1112,18 +1106,16 @@ func TestFillBlobTransaction(t *testing.T) { } suite := []struct { name string - args BlobTransactionArgs + args TransactionArgs err string want *result }{ { name: "TestInvalidParamsCombination1", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{{}}, Proofs: []kzg4844.Proof{{}}, }, @@ -1131,12 +1123,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestInvalidParamsCombination2", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{{}}, Commitments: []kzg4844.Commitment{{}}, }, @@ -1144,12 +1134,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestInvalidParamsCount1", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{{}}, Commitments: []kzg4844.Commitment{{}, {}}, Proofs: []kzg4844.Proof{{}, {}}, @@ -1158,12 +1146,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestInvalidParamsCount2", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{{}, {}}, Commitments: []kzg4844.Commitment{{}, {}}, Proofs: []kzg4844.Proof{{}}, @@ -1172,12 +1158,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestInvalidProofVerification", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{{}, {}}, Commitments: []kzg4844.Commitment{{}, {}}, Proofs: []kzg4844.Proof{{}, {}}, @@ -1186,12 +1170,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestGenerateBlobHashes", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{emptyBlob}, Commitments: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}, @@ -1207,13 +1189,11 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestValidBlobHashes", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{emptyBlobHash}, - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{emptyBlobHash}, Blobs: []kzg4844.Blob{emptyBlob}, Commitments: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}, @@ -1229,13 +1209,11 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestInvalidBlobHashes", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{{0x01, 0x22}}, - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, Blobs: []kzg4844.Blob{emptyBlob}, Commitments: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}, @@ -1244,12 +1222,10 @@ func TestFillBlobTransaction(t *testing.T) { }, { name: "TestGenerateBlobProofs", - args: BlobTransactionArgs{ - TransactionArgs: TransactionArgs{ - From: &b.acc.Address, - To: &to, - Value: (*hexutil.Big)(big.NewInt(1)), - }, + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), Blobs: []kzg4844.Blob{emptyBlob}, }, want: &result{ diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 6b41e0a1c2f9..6a9f4d6c16e2 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -41,97 +41,6 @@ var ( maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob ) -// BlobTransactionArgs represents the arguments to construct a new -// blob transaction. It includes the blob side-car. -type BlobTransactionArgs struct { - TransactionArgs - Blobs []kzg4844.Blob `json:"blobs"` - Commitments []kzg4844.Commitment `json:"commitments"` - Proofs []kzg4844.Proof `json:"proofs"` -} - -func (args *BlobTransactionArgs) setDefaults(ctx context.Context, b Backend) error { - // First validate and fill in other fields to avoid - // the expensive blob proof computation for an invalid transaction. - if err := args.TransactionArgs.setDefaults(ctx, b); err != nil { - return err - } - // No blobs, we're done. - if args.Blobs == nil { - return nil - } - 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 { - return errors.New(`blob proofs provided while commitments were not`) - } else if args.Commitments != nil && args.Proofs == nil { - 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) - } - 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 { - // Generate commitment and proof. - commitments := make([]kzg4844.Commitment, n) - proofs := make([]kzg4844.Proof, n) - for i, b := range args.Blobs { - c, err := kzg4844.BlobToCommitment(b) - if err != nil { - return err - } - commitments[i] = c - p, err := kzg4844.ComputeBlobProof(b, c) - if err != nil { - return err - } - 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 { - return fmt.Errorf("failed to verify blob proof: %v", err) - } - } - } - hashes := make([]common.Hash, n) - hasher := sha256.New() - for i, c := range args.Commitments { - hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c) - } - if args.BlobHashes != nil { - for i, h := range hashes { - if h != args.BlobHashes[i] { - return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h) - } - } - } else { - args.BlobHashes = hashes - } - return nil -} - -func (args *BlobTransactionArgs) toTransaction() *types.Transaction { - tx := args.TransactionArgs.toTransaction() - if args.Blobs != nil && tx.Type() == types.BlobTxType { - return tx.WithBlobTxSidecar(&types.BlobTxSidecar{ - Blobs: args.Blobs, - Commitments: args.Commitments, - Proofs: args.Proofs, - }) - } - return tx -} - // TransactionArgs represents the arguments to construct a new transaction // or a message call. type TransactionArgs struct { @@ -154,9 +63,17 @@ type TransactionArgs struct { AccessList *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` - // Introduced by EIP-4844. + // For BlobTxType BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"` BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + + // For BlobTxType transactions with blob sidecar + Blobs []kzg4844.Blob `json:"blobs"` + Commitments []kzg4844.Commitment `json:"commitments"` + Proofs []kzg4844.Proof `json:"proofs"` + + // This configures whether blobs are allowed to be passed. + blobSidecarAllowed bool } // from retrieves the transaction sender address. @@ -180,9 +97,13 @@ func (args *TransactionArgs) data() []byte { // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { + if err := args.setBlobTxSidecar(ctx, b); err != nil { + return err + } if err := args.setFeeDefaults(ctx, b); err != nil { return err } + if args.Value == nil { args.Value = new(hexutil.Big) } @@ -196,6 +117,8 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) } + + // BlobTx fields if args.BlobHashes != nil && args.To == nil { return errors.New(`blob transactions cannot have the form of a create transaction`) } @@ -205,9 +128,12 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction { return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction) } + + // create check if args.To == nil && len(args.data()) == 0 { return errors.New(`contract creation without any data provided`) } + // Estimate the gas usage if necessary. if args.Gas == nil { // These fields are immutable during the estimation, safe to @@ -231,6 +157,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { args.Gas = &estimated log.Trace("Estimate gas usage automatically", "gas", args.Gas) } + // If chain id is provided, ensure it matches the local chain id. Otherwise, set the local // chain id as the default. want := b.ChainConfig().ChainID @@ -266,10 +193,12 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) erro } return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas } + // Sanity check the EIP-4844 fee parameters. if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 { return errors.New("maxFeePerBlobGas must be non-zero") } + // Sanity check the non-EIP-1559 fee parameters. head := b.CurrentHeader() isLondon := b.ChainConfig().IsLondon(head.Number) @@ -351,6 +280,81 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ return nil } +// setBlobTxSidecar adds the blob tx +func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) 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 { + 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 { + return errors.New(`blob proofs provided while commitments were not`) + } else if args.Commitments != nil && args.Proofs == nil { + 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) + } + 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 { + // Generate commitment and proof. + commitments := make([]kzg4844.Commitment, n) + proofs := make([]kzg4844.Proof, n) + for i, b := range args.Blobs { + c, err := kzg4844.BlobToCommitment(b) + if err != nil { + return err + } + commitments[i] = c + p, err := kzg4844.ComputeBlobProof(b, c) + if err != nil { + return err + } + 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 { + return fmt.Errorf("failed to verify blob proof: %v", err) + } + } + } + + hashes := make([]common.Hash, n) + hasher := sha256.New() + for i, c := range args.Commitments { + hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c) + } + if args.BlobHashes != nil { + for i, h := range hashes { + if h != args.BlobHashes[i] { + return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h) + } + } + } else { + args.BlobHashes = hashes + } + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. @@ -464,6 +468,14 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { BlobHashes: args.BlobHashes, BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)), } + if args.Blobs != nil { + data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{ + Blobs: args.Blobs, + Commitments: args.Commitments, + Proofs: args.Proofs, + } + } + case args.MaxFeePerGas != nil: al := types.AccessList{} if args.AccessList != nil { @@ -480,6 +492,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { Data: args.data(), AccessList: al, } + case args.AccessList != nil: data = &types.AccessListTx{ To: args.To, @@ -491,6 +504,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { Data: args.data(), AccessList: *args.AccessList, } + default: data = &types.LegacyTx{ To: args.To, @@ -504,12 +518,6 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { return types.NewTx(data) } -// ToTransaction converts the arguments to a transaction. -// This assumes that setDefaults has been called. -func (args *TransactionArgs) ToTransaction() *types.Transaction { - return args.toTransaction() -} - // IsEIP4844 returns an indicator if the args contains EIP4844 fields. func (args *TransactionArgs) IsEIP4844() bool { return args.BlobHashes != nil || args.BlobFeeCap != nil From 961ed08586d4253e674e43c2e58c680201227b89 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 17:05:08 +0100 Subject: [PATCH 10/13] internal/ethapi: improve error message for blobs --- internal/ethapi/transaction_args.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 6a9f4d6c16e2..1d4d51b5a6ba 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -319,12 +319,12 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) er for i, b := range args.Blobs { c, err := kzg4844.BlobToCommitment(b) if err != nil { - return err + return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err) } commitments[i] = c p, err := kzg4844.ComputeBlobProof(b, c) if err != nil { - return err + return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err) } proofs[i] = p } From 96b955de230ba969a72944cfdfe85b2d4d550648 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 17:29:29 +0100 Subject: [PATCH 11/13] internal/ethapi: improve error message for blobtx as create --- internal/ethapi/transaction_args.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 1d4d51b5a6ba..a2508c192c08 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -119,9 +119,6 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // BlobTx fields - if args.BlobHashes != nil && args.To == nil { - return errors.New(`blob transactions cannot have the form of a create transaction`) - } if args.BlobHashes != nil && len(args.BlobHashes) == 0 { return errors.New(`need at least 1 blob for a blob transaction`) } @@ -130,8 +127,13 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // create check - if args.To == nil && len(args.data()) == 0 { - return errors.New(`contract creation without any data provided`) + if args.To == nil { + if args.BlobHashes != nil { + return errors.New(`missing "to" in blob transaction`) + } + if len(args.data()) == 0 { + return errors.New(`contract creation without any data provided`) + } } // Estimate the gas usage if necessary. @@ -289,7 +291,7 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) er // Passing blobs is not allowed in all contexts, only in specific methods. if !args.blobSidecarAllowed { - return errors.New("'blobs' is not supported for this RPC method") + return errors.New(`"blobs" is not supported for this RPC method`) } n := len(args.Blobs) From 97a2919561c494969d03d200be69b394a0868b4a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 17:30:51 +0100 Subject: [PATCH 12/13] core/types: remove WithBlobTxSidecar --- core/types/transaction.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 4e361bc81f32..9ec0199a0383 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -424,26 +424,6 @@ func (tx *Transaction) BlobGasFeeCapIntCmp(other *big.Int) int { return tx.BlobGasFeeCap().Cmp(other) } -// WithBlobTxSidecar returns a copy of tx with the blob sidecar. -func (tx *Transaction) WithBlobTxSidecar(sidecar *BlobTxSidecar) *Transaction { - blobtx, ok := tx.inner.(*BlobTx) - if !ok { - return tx - } - cpy := &Transaction{ - inner: blobtx.withSidecar(sidecar), - time: tx.time, - } - // Note: tx.size cache not carried over because the sidecar was not included in size! - if h := tx.hash.Load(); h != nil { - cpy.hash.Store(h) - } - if f := tx.from.Load(); f != nil { - cpy.from.Store(f) - } - return cpy -} - // WithoutBlobTxSidecar returns a copy of tx with the blob sidecar removed. func (tx *Transaction) WithoutBlobTxSidecar() *Transaction { blobtx, ok := tx.inner.(*BlobTx) From ee3c1d6163bd701b2961c6f19c18024fc6426d18 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 18:32:14 +0100 Subject: [PATCH 13/13] core/types: remove withSidecar --- core/types/tx_blob.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 72d7cc38fa8f..caede7cc5334 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -191,12 +191,6 @@ func (tx *BlobTx) withoutSidecar() *BlobTx { return &cpy } -func (tx *BlobTx) withSidecar(sidecar *BlobTxSidecar) *BlobTx { - cpy := *tx - cpy.Sidecar = sidecar - return &cpy -} - func (tx *BlobTx) encode(b *bytes.Buffer) error { if tx.Sidecar == nil { return rlp.Encode(b, tx)