github.com/koko1123/flow-go-1@v0.29.6/access/validator.go (about)

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