github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/access/validator.go (about) 1 package access 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/onflow/cadence/runtime/parser" 8 "github.com/onflow/crypto" 9 10 "github.com/onflow/flow-go/model/flow" 11 "github.com/onflow/flow-go/state" 12 "github.com/onflow/flow-go/state/protocol" 13 ) 14 15 type Blocks interface { 16 HeaderByID(id flow.Identifier) (*flow.Header, error) 17 FinalizedHeader() (*flow.Header, error) 18 } 19 20 type ProtocolStateBlocks struct { 21 state protocol.State 22 } 23 24 func NewProtocolStateBlocks(state protocol.State) *ProtocolStateBlocks { 25 return &ProtocolStateBlocks{state: state} 26 } 27 28 func (b *ProtocolStateBlocks) HeaderByID(id flow.Identifier) (*flow.Header, error) { 29 header, err := b.state.AtBlockID(id).Head() 30 if err != nil { 31 if errors.Is(err, state.ErrUnknownSnapshotReference) { 32 return nil, nil 33 } 34 35 return nil, err 36 } 37 38 return header, nil 39 } 40 41 func (b *ProtocolStateBlocks) FinalizedHeader() (*flow.Header, error) { 42 return b.state.Final().Head() 43 } 44 45 // RateLimiter is an interface for checking if an address is rate limited. 46 // By convention, the address used is the payer field of a transaction. 47 // This rate limiter is applied when a transaction is first received by a 48 // node, meaning that if a transaction is rate-limited it will be dropped. 49 type RateLimiter interface { 50 // IsRateLimited returns true if the address is rate limited 51 IsRateLimited(address flow.Address) bool 52 } 53 54 type NoopLimiter struct{} 55 56 func NewNoopLimiter() *NoopLimiter { 57 return &NoopLimiter{} 58 } 59 60 func (l *NoopLimiter) IsRateLimited(address flow.Address) bool { 61 return false 62 } 63 64 type TransactionValidationOptions struct { 65 Expiry uint 66 ExpiryBuffer uint 67 AllowEmptyReferenceBlockID bool 68 AllowUnknownReferenceBlockID bool 69 MaxGasLimit uint64 70 CheckScriptsParse bool 71 MaxTransactionByteSize uint64 72 MaxCollectionByteSize uint64 73 } 74 75 type TransactionValidator struct { 76 blocks Blocks // for looking up blocks to check transaction expiry 77 chain flow.Chain // for checking validity of addresses 78 options TransactionValidationOptions 79 serviceAccountAddress flow.Address 80 limiter RateLimiter 81 } 82 83 func NewTransactionValidator( 84 blocks Blocks, 85 chain flow.Chain, 86 options TransactionValidationOptions, 87 ) *TransactionValidator { 88 return &TransactionValidator{ 89 blocks: blocks, 90 chain: chain, 91 options: options, 92 serviceAccountAddress: chain.ServiceAddress(), 93 limiter: NewNoopLimiter(), 94 } 95 } 96 97 func NewTransactionValidatorWithLimiter( 98 blocks Blocks, 99 chain flow.Chain, 100 options TransactionValidationOptions, 101 rateLimiter RateLimiter, 102 ) *TransactionValidator { 103 return &TransactionValidator{ 104 blocks: blocks, 105 chain: chain, 106 options: options, 107 serviceAccountAddress: chain.ServiceAddress(), 108 limiter: rateLimiter, 109 } 110 } 111 112 func (v *TransactionValidator) Validate(tx *flow.TransactionBody) (err error) { 113 // rate limit transactions for specific payers. 114 // a short term solution to prevent attacks that send too many failed transactions 115 // if a transaction is from a payer that should be rate limited, all the following 116 // checks will be skipped 117 err = v.checkRateLimitPayer(tx) 118 if err != nil { 119 return err 120 } 121 122 err = v.checkTxSizeLimit(tx) 123 if err != nil { 124 return err 125 } 126 127 err = v.checkMissingFields(tx) 128 if err != nil { 129 return err 130 } 131 132 err = v.checkGasLimit(tx) 133 if err != nil { 134 return err 135 } 136 137 err = v.checkExpiry(tx) 138 if err != nil { 139 return err 140 } 141 142 err = v.checkCanBeParsed(tx) 143 if err != nil { 144 return err 145 } 146 147 err = v.checkAddresses(tx) 148 if err != nil { 149 return err 150 } 151 152 err = v.checkSignatureFormat(tx) 153 if err != nil { 154 return err 155 } 156 157 err = v.checkSignatureDuplications(tx) 158 if err != nil { 159 return err 160 } 161 162 // TODO replace checkSignatureFormat by verifying the account/payer signatures 163 164 return nil 165 } 166 167 func (v *TransactionValidator) checkRateLimitPayer(tx *flow.TransactionBody) error { 168 if v.limiter.IsRateLimited(tx.Payer) { 169 return InvalidTxRateLimitedError{ 170 Payer: tx.Payer, 171 } 172 } 173 return nil 174 } 175 176 func (v *TransactionValidator) checkTxSizeLimit(tx *flow.TransactionBody) error { 177 txSize := uint64(tx.ByteSize()) 178 // first check compatibility to collection byte size 179 // this guarantees liveness 180 if txSize >= v.options.MaxCollectionByteSize { 181 return InvalidTxByteSizeError{ 182 Actual: txSize, 183 Maximum: v.options.MaxCollectionByteSize, 184 } 185 } 186 // this logic need the reason we don't greenlist the service account against the collection size 187 // limits is we can't verify the signature here yet. 188 if tx.Payer == v.serviceAccountAddress { 189 return nil 190 } 191 if txSize > v.options.MaxTransactionByteSize { 192 return InvalidTxByteSizeError{ 193 Actual: txSize, 194 Maximum: v.options.MaxTransactionByteSize, 195 } 196 } 197 return nil 198 } 199 200 func (v *TransactionValidator) checkMissingFields(tx *flow.TransactionBody) error { 201 missingFields := tx.MissingFields() 202 203 if v.options.AllowEmptyReferenceBlockID { 204 missingFields = remove(missingFields, flow.TransactionFieldRefBlockID.String()) 205 } 206 207 if len(missingFields) > 0 { 208 return IncompleteTransactionError{MissingFields: missingFields} 209 } 210 211 return nil 212 } 213 214 func (v *TransactionValidator) checkGasLimit(tx *flow.TransactionBody) error { 215 // if service account is the payer of the transaction accepts any gas limit 216 // note that even though we don't enforce any limit here, exec node later 217 // enforce a max value for any transaction 218 if tx.Payer == v.serviceAccountAddress { 219 return nil 220 } 221 if tx.GasLimit > v.options.MaxGasLimit || tx.GasLimit == 0 { 222 return InvalidGasLimitError{ 223 Actual: tx.GasLimit, 224 Maximum: v.options.MaxGasLimit, 225 } 226 } 227 228 return nil 229 } 230 231 // checkExpiry checks whether a transaction's reference block ID is 232 // valid. Returns nil if the reference is valid, returns an error if the 233 // reference is invalid or we failed to check it. 234 func (v *TransactionValidator) checkExpiry(tx *flow.TransactionBody) error { 235 if tx.ReferenceBlockID == flow.ZeroID && v.options.AllowEmptyReferenceBlockID { 236 return nil 237 } 238 239 // look up the reference block 240 ref, err := v.blocks.HeaderByID(tx.ReferenceBlockID) 241 if err != nil { 242 return fmt.Errorf("could not get reference block: %w", err) 243 } 244 245 if ref == nil { 246 // the transaction references an unknown block - at this point we decide 247 // whether to consider it expired based on configuration 248 if v.options.AllowUnknownReferenceBlockID { 249 return nil 250 } 251 252 return ErrUnknownReferenceBlock 253 } 254 255 // get the latest finalized block we know about 256 final, err := v.blocks.FinalizedHeader() 257 if err != nil { 258 return fmt.Errorf("could not get finalized header: %w", err) 259 } 260 261 diff := final.Height - ref.Height 262 // check for overflow 263 if ref.Height > final.Height { 264 diff = 0 265 } 266 267 // discard transactions that are expired, or that will expire sooner than 268 // our configured buffer allows 269 if uint(diff) > v.options.Expiry-v.options.ExpiryBuffer { 270 return ExpiredTransactionError{ 271 RefHeight: ref.Height, 272 FinalHeight: final.Height, 273 } 274 } 275 276 return nil 277 } 278 279 func (v *TransactionValidator) checkCanBeParsed(tx *flow.TransactionBody) error { 280 if v.options.CheckScriptsParse { 281 _, err := parser.ParseProgram(nil, tx.Script, parser.Config{}) 282 if err != nil { 283 return InvalidScriptError{ParserErr: err} 284 } 285 } 286 287 return nil 288 } 289 290 func (v *TransactionValidator) checkAddresses(tx *flow.TransactionBody) error { 291 292 for _, address := range append(tx.Authorizers, tx.Payer) { 293 // we check whether this is a valid output of the address generator 294 if !v.chain.IsValid(address) { 295 return InvalidAddressError{Address: address} 296 } 297 } 298 299 return nil 300 } 301 302 // every key (account, key index combination) can only be used once for signing 303 func (v *TransactionValidator) checkSignatureDuplications(tx *flow.TransactionBody) error { 304 type uniqueKey struct { 305 address flow.Address 306 index uint64 307 } 308 observedSigs := make(map[uniqueKey]bool) 309 for _, sig := range append(tx.PayloadSignatures, tx.EnvelopeSignatures...) { 310 if observedSigs[uniqueKey{sig.Address, sig.KeyIndex}] { 311 return DuplicatedSignatureError{Address: sig.Address, KeyIndex: sig.KeyIndex} 312 } 313 observedSigs[uniqueKey{sig.Address, sig.KeyIndex}] = true 314 } 315 return nil 316 } 317 318 func (v *TransactionValidator) checkSignatureFormat(tx *flow.TransactionBody) error { 319 320 for _, signature := range append(tx.PayloadSignatures, tx.EnvelopeSignatures...) { 321 // check the format of the signature is valid. 322 // a valid signature is an ECDSA signature of either P-256 or secp256k1 curve. 323 ecdsaSignature := signature.Signature 324 325 // check if the signature could be a P-256 signature 326 valid, err := crypto.SignatureFormatCheck(crypto.ECDSAP256, ecdsaSignature) 327 if err != nil { 328 return fmt.Errorf("could not check the signature format (%s): %w", signature, err) 329 } 330 if valid { 331 continue 332 } 333 334 // check if the signature could be a secp256k1 signature 335 valid, err = crypto.SignatureFormatCheck(crypto.ECDSASecp256k1, ecdsaSignature) 336 if err != nil { 337 return fmt.Errorf("could not check the signature format (%s): %w", signature, err) 338 } 339 if valid { 340 continue 341 } 342 343 return InvalidSignatureError{Signature: signature} 344 } 345 346 return nil 347 } 348 349 func remove(s []string, r string) []string { 350 for i, v := range s { 351 if v == r { 352 return append(s[:i], s[i+1:]...) 353 } 354 } 355 return s 356 }