github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/signer/core/validation.go (about)

     1  
     2  package core
     3  
     4  import (
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  
    10  	"github.com/quickchainproject/quickchain/common"
    11  )
    12  
    13  // The validation package contains validation checks for transactions
    14  // - ABI-data validation
    15  // - Transaction semantics validation
    16  // The package provides warnings for typical pitfalls
    17  
    18  func (vs *ValidationMessages) crit(msg string) {
    19  	vs.Messages = append(vs.Messages, ValidationInfo{"CRITICAL", msg})
    20  }
    21  func (vs *ValidationMessages) warn(msg string) {
    22  	vs.Messages = append(vs.Messages, ValidationInfo{"WARNING", msg})
    23  }
    24  func (vs *ValidationMessages) info(msg string) {
    25  	vs.Messages = append(vs.Messages, ValidationInfo{"Info", msg})
    26  }
    27  
    28  type Validator struct {
    29  	db *AbiDb
    30  }
    31  
    32  func NewValidator(db *AbiDb) *Validator {
    33  	return &Validator{db}
    34  }
    35  func testSelector(selector string, data []byte) (*decodedCallData, error) {
    36  	if selector == "" {
    37  		return nil, fmt.Errorf("selector not found")
    38  	}
    39  	abiData, err := MethodSelectorToAbi(selector)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	info, err := parseCallData(data, string(abiData))
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	return info, nil
    48  
    49  }
    50  
    51  // validateCallData checks if the ABI-data + methodselector (if given) can be parsed and seems to match
    52  func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, methodSelector *string) {
    53  	if len(data) == 0 {
    54  		return
    55  	}
    56  	if len(data) < 4 {
    57  		msgs.warn("Tx contains data which is not valid ABI")
    58  		return
    59  	}
    60  	var (
    61  		info *decodedCallData
    62  		err  error
    63  	)
    64  	// Check the provided one
    65  	if methodSelector != nil {
    66  		info, err = testSelector(*methodSelector, data)
    67  		if err != nil {
    68  			msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err))
    69  		} else {
    70  			msgs.info(info.String())
    71  			//Successfull match. add to db if not there already (ignore errors there)
    72  			v.db.AddSignature(*methodSelector, data[:4])
    73  		}
    74  		return
    75  	}
    76  	// Check the db
    77  	selector, err := v.db.LookupMethodSelector(data[:4])
    78  	if err != nil {
    79  		msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err))
    80  		return
    81  	}
    82  	info, err = testSelector(selector, data)
    83  	if err != nil {
    84  		msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err))
    85  	} else {
    86  		msgs.info(info.String())
    87  	}
    88  }
    89  
    90  // validateSemantics checks if the transactions 'makes sense', and generate warnings for a couple of typical scenarios
    91  func (v *Validator) validate(msgs *ValidationMessages, txargs *SendTxArgs, methodSelector *string) error {
    92  	// Prevent accidental erroneous usage of both 'input' and 'data'
    93  	if txargs.Data != nil && txargs.Input != nil && !bytes.Equal(*txargs.Data, *txargs.Input) {
    94  		// This is a showstopper
    95  		return errors.New(`Ambiguous request: both "data" and "input" are set and are not identical`)
    96  	}
    97  	var (
    98  		data []byte
    99  	)
   100  	// Place data on 'data', and nil 'input'
   101  	if txargs.Input != nil {
   102  		txargs.Data = txargs.Input
   103  		txargs.Input = nil
   104  	}
   105  	if txargs.Data != nil {
   106  		data = *txargs.Data
   107  	}
   108  
   109  	if txargs.To == nil {
   110  		//Contract creation should contain sufficient data to deploy a contract
   111  		// A typical error is omitting sender due to some quirk in the javascript call
   112  		// e.g. https://github.com/quickchainproject/quickchain/issues/16106
   113  		if len(data) == 0 {
   114  			if txargs.Value.ToInt().Cmp(big.NewInt(0)) > 0 {
   115  				// Sending ether into black hole
   116  				return errors.New(`Tx will create contract with value but empty code!`)
   117  			}
   118  			// No value submitted at least
   119  			msgs.crit("Tx will create contract with empty code!")
   120  		} else if len(data) < 40 { //Arbitrary limit
   121  			msgs.warn(fmt.Sprintf("Tx will will create contract, but payload is suspiciously small (%d b)", len(data)))
   122  		}
   123  		// methodSelector should be nil for contract creation
   124  		if methodSelector != nil {
   125  			msgs.warn("Tx will create contract, but method selector supplied; indicating intent to call a method.")
   126  		}
   127  
   128  	} else {
   129  		if !txargs.To.ValidChecksum() {
   130  			msgs.warn("Invalid checksum on to-address")
   131  		}
   132  		// Normal transaction
   133  		if bytes.Equal(txargs.To.Address().Bytes(), common.Address{}.Bytes()) {
   134  			// Sending to 0
   135  			msgs.crit("Tx destination is the zero address!")
   136  		}
   137  		// Validate calldata
   138  		v.validateCallData(msgs, data, methodSelector)
   139  	}
   140  	return nil
   141  }
   142  
   143  // ValidateTransaction does a number of checks on the supplied transaction, and returns either a list of warnings,
   144  // or an error, indicating that the transaction should be immediately rejected
   145  func (v *Validator) ValidateTransaction(txArgs *SendTxArgs, methodSelector *string) (*ValidationMessages, error) {
   146  	msgs := &ValidationMessages{}
   147  	return msgs, v.validate(msgs, txArgs, methodSelector)
   148  }