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 }