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  }