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  }