Sequencer is working (minimal parsing)
This commit is contained in:
329
pkg/validation/input_validator.go
Normal file
329
pkg/validation/input_validator.go
Normal file
@@ -0,0 +1,329 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user