github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/signer/fourbyte/validation.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package fourbyte 19 20 import ( 21 "bytes" 22 "errors" 23 "fmt" 24 "math/big" 25 26 "github.com/AigarNetwork/aigar/common" 27 "github.com/AigarNetwork/aigar/signer/core" 28 ) 29 30 // ValidateTransaction does a number of checks on the supplied transaction, and 31 // returns either a list of warnings, or an error (indicating that the transaction 32 // should be immediately rejected). 33 func (db *Database) ValidateTransaction(selector *string, tx *core.SendTxArgs) (*core.ValidationMessages, error) { 34 messages := new(core.ValidationMessages) 35 36 // Prevent accidental erroneous usage of both 'input' and 'data' (show stopper) 37 if tx.Data != nil && tx.Input != nil && !bytes.Equal(*tx.Data, *tx.Input) { 38 return nil, errors.New(`ambiguous request: both "data" and "input" are set and are not identical`) 39 } 40 // Place data on 'data', and nil 'input' 41 var data []byte 42 if tx.Input != nil { 43 tx.Data = tx.Input 44 tx.Input = nil 45 } 46 if tx.Data != nil { 47 data = *tx.Data 48 } 49 // Contract creation doesn't validate call data, handle first 50 if tx.To == nil { 51 // Contract creation should contain sufficient data to deploy a contract. A 52 // typical error is omitting sender due to some quirk in the javascript call 53 // e.g. https://github.com/AigarNetwork/aigar/issues/16106. 54 if len(data) == 0 { 55 // Prevent sending ether into black hole (show stopper) 56 if tx.Value.ToInt().Cmp(big.NewInt(0)) > 0 { 57 return nil, errors.New("transaction will create a contract with value but empty code") 58 } 59 // No value submitted at least, critically Warn, but don't blow up 60 messages.Crit("Transaction will create a contract with empty code") 61 } else if len(data) < 40 { // arbitrary heuristic limit 62 messages.Warn(fmt.Sprintf("Transaction will create a contract, but the payload is suspiciously small (%d bytes)", len(data))) 63 } 64 // Method selector should be nil for contract creation 65 if selector != nil { 66 messages.Warn("Transaction will create a contract, but method selector supplied, indicating an intent to call a method") 67 } 68 return messages, nil 69 } 70 // Not a contract creation, validate as a plain transaction 71 if !tx.To.ValidChecksum() { 72 messages.Warn("Invalid checksum on recipient address") 73 } 74 if bytes.Equal(tx.To.Address().Bytes(), common.Address{}.Bytes()) { 75 messages.Crit("Transaction recipient is the zero address") 76 } 77 // Semantic fields validated, try to make heads or tails of the call data 78 db.validateCallData(selector, data, messages) 79 return messages, nil 80 } 81 82 // validateCallData checks if the ABI call-data + method selector (if given) can 83 // be parsed and seems to match. 84 func (db *Database) validateCallData(selector *string, data []byte, messages *core.ValidationMessages) { 85 // If the data is empty, we have a plain value transfer, nothing more to do 86 if len(data) == 0 { 87 return 88 } 89 // Validate the call data that it has the 4byte prefix and the rest divisible by 32 bytes 90 if len(data) < 4 { 91 messages.Warn("Transaction data is not valid ABI (missing the 4 byte call prefix)") 92 return 93 } 94 if n := len(data) - 4; n%32 != 0 { 95 messages.Warn(fmt.Sprintf("Transaction data is not valid ABI (length should be a multiple of 32 (was %d))", n)) 96 } 97 // If a custom method selector was provided, validate with that 98 if selector != nil { 99 if info, err := verifySelector(*selector, data); err != nil { 100 messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be matched: %v", err)) 101 } else { 102 messages.Info(info.String()) 103 db.AddSelector(*selector, data[:4]) 104 } 105 return 106 } 107 // No method selector was provided, check the database for embedded ones 108 embedded, err := db.Selector(data[:4]) 109 if err != nil { 110 messages.Warn(fmt.Sprintf("Transaction contains data, but the ABI signature could not be found: %v", err)) 111 return 112 } 113 if info, err := verifySelector(embedded, data); err != nil { 114 messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be varified: %v", err)) 115 } else { 116 messages.Info(info.String()) 117 } 118 }