internal/ethapi: add support for blobs in eth_fillTransaction (#28839)
This change adds support for blob-transaction in certain API-endpoints, e.g. eth_fillTransaction. A follow-up PR will add support for signing such transactions.
This commit is contained in:
parent
2732fb10d2
commit
ac5aa672d3
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +48,11 @@ type txJSON struct {
|
||||||
S *hexutil.Big `json:"s"`
|
S *hexutil.Big `json:"s"`
|
||||||
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
|
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:
|
// Only used for encoding:
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
|
@ -142,6 +148,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
enc.S = (*hexutil.Big)(itx.S.ToBig())
|
enc.S = (*hexutil.Big)(itx.S.ToBig())
|
||||||
yparity := itx.V.Uint64()
|
yparity := itx.V.Uint64()
|
||||||
enc.YParity = (*hexutil.Uint64)(&yparity)
|
enc.YParity = (*hexutil.Uint64)(&yparity)
|
||||||
|
if sidecar := itx.Sidecar; sidecar != nil {
|
||||||
|
enc.Blobs = itx.Sidecar.Blobs
|
||||||
|
enc.Commitments = itx.Sidecar.Commitments
|
||||||
|
enc.Proofs = itx.Sidecar.Proofs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,60 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"hash"
|
"hash"
|
||||||
|
"reflect"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed trusted_setup.json
|
//go:embed trusted_setup.json
|
||||||
var content embed.FS
|
var content embed.FS
|
||||||
|
|
||||||
|
var (
|
||||||
|
blobT = reflect.TypeOf(Blob{})
|
||||||
|
commitmentT = reflect.TypeOf(Commitment{})
|
||||||
|
proofT = reflect.TypeOf(Proof{})
|
||||||
|
)
|
||||||
|
|
||||||
// Blob represents a 4844 data blob.
|
// Blob represents a 4844 data blob.
|
||||||
type Blob [131072]byte
|
type Blob [131072]byte
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a blob in hex syntax.
|
||||||
|
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.
|
// Commitment is a serialized commitment to a polynomial.
|
||||||
type Commitment [48]byte
|
type Commitment [48]byte
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a commitment in hex syntax.
|
||||||
|
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.
|
// Proof is a serialized commitment to the quotient polynomial.
|
||||||
type Proof [48]byte
|
type Proof [48]byte
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a proof in hex syntax.
|
||||||
|
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.
|
// Point is a BLS field element.
|
||||||
type Point [32]byte
|
type Point [32]byte
|
||||||
|
|
||||||
|
|
|
@ -1812,13 +1812,14 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
|
||||||
// on a given unsigned transaction, and returns it to the caller for further
|
// on a given unsigned transaction, and returns it to the caller for further
|
||||||
// processing (signing + broadcast).
|
// processing (signing + broadcast).
|
||||||
func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
||||||
|
args.blobSidecarAllowed = true
|
||||||
|
|
||||||
// Set some sanity defaults and terminate on failure
|
// Set some sanity defaults and terminate on failure
|
||||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and obtain rlp
|
// Assemble the transaction and obtain rlp
|
||||||
tx := args.toTransaction()
|
tx := args.toTransaction()
|
||||||
// TODO(s1na): fill in blob proofs, commitments
|
|
||||||
data, err := tx.MarshalBinary()
|
data, err := tx.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -45,6 +46,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/internal/blocktest"
|
"github.com/ethereum/go-ethereum/internal/blocktest"
|
||||||
|
@ -1079,6 +1081,195 @@ 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 common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &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 TransactionArgs
|
||||||
|
err string
|
||||||
|
want *result
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TestInvalidParamsCombination1",
|
||||||
|
args: 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: 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: 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: 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: 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: 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: 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: 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: 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.Equal(have, want) {
|
||||||
|
t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs {
|
func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs {
|
||||||
var (
|
var (
|
||||||
gas = tx.Gas()
|
gas = tx.Gas()
|
||||||
|
|
|
@ -19,6 +19,7 @@ package ethapi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -29,11 +30,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
|
||||||
|
)
|
||||||
|
|
||||||
// TransactionArgs represents the arguments to construct a new transaction
|
// TransactionArgs represents the arguments to construct a new transaction
|
||||||
// or a message call.
|
// or a message call.
|
||||||
type TransactionArgs struct {
|
type TransactionArgs struct {
|
||||||
|
@ -56,9 +63,17 @@ type TransactionArgs struct {
|
||||||
AccessList *types.AccessList `json:"accessList,omitempty"`
|
AccessList *types.AccessList `json:"accessList,omitempty"`
|
||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
|
|
||||||
// Introduced by EIP-4844.
|
// For BlobTxType
|
||||||
BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"`
|
BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"`
|
||||||
BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
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.
|
// from retrieves the transaction sender address.
|
||||||
|
@ -82,9 +97,13 @@ func (args *TransactionArgs) data() []byte {
|
||||||
|
|
||||||
// setDefaults fills in default values for unspecified tx fields.
|
// setDefaults fills in default values for unspecified tx fields.
|
||||||
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
|
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 {
|
if err := args.setFeeDefaults(ctx, b); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Value == nil {
|
if args.Value == nil {
|
||||||
args.Value = new(hexutil.Big)
|
args.Value = new(hexutil.Big)
|
||||||
}
|
}
|
||||||
|
@ -98,15 +117,25 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
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`)
|
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
|
||||||
}
|
}
|
||||||
if args.BlobHashes != nil && args.To == nil {
|
|
||||||
return errors.New(`blob transactions cannot have the form of a create transaction`)
|
// BlobTx fields
|
||||||
}
|
|
||||||
if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
|
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`)
|
||||||
}
|
}
|
||||||
if args.To == nil && len(args.data()) == 0 {
|
if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction {
|
||||||
return errors.New(`contract creation without any data provided`)
|
return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create check
|
||||||
|
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.
|
// Estimate the gas usage if necessary.
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
// These fields are immutable during the estimation, safe to
|
// These fields are immutable during the estimation, safe to
|
||||||
|
@ -130,6 +159,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||||
args.Gas = &estimated
|
args.Gas = &estimated
|
||||||
log.Trace("Estimate gas usage automatically", "gas", args.Gas)
|
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
|
// If chain id is provided, ensure it matches the local chain id. Otherwise, set the local
|
||||||
// chain id as the default.
|
// chain id as the default.
|
||||||
want := b.ChainConfig().ChainID
|
want := b.ChainConfig().ChainID
|
||||||
|
@ -165,10 +195,12 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) erro
|
||||||
}
|
}
|
||||||
return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
|
return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check the EIP-4844 fee parameters.
|
// Sanity check the EIP-4844 fee parameters.
|
||||||
if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 {
|
if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 {
|
||||||
return errors.New("maxFeePerBlobGas must be non-zero")
|
return errors.New("maxFeePerBlobGas must be non-zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check the non-EIP-1559 fee parameters.
|
// Sanity check the non-EIP-1559 fee parameters.
|
||||||
head := b.CurrentHeader()
|
head := b.CurrentHeader()
|
||||||
isLondon := b.ChainConfig().IsLondon(head.Number)
|
isLondon := b.ChainConfig().IsLondon(head.Number)
|
||||||
|
@ -250,6 +282,81 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ
|
||||||
return nil
|
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 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)
|
||||||
|
}
|
||||||
|
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
|
// 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
|
// core evm. This method is used in calls and traces that do not require a real
|
||||||
// live transaction.
|
// live transaction.
|
||||||
|
@ -363,6 +470,14 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||||
BlobHashes: args.BlobHashes,
|
BlobHashes: args.BlobHashes,
|
||||||
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
|
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:
|
case args.MaxFeePerGas != nil:
|
||||||
al := types.AccessList{}
|
al := types.AccessList{}
|
||||||
if args.AccessList != nil {
|
if args.AccessList != nil {
|
||||||
|
@ -379,6 +494,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||||
Data: args.data(),
|
Data: args.data(),
|
||||||
AccessList: al,
|
AccessList: al,
|
||||||
}
|
}
|
||||||
|
|
||||||
case args.AccessList != nil:
|
case args.AccessList != nil:
|
||||||
data = &types.AccessListTx{
|
data = &types.AccessListTx{
|
||||||
To: args.To,
|
To: args.To,
|
||||||
|
@ -390,6 +506,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||||
Data: args.data(),
|
Data: args.data(),
|
||||||
AccessList: *args.AccessList,
|
AccessList: *args.AccessList,
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
data = &types.LegacyTx{
|
data = &types.LegacyTx{
|
||||||
To: args.To,
|
To: args.To,
|
||||||
|
@ -403,12 +520,6 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||||
return types.NewTx(data)
|
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.
|
// IsEIP4844 returns an indicator if the args contains EIP4844 fields.
|
||||||
func (args *TransactionArgs) IsEIP4844() bool {
|
func (args *TransactionArgs) IsEIP4844() bool {
|
||||||
return args.BlobHashes != nil || args.BlobFeeCap != nil
|
return args.BlobHashes != nil || args.BlobFeeCap != nil
|
||||||
|
|
Loading…
Reference in New Issue