This commit is contained in:
Nikolas-Cao 2026-05-21 21:53:40 -07:00 committed by GitHub
commit 527b063b98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 113 additions and 0 deletions

View file

@ -42,6 +42,7 @@ import (
type TransactionArgs struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Type *hexutil.Uint64 `json:"type,omitempty"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
@ -101,6 +102,9 @@ type sidecarConfig struct {
// setDefaults fills in default values for unspecified tx fields.
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config sidecarConfig) error {
if err := args.validateTxTypeSupported(); err != nil {
return err
}
if err := args.setBlobTxSidecar(ctx, config); err != nil {
return err
}
@ -176,6 +180,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config
} else {
args.ChainID = (*hexutil.Big)(want)
}
if err := args.validateTxTypeMatch(types.LegacyTxType); err != nil {
return err
}
return nil
}
@ -390,6 +397,9 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
// CallDefaults sanitizes the transaction arguments, often filling in zero values,
// for the purpose of eth_call class of RPC methods.
func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, chainID *big.Int) error {
if err := args.validateTxTypeSupported(); err != nil {
return err
}
// Reject invalid combinations of pre- and post-1559 fee styles
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
@ -436,10 +446,40 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int,
if args.BlobFeeCap == nil && args.BlobHashes != nil {
args.BlobFeeCap = new(hexutil.Big)
}
if err := args.validateTxTypeMatch(types.LegacyTxType); err != nil {
return err
}
return nil
}
func (args *TransactionArgs) validateTxTypeSupported() error {
if args.Type == nil {
return nil
}
switch uint64(*args.Type) {
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType:
return nil
default:
return fmt.Errorf("unsupported transaction type: %d", uint64(*args.Type))
}
}
func (args *TransactionArgs) validateTxTypeMatch(defaultType int) error {
if args.Type == nil {
return nil
}
// Blob txs cannot be contract creations. ToTransaction assumes this invariant.
if args.BlobHashes != nil && args.To == nil {
return errors.New(`missing "to" in blob transaction`)
}
inferred := args.ToTransaction(defaultType).Type()
if uint64(*args.Type) != uint64(inferred) {
return fmt.Errorf("transaction type mismatch (requested=%d inferred=%d)", uint64(*args.Type), inferred)
}
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.

View file

@ -256,6 +256,79 @@ func TestSetFeeDefaults(t *testing.T) {
}
}
func TestTransactionArgsRejectUnsupportedTypeInCallDefaults(t *testing.T) {
t.Parallel()
badType := hexutil.Uint64(0x5)
args := &TransactionArgs{Type: &badType}
err := args.CallDefaults(0, big.NewInt(1), big.NewInt(1))
if err == nil {
t.Fatal("expected error for unsupported transaction type")
}
if err.Error() != "unsupported transaction type: 5" {
t.Fatalf("unexpected error: %v", err)
}
}
func TestTransactionArgsRejectUnsupportedTypeInSetDefaults(t *testing.T) {
t.Parallel()
badType := hexutil.Uint64(0x5)
gas := hexutil.Uint64(21000)
to := common.Address{0x1}
args := &TransactionArgs{
To: &to,
Gas: &gas,
Type: &badType,
}
err := args.setDefaults(context.Background(), newBackendMock(), sidecarConfig{})
if err == nil {
t.Fatal("expected error for unsupported transaction type")
}
if err.Error() != "unsupported transaction type: 5" {
t.Fatalf("unexpected error: %v", err)
}
}
func TestTransactionArgsRejectTypeMismatchInCallDefaults(t *testing.T) {
t.Parallel()
requestedLegacy := hexutil.Uint64(types.LegacyTxType)
args := &TransactionArgs{
Type: &requestedLegacy,
MaxFeePerGas: (*hexutil.Big)(big.NewInt(2)),
}
err := args.CallDefaults(0, big.NewInt(1), big.NewInt(1))
if err == nil {
t.Fatal("expected type mismatch error")
}
if err.Error() != "transaction type mismatch (requested=0 inferred=2)" {
t.Fatalf("unexpected error: %v", err)
}
}
func TestTransactionArgsRejectTypeMismatchInSetDefaults(t *testing.T) {
t.Parallel()
requestedDynamic := hexutil.Uint64(types.DynamicFeeTxType)
gas := hexutil.Uint64(21000)
gasPrice := (*hexutil.Big)(big.NewInt(1))
to := common.Address{0x1}
args := &TransactionArgs{
To: &to,
Gas: &gas,
GasPrice: gasPrice,
Type: &requestedDynamic,
}
err := args.setDefaults(context.Background(), newBackendMock(), sidecarConfig{})
if err == nil {
t.Fatal("expected type mismatch error")
}
if err.Error() != "transaction type mismatch (requested=2 inferred=0)" {
t.Fatalf("unexpected error: %v", err)
}
}
type backendMock struct {
current *types.Header
config *params.ChainConfig