github.com/koko1123/flow-go-1@v0.29.6/fvm/transactionVerifier.go (about) 1 package fvm 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 "go.opentelemetry.io/otel/attribute" 9 10 "github.com/koko1123/flow-go-1/fvm/crypto" 11 "github.com/koko1123/flow-go-1/fvm/environment" 12 "github.com/koko1123/flow-go-1/fvm/errors" 13 "github.com/koko1123/flow-go-1/fvm/state" 14 "github.com/koko1123/flow-go-1/fvm/tracing" 15 "github.com/koko1123/flow-go-1/model/flow" 16 "github.com/koko1123/flow-go-1/module/trace" 17 ) 18 19 type signatureType struct { 20 message []byte 21 22 errorBuilder func(flow.TransactionSignature, error) errors.CodedError 23 24 aggregateWeights map[flow.Address]int 25 } 26 27 type signatureEntry struct { 28 flow.TransactionSignature 29 30 signatureType 31 } 32 33 // signatureContinatuion is an internal/helper struct, accessible only by 34 // TransactionVerifier, used to keep track of the signature verification 35 // continuation state. 36 type signatureContinuation struct { 37 // signatureEntry is the initial input. 38 signatureEntry 39 40 // accountKey is set by getAccountKeys(). 41 accountKey flow.AccountPublicKey 42 43 // invokedVerify and verifyErr are set by verifyAccountSignatures(). Note 44 // that verifyAccountSignatures() is always called after getAccountKeys() 45 // (i.e., accountKey is always initialized by the time 46 // verifyAccountSignatures is called). 47 invokedVerify bool 48 verifyErr errors.CodedError 49 } 50 51 func (entry *signatureContinuation) newError(err error) errors.CodedError { 52 return entry.errorBuilder(entry.TransactionSignature, err) 53 } 54 55 func (entry *signatureContinuation) matches( 56 proposalKey flow.ProposalKey, 57 ) bool { 58 return entry.Address == proposalKey.Address && 59 entry.KeyIndex == proposalKey.KeyIndex 60 } 61 62 func (entry *signatureContinuation) verify() errors.CodedError { 63 if entry.invokedVerify { 64 return entry.verifyErr 65 } 66 67 entry.invokedVerify = true 68 69 valid, err := crypto.VerifySignatureFromTransaction( 70 entry.Signature, 71 entry.message, 72 entry.accountKey.PublicKey, 73 entry.accountKey.HashAlgo, 74 ) 75 if err != nil { 76 entry.verifyErr = entry.newError(err) 77 } else if !valid { 78 entry.verifyErr = entry.newError(fmt.Errorf("signature is not valid")) 79 } 80 81 return entry.verifyErr 82 } 83 84 func newSignatureEntries( 85 payloadSignatures []flow.TransactionSignature, 86 payloadMessage []byte, 87 envelopeSignatures []flow.TransactionSignature, 88 envelopeMessage []byte, 89 ) ( 90 []*signatureContinuation, 91 map[flow.Address]int, 92 map[flow.Address]int, 93 error, 94 ) { 95 payloadWeights := make(map[flow.Address]int, len(payloadSignatures)) 96 envelopeWeights := make(map[flow.Address]int, len(envelopeSignatures)) 97 98 type pair struct { 99 signatureType 100 signatures []flow.TransactionSignature 101 } 102 103 list := []pair{ 104 { 105 signatureType{ 106 payloadMessage, 107 errors.NewInvalidPayloadSignatureError, 108 payloadWeights, 109 }, 110 payloadSignatures, 111 }, 112 { 113 signatureType{ 114 envelopeMessage, 115 errors.NewInvalidEnvelopeSignatureError, 116 envelopeWeights, 117 }, 118 envelopeSignatures, 119 }, 120 } 121 122 numSignatures := len(payloadSignatures) + len(envelopeSignatures) 123 signatures := make([]*signatureContinuation, 0, numSignatures) 124 125 type uniqueKey struct { 126 address flow.Address 127 index uint64 128 } 129 duplicate := make(map[uniqueKey]struct{}, numSignatures) 130 131 for _, group := range list { 132 for _, signature := range group.signatures { 133 entry := &signatureContinuation{ 134 signatureEntry: signatureEntry{ 135 TransactionSignature: signature, 136 signatureType: group.signatureType, 137 }, 138 } 139 140 key := uniqueKey{ 141 address: signature.Address, 142 index: signature.KeyIndex, 143 } 144 145 _, ok := duplicate[key] 146 if ok { 147 return nil, nil, nil, entry.newError( 148 fmt.Errorf("duplicate signatures are provided for the same key")) 149 } 150 duplicate[key] = struct{}{} 151 signatures = append(signatures, entry) 152 } 153 } 154 155 return signatures, payloadWeights, envelopeWeights, nil 156 } 157 158 // TransactionVerifier verifies the content of the transaction by 159 // checking accounts (authorizers, payer, proposer) are not frozen 160 // checking there is no double signature 161 // all signatures are valid 162 // all accounts provides enoguh weights 163 // 164 // if KeyWeightThreshold is set to a negative number, signature verification is skipped 165 type TransactionVerifier struct { 166 VerificationConcurrency int 167 } 168 169 func (v *TransactionVerifier) CheckAuthorization( 170 tracer tracing.TracerSpan, 171 proc *TransactionProcedure, 172 txnState *state.TransactionState, 173 keyWeightThreshold int, 174 ) error { 175 // TODO(Janez): verification is part of inclusion fees, not execution fees. 176 var err error 177 txnState.RunWithAllLimitsDisabled(func() { 178 err = v.verifyTransaction(tracer, proc, txnState, keyWeightThreshold) 179 }) 180 if err != nil { 181 return fmt.Errorf("transaction verification failed: %w", err) 182 } 183 184 return nil 185 } 186 187 // verifyTransaction verifies the transaction from the given procedure, 188 // and check the Authorizers have enough weights. 189 func (v *TransactionVerifier) verifyTransaction( 190 tracer tracing.TracerSpan, 191 proc *TransactionProcedure, 192 txnState *state.TransactionState, 193 keyWeightThreshold int, 194 ) error { 195 span := tracer.StartChildSpan(trace.FVMVerifyTransaction) 196 span.SetAttributes( 197 attribute.String("transaction.ID", proc.ID.String()), 198 ) 199 defer span.End() 200 201 tx := proc.Transaction 202 if tx.Payer == flow.EmptyAddress { 203 return errors.NewInvalidAddressErrorf(tx.Payer, "payer address is invalid") 204 } 205 206 signatures, payloadWeights, envelopeWeights, err := newSignatureEntries( 207 tx.PayloadSignatures, 208 tx.PayloadMessage(), 209 tx.EnvelopeSignatures, 210 tx.EnvelopeMessage()) 211 if err != nil { 212 return err 213 } 214 215 accounts := environment.NewAccounts(txnState) 216 err = v.checkAccountsAreNotFrozen(tx, accounts) 217 if err != nil { 218 return err 219 } 220 221 if keyWeightThreshold < 0 { 222 return nil 223 } 224 225 err = v.getAccountKeys(txnState, accounts, signatures, tx.ProposalKey) 226 if err != nil { 227 return errors.NewInvalidProposalSignatureError(tx.ProposalKey, err) 228 } 229 230 err = v.verifyAccountSignatures(signatures) 231 if err != nil { 232 return errors.NewInvalidProposalSignatureError(tx.ProposalKey, err) 233 } 234 235 for _, addr := range tx.Authorizers { 236 // Skip this authorizer if it is also the payer. In the case where an account is 237 // both a PAYER as well as an AUTHORIZER or PROPOSER, that account is required 238 // to sign only the envelope. 239 if addr == tx.Payer { 240 continue 241 } 242 // hasSufficientKeyWeight 243 if !v.hasSufficientKeyWeight(payloadWeights, addr, keyWeightThreshold) { 244 return errors.NewAccountAuthorizationErrorf( 245 addr, 246 "authorizer account does not have sufficient signatures (%d < %d)", 247 payloadWeights[addr], 248 keyWeightThreshold) 249 } 250 } 251 252 if !v.hasSufficientKeyWeight(envelopeWeights, tx.Payer, keyWeightThreshold) { 253 // TODO change this to payer error (needed for fees) 254 return errors.NewAccountAuthorizationErrorf( 255 tx.Payer, 256 "payer account does not have sufficient signatures (%d < %d)", 257 envelopeWeights[tx.Payer], 258 keyWeightThreshold) 259 } 260 261 return nil 262 } 263 264 // getAccountKeys gets the signatures' account keys and populate the account 265 // keys into the signature continuation structs. 266 func (v *TransactionVerifier) getAccountKeys( 267 txnState *state.TransactionState, 268 accounts environment.Accounts, 269 signatures []*signatureContinuation, 270 proposalKey flow.ProposalKey, 271 ) error { 272 foundProposalSignature := false 273 for _, signature := range signatures { 274 accountKey, err := accounts.GetPublicKey( 275 signature.Address, 276 signature.KeyIndex) 277 if err != nil { 278 return signature.newError(err) 279 } 280 281 if accountKey.Revoked { 282 return signature.newError( 283 fmt.Errorf("account key has been revoked")) 284 } 285 286 signature.accountKey = accountKey 287 288 if !foundProposalSignature && signature.matches(proposalKey) { 289 foundProposalSignature = true 290 } 291 } 292 293 if !foundProposalSignature { 294 return fmt.Errorf( 295 "either the payload or the envelope should provide proposal " + 296 "signatures") 297 } 298 299 return nil 300 } 301 302 // verifyAccountSignatures verifies the given signature continuations and 303 // aggregate the valid signatures' weights. 304 func (v *TransactionVerifier) verifyAccountSignatures( 305 signatures []*signatureContinuation, 306 ) error { 307 toVerifyChan := make(chan *signatureContinuation, len(signatures)) 308 verifiedChan := make(chan *signatureContinuation, len(signatures)) 309 310 verificationConcurrency := v.VerificationConcurrency 311 if len(signatures) < verificationConcurrency { 312 verificationConcurrency = len(signatures) 313 } 314 315 ctx, cancel := context.WithCancel(context.Background()) 316 defer cancel() 317 318 wg := sync.WaitGroup{} 319 wg.Add(verificationConcurrency) 320 321 for i := 0; i < verificationConcurrency; i++ { 322 go func() { 323 defer wg.Done() 324 325 for entry := range toVerifyChan { 326 err := entry.verify() 327 328 verifiedChan <- entry 329 330 if err != nil { 331 // Signal to other workers to early exit 332 cancel() 333 return 334 } 335 336 select { 337 case <-ctx.Done(): 338 // Another worker has error-ed out. 339 return 340 default: 341 // continue 342 } 343 } 344 }() 345 } 346 347 for _, entry := range signatures { 348 toVerifyChan <- entry 349 } 350 close(toVerifyChan) 351 352 foundError := false 353 for i := 0; i < len(signatures); i++ { 354 entry := <-verifiedChan 355 356 if !entry.invokedVerify { 357 // This is a programming error. 358 return fmt.Errorf("signatureContinuation.verify not called") 359 } 360 361 if entry.verifyErr != nil { 362 // Unfortunately, we cannot return the first error we received 363 // from the verifiedChan since the entries may be out of order, 364 // which could lead to non-deterministic error output. 365 foundError = true 366 break 367 } 368 369 entry.aggregateWeights[entry.Address] += entry.accountKey.Weight 370 } 371 372 if !foundError { 373 return nil 374 } 375 376 // We need to wait for all workers to finish in order to deterministically 377 // return the first error with respect to the signatures slice. 378 379 wg.Wait() 380 381 for _, entry := range signatures { 382 if entry.verifyErr != nil { 383 return entry.verifyErr 384 } 385 } 386 387 panic("Should never reach here") 388 } 389 390 func (v *TransactionVerifier) hasSufficientKeyWeight( 391 weights map[flow.Address]int, 392 address flow.Address, 393 keyWeightThreshold int, 394 ) bool { 395 return weights[address] >= keyWeightThreshold 396 } 397 398 func (v *TransactionVerifier) checkAccountsAreNotFrozen( 399 tx *flow.TransactionBody, 400 accounts environment.Accounts, 401 ) error { 402 authorizers := make([]flow.Address, 0, len(tx.Authorizers)+2) 403 authorizers = append(authorizers, tx.Authorizers...) 404 authorizers = append(authorizers, tx.ProposalKey.Address, tx.Payer) 405 406 for _, authorizer := range authorizers { 407 err := accounts.CheckAccountNotFrozen(authorizer) 408 if err != nil { 409 return fmt.Errorf("checking frozen account failed: %w", err) 410 } 411 } 412 413 return nil 414 }