github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/signer/core/validation.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"github.com/insight-chain/inb-go/core/types"
    24  	"math/big"
    25  	"regexp"
    26  
    27  	"github.com/insight-chain/inb-go/common"
    28  )
    29  
    30  // The validation package contains validation checks for transactions
    31  // - ABI-data validation
    32  // - Transaction semantics validation
    33  // The package provides warnings for typical pitfalls
    34  
    35  type Validator struct {
    36  	db *AbiDb
    37  }
    38  
    39  func NewValidator(db *AbiDb) *Validator {
    40  	return &Validator{db}
    41  }
    42  func testSelector(selector string, data []byte) (*decodedCallData, error) {
    43  	if selector == "" {
    44  		return nil, fmt.Errorf("selector not found")
    45  	}
    46  	abiData, err := MethodSelectorToAbi(selector)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	info, err := parseCallData(data, string(abiData))
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return info, nil
    55  
    56  }
    57  
    58  // validateCallData checks if the ABI-data + methodselector (if given) can be parsed and seems to match
    59  func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, methodSelector *string) {
    60  	if len(data) == 0 {
    61  		return
    62  	}
    63  	if len(data) < 4 {
    64  		msgs.warn("Tx contains data which is not valid ABI")
    65  		return
    66  	}
    67  	if arglen := len(data) - 4; arglen%32 != 0 {
    68  		msgs.warn(fmt.Sprintf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", arglen))
    69  	}
    70  	var (
    71  		info *decodedCallData
    72  		err  error
    73  	)
    74  	// Check the provided one
    75  	if methodSelector != nil {
    76  		info, err = testSelector(*methodSelector, data)
    77  		if err != nil {
    78  			msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err))
    79  		} else {
    80  			msgs.info(info.String())
    81  			//Successfull match. add to db if not there already (ignore errors there)
    82  			v.db.AddSignature(*methodSelector, data[:4])
    83  		}
    84  		return
    85  	}
    86  	// Check the db
    87  	selector, err := v.db.LookupMethodSelector(data[:4])
    88  	if err != nil {
    89  		msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err))
    90  		return
    91  	}
    92  	info, err = testSelector(selector, data)
    93  	if err != nil {
    94  		msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err))
    95  	} else {
    96  		msgs.info(info.String())
    97  	}
    98  }
    99  
   100  // validateSemantics checks if the transactions 'makes sense', and generate warnings for a couple of typical scenarios
   101  func (v *Validator) validate(msgs *ValidationMessages, txargs *SendTxArgs, methodSelector *string) error {
   102  	// Prevent accidental erroneous usage of both 'input' and 'data'
   103  	if txargs.Data != nil && txargs.Input != nil && !bytes.Equal(*txargs.Data, *txargs.Input) {
   104  		// This is a showstopper
   105  		return errors.New(`Ambiguous request: both "data" and "input" are set and are not identical`)
   106  	}
   107  	var (
   108  		data []byte
   109  	)
   110  	// Place data on 'data', and nil 'input'
   111  	if txargs.Input != nil {
   112  		txargs.Data = txargs.Input
   113  		txargs.Input = nil
   114  	}
   115  	if txargs.Data != nil {
   116  		data = *txargs.Data
   117  	}
   118  
   119  	if txargs.To == nil && txargs.Types == types.Contract {
   120  		//Contract creation should contain sufficient data to deploy a contract
   121  		// A typical error is omitting sender due to some quirk in the javascript call
   122  		// e.g. https://github.com/ethereum/go-ethereum/issues/16106
   123  		if len(data) == 0 {
   124  			if txargs.Value.ToInt().Cmp(big.NewInt(0)) > 0 {
   125  				// Sending ether into black hole
   126  				return errors.New("Tx will create contract with value but empty code!")
   127  			}
   128  			// No value submitted at least
   129  			msgs.crit("Tx will create contract with empty code!")
   130  		} else if len(data) < 40 { //Arbitrary limit
   131  			msgs.warn(fmt.Sprintf("Tx will will create contract, but payload is suspiciously small (%d b)", len(data)))
   132  		}
   133  		// methodSelector should be nil for contract creation
   134  		if methodSelector != nil {
   135  			msgs.warn("Tx will create contract, but method selector supplied; indicating intent to call a method.")
   136  		}
   137  
   138  	} else {
   139  		if !txargs.To.ValidChecksum() {
   140  			msgs.warn("Invalid checksum on to-address")
   141  		}
   142  		// Normal transaction
   143  		if bytes.Equal(txargs.To.Address().Bytes(), common.Address{}.Bytes()) {
   144  			// Sending to 0
   145  			msgs.crit("Tx destination is the zero address!")
   146  		}
   147  		// Validate calldata
   148  		v.validateCallData(msgs, data, methodSelector)
   149  	}
   150  	return nil
   151  }
   152  
   153  // ValidateTransaction does a number of checks on the supplied transaction, and returns either a list of warnings,
   154  // or an error, indicating that the transaction should be immediately rejected
   155  func (v *Validator) ValidateTransaction(txArgs *SendTxArgs, methodSelector *string) (*ValidationMessages, error) {
   156  	msgs := &ValidationMessages{}
   157  	return msgs, v.validate(msgs, txArgs, methodSelector)
   158  }
   159  
   160  var Printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ]+$")
   161  
   162  // ValidatePasswordFormat returns an error if the password is too short, or consists of characters
   163  // outside the range of the printable 7bit ascii set
   164  func ValidatePasswordFormat(password string) error {
   165  	if len(password) < 10 {
   166  		return errors.New("password too short (<10 characters)")
   167  	}
   168  	if !Printable7BitAscii.MatchString(password) {
   169  		return errors.New("password contains invalid characters - only 7bit printable ascii allowed")
   170  	}
   171  	return nil
   172  }