package validation import ( "fmt" "math/big" "regexp" "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) // InputValidator provides comprehensive validation for external data type InputValidator struct { // Regex patterns for validation addressPattern *regexp.Regexp txHashPattern *regexp.Regexp blockHashPattern *regexp.Regexp hexDataPattern *regexp.Regexp } // NewInputValidator creates a new input validator func NewInputValidator() *InputValidator { return &InputValidator{ addressPattern: regexp.MustCompile(`^0x[a-fA-F0-9]{40}$`), txHashPattern: regexp.MustCompile(`^0x[a-fA-F0-9]{64}$`), blockHashPattern: regexp.MustCompile(`^0x[a-fA-F0-9]{64}$`), hexDataPattern: regexp.MustCompile(`^0x[a-fA-F0-9]*$`), } } // ValidationError represents a validation error type ValidationError struct { Field string Value interface{} Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation error for field '%s': %s (value: %v)", e.Field, e.Message, e.Value) } // ValidateAddress validates an Ethereum address func (v *InputValidator) ValidateAddress(address string) error { if address == "" { return &ValidationError{"address", address, "address cannot be empty"} } if !v.addressPattern.MatchString(address) { return &ValidationError{"address", address, "invalid address format"} } // Additional validation: check for zero address if address == "0x0000000000000000000000000000000000000000" { return &ValidationError{"address", address, "zero address not allowed"} } return nil } // ValidateCommonAddress validates a common.Address func (v *InputValidator) ValidateCommonAddress(address common.Address) error { return v.ValidateAddress(address.Hex()) } // ValidateTransactionHash validates a transaction hash func (v *InputValidator) ValidateTransactionHash(hash string) error { if hash == "" { return &ValidationError{"txHash", hash, "transaction hash cannot be empty"} } if !v.txHashPattern.MatchString(hash) { return &ValidationError{"txHash", hash, "invalid transaction hash format"} } return nil } // ValidateBlockHash validates a block hash func (v *InputValidator) ValidateBlockHash(hash string) error { if hash == "" { return &ValidationError{"blockHash", hash, "block hash cannot be empty"} } if !v.blockHashPattern.MatchString(hash) { return &ValidationError{"blockHash", hash, "invalid block hash format"} } return nil } // ValidateHexData validates hex-encoded data func (v *InputValidator) ValidateHexData(data string) error { if data == "" { return nil // Empty data is valid } if !v.hexDataPattern.MatchString(data) { return &ValidationError{"hexData", data, "invalid hex data format"} } return nil } // ValidateBigInt validates a big integer func (v *InputValidator) ValidateBigInt(value *big.Int, fieldName string) error { if value == nil { return &ValidationError{fieldName, value, "value cannot be nil"} } // Check for reasonable bounds to prevent overflow attacks maxValue := new(big.Int).Exp(big.NewInt(10), big.NewInt(77), nil) // 10^77 if value.Cmp(maxValue) > 0 { return &ValidationError{fieldName, value, "value exceeds maximum allowed"} } if value.Sign() < 0 { return &ValidationError{fieldName, value, "negative values not allowed"} } return nil } // ValidateBlockNumber validates a block number func (v *InputValidator) ValidateBlockNumber(blockNumber uint64) error { // Check for reasonable block number bounds maxBlock := uint64(1000000000) // 1 billion - reasonable upper bound if blockNumber > maxBlock { return &ValidationError{"blockNumber", blockNumber, "block number exceeds reasonable bounds"} } return nil } // ValidateTransaction validates a transaction structure func (v *InputValidator) ValidateTransaction(tx *types.Transaction) error { if tx == nil { return &ValidationError{"transaction", tx, "transaction cannot be nil"} } // Validate transaction hash if err := v.ValidateTransactionHash(tx.Hash().Hex()); err != nil { return err } // Validate to address if present if tx.To() != nil { if err := v.ValidateCommonAddress(*tx.To()); err != nil { return err } } // Validate value if err := v.ValidateBigInt(tx.Value(), "value"); err != nil { return err } // Validate gas limit gasLimit := tx.Gas() if gasLimit > 50000000 { // 50M gas limit seems reasonable return &ValidationError{"gasLimit", gasLimit, "gas limit exceeds reasonable bounds"} } // Validate gas price if tx.GasPrice() != nil { if err := v.ValidateBigInt(tx.GasPrice(), "gasPrice"); err != nil { return err } // Check for reasonable gas price bounds (up to 1000 Gwei) maxGasPrice := big.NewInt(1000000000000) // 1000 Gwei in wei if tx.GasPrice().Cmp(maxGasPrice) > 0 { return &ValidationError{"gasPrice", tx.GasPrice(), "gas price exceeds reasonable bounds"} } } // Validate transaction data if err := v.ValidateHexData(common.Bytes2Hex(tx.Data())); err != nil { return err } // Check data size limits if len(tx.Data()) > 1024*1024 { // 1MB limit return &ValidationError{"data", len(tx.Data()), "transaction data exceeds size limit"} } return nil } // ValidateBlock validates a block structure func (v *InputValidator) ValidateBlock(block *types.Block) error { if block == nil { return &ValidationError{"block", block, "block cannot be nil"} } // Validate block number if err := v.ValidateBlockNumber(block.Number().Uint64()); err != nil { return err } // Validate block hash if err := v.ValidateBlockHash(block.Hash().Hex()); err != nil { return err } // Validate parent hash if err := v.ValidateBlockHash(block.ParentHash().Hex()); err != nil { return err } // Validate coinbase address if err := v.ValidateCommonAddress(block.Coinbase()); err != nil { return err } // Validate timestamp timestamp := block.Time() if timestamp == 0 { return &ValidationError{"timestamp", timestamp, "timestamp cannot be zero"} } // Check for future timestamps (with 5 minute tolerance) maxTimestamp := uint64(1<<63 - 1) // Max int64 if timestamp > maxTimestamp { return &ValidationError{"timestamp", timestamp, "timestamp exceeds maximum value"} } // Validate transaction count txCount := len(block.Transactions()) if txCount > 10000 { // Reasonable transaction count limit return &ValidationError{"txCount", txCount, "transaction count exceeds reasonable limit"} } return nil } // ValidateAmount validates trading amounts for reasonable bounds func (v *InputValidator) ValidateAmount(amount *big.Int, tokenDecimals uint8, fieldName string) error { if err := v.ValidateBigInt(amount, fieldName); err != nil { return err } // Check for dust amounts (too small to be meaningful) minAmount := big.NewInt(1) if amount.Cmp(minAmount) < 0 { return &ValidationError{fieldName, amount, "amount too small (dust)"} } // Check for unreasonably large amounts based on token decimals maxTokenAmount := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenDecimals+9)), nil) // 1B tokens if amount.Cmp(maxTokenAmount) > 0 { return &ValidationError{fieldName, amount, "amount exceeds reasonable token bounds"} } return nil } // ValidateSlippageTolerance validates slippage tolerance parameters func (v *InputValidator) ValidateSlippageTolerance(slippage float64) error { if slippage < 0 { return &ValidationError{"slippage", slippage, "slippage cannot be negative"} } if slippage > 50.0 { // 50% max slippage return &ValidationError{"slippage", slippage, "slippage exceeds maximum allowed (50%)"} } return nil } // ValidateDeadline validates transaction deadline func (v *InputValidator) ValidateDeadline(deadline uint64) error { if deadline == 0 { return &ValidationError{"deadline", deadline, "deadline cannot be zero"} } // Check if deadline is in the past (with 1 minute tolerance) // Note: This would need actual timestamp comparison in real implementation maxDeadline := uint64(1<<32 - 1) // Reasonable unix timestamp bound if deadline > maxDeadline { return &ValidationError{"deadline", deadline, "deadline exceeds reasonable bounds"} } return nil } // SanitizeString removes potentially dangerous characters from strings func (v *InputValidator) SanitizeString(input string) string { // Remove null bytes and control characters cleaned := strings.ReplaceAll(input, "\x00", "") cleaned = regexp.MustCompile(`[\x00-\x1F\x7F]`).ReplaceAllString(cleaned, "") // Trim whitespace cleaned = strings.TrimSpace(cleaned) // Limit length if len(cleaned) > 1000 { cleaned = cleaned[:1000] } return cleaned } // ValidateEvent validates a DEX event structure func (v *InputValidator) ValidateEvent(event interface{}) error { if event == nil { return &ValidationError{"event", event, "event cannot be nil"} } // Use reflection or type assertion to validate event fields // For now, just validate that it's not nil // In a real implementation, you'd validate specific event fields return nil } // ValidateMultiple validates multiple fields and returns all errors func (v *InputValidator) ValidateMultiple(validators ...func() error) []error { var errors []error for _, validator := range validators { if err := validator(); err != nil { errors = append(errors, err) } } return errors }