mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-07 23:48:36 +00:00
core, internal/ethapi: reject tx value and gas price above uint256 max
Raw RLP transactions can decode values larger than uint256, so reject them during txpool validation before they fall through to state-dependent checks. Also return ErrUint256Overflow from state transition and map it to an invalid params RPC error so raw transaction submission reports a consistent parameter validation failure. Add focused regression tests for txpool validation and eth_sendRawTransaction error handling.
This commit is contained in:
parent
b1baab4427
commit
bbfa8d9255
5 changed files with 103 additions and 2 deletions
|
|
@ -481,7 +481,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// Check clause 6
|
||||
value, overflow := uint256.FromBig(msg.Value)
|
||||
if overflow {
|
||||
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
|
||||
return nil, types.ErrUint256Overflow
|
||||
}
|
||||
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
|
||||
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
|
||||
|
|
|
|||
|
|
@ -97,9 +97,19 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
}
|
||||
// Transactions can't be negative. This may never happen using RLP decoded
|
||||
// transactions but may occur for transactions created using the RPC.
|
||||
if tx.Value().Sign() < 0 {
|
||||
val := tx.Value()
|
||||
if val.Sign() < 0 {
|
||||
return ErrNegativeValue
|
||||
}
|
||||
if val.BitLen() > 256 {
|
||||
return types.ErrUint256Overflow
|
||||
}
|
||||
// For legacy transactions, ensure gasPrice does not exceed 256 bits
|
||||
if tx.Type() == types.LegacyTxType {
|
||||
if tx.GasPrice().BitLen() > 256 {
|
||||
return types.ErrUint256Overflow
|
||||
}
|
||||
}
|
||||
// Ensure the transaction doesn't exceed the current block limit gas
|
||||
if head.GasLimit < tx.Gas() {
|
||||
return ErrGasLimit
|
||||
|
|
|
|||
|
|
@ -96,6 +96,44 @@ func TestValidateTransactionEIP2681(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateTransactionValueOverflow(t *testing.T) {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
head := &types.Header{
|
||||
Number: big.NewInt(1),
|
||||
GasLimit: 5000000,
|
||||
Time: 1,
|
||||
Difficulty: big.NewInt(1),
|
||||
}
|
||||
signer := types.LatestSigner(params.TestChainConfig)
|
||||
opts := &ValidationOptions{
|
||||
Config: params.TestChainConfig,
|
||||
Accept: 0xFF,
|
||||
MaxSize: 32 * 1024,
|
||||
MaxBlobCount: 6,
|
||||
MinTip: big.NewInt(0),
|
||||
}
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
overflowValue := new(big.Int).Lsh(big.NewInt(1), 256)
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &to,
|
||||
Value: overflowValue,
|
||||
Gas: 21000,
|
||||
GasPrice: big.NewInt(1),
|
||||
})
|
||||
tx, err = types.SignTx(tx, types.HomesteadSigner{}, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ValidateTransaction(tx, head, signer, opts)
|
||||
if !errors.Is(err, types.ErrUint256Overflow) {
|
||||
t.Fatalf("expected %v, got %v", types.ErrUint256Overflow, err)
|
||||
}
|
||||
}
|
||||
|
||||
// createTestTransaction creates a basic transaction for testing
|
||||
func createTestTransaction(key *ecdsa.PrivateKey, nonce uint64) *types.Transaction {
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
|
|
|
|||
|
|
@ -3968,6 +3968,11 @@ type configTimeBackend struct {
|
|||
time uint64
|
||||
}
|
||||
|
||||
type overflowRejectingBackend struct {
|
||||
*backendMock
|
||||
lastTx *types.Transaction
|
||||
}
|
||||
|
||||
func (b configTimeBackend) ChainConfig() *params.ChainConfig {
|
||||
return b.genesis.Config
|
||||
}
|
||||
|
|
@ -3998,6 +4003,14 @@ func (b *testBackend) RPCTxSyncMaxTimeout() time.Duration {
|
|||
func (b *backendMock) RPCTxSyncDefaultTimeout() time.Duration { return 2 * time.Second }
|
||||
func (b *backendMock) RPCTxSyncMaxTimeout() time.Duration { return 5 * time.Minute }
|
||||
|
||||
func (b *overflowRejectingBackend) SendTx(ctx context.Context, tx *types.Transaction) error {
|
||||
b.lastTx = tx
|
||||
if tx.Value().BitLen() > 256 {
|
||||
return types.ErrUint256Overflow
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeSignedRaw(t *testing.T, api *TransactionAPI, from, to common.Address, value *big.Int) (hexutil.Bytes, *types.Transaction) {
|
||||
t.Helper()
|
||||
|
||||
|
|
@ -4089,6 +4102,43 @@ func TestSendRawTransactionSync_Timeout(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSendRawTransactionRejectsValueOverflow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
require.NoError(t, err)
|
||||
|
||||
backend := &overflowRejectingBackend{backendMock: newBackendMock()}
|
||||
api := NewTransactionAPI(backend, nil)
|
||||
|
||||
to := common.Address{0x42}
|
||||
overflowValue := new(big.Int).Lsh(big.NewInt(1), 256)
|
||||
tx, err := types.SignNewTx(key, types.LatestSigner(backend.ChainConfig()), &types.LegacyTx{
|
||||
Nonce: 0,
|
||||
GasPrice: big.NewInt(2),
|
||||
Gas: 21000,
|
||||
To: &to,
|
||||
Value: overflowValue,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
raw, err := tx.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = api.SendRawTransaction(context.Background(), raw)
|
||||
require.ErrorIs(t, err, types.ErrUint256Overflow)
|
||||
require.NotNil(t, backend.lastTx)
|
||||
require.Equal(t, overflowValue, backend.lastTx.Value())
|
||||
}
|
||||
|
||||
func TestTxValidationErrorUint256Overflow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := txValidationError(types.ErrUint256Overflow)
|
||||
require.Equal(t, errCodeInvalidParams, err.ErrorCode())
|
||||
require.ErrorContains(t, err, types.ErrUint256Overflow.Error())
|
||||
}
|
||||
|
||||
func TestGetStorageValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
|
|
@ -131,6 +132,8 @@ func txValidationError(err error) *invalidTxError {
|
|||
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
|
||||
case errors.Is(err, core.ErrTipVeryHigh):
|
||||
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
|
||||
case errors.Is(err, types.ErrUint256Overflow):
|
||||
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
|
||||
case errors.Is(err, core.ErrTipAboveFeeCap):
|
||||
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
|
||||
case errors.Is(err, core.ErrFeeCapTooLow):
|
||||
|
|
|
|||
Loading…
Reference in a new issue