github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/txs/executor/syntactic_verifier.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package executor
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  	"unicode"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils"
    14  	"github.com/MetalBlockchain/metalgo/utils/set"
    15  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    16  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    17  )
    18  
    19  const (
    20  	minNameLen      = 1
    21  	maxNameLen      = 128
    22  	minSymbolLen    = 1
    23  	maxSymbolLen    = 4
    24  	maxDenomination = 32
    25  )
    26  
    27  var (
    28  	_ txs.Visitor = (*SyntacticVerifier)(nil)
    29  
    30  	errWrongNumberOfCredentials     = errors.New("wrong number of credentials")
    31  	errInitialStatesNotSortedUnique = errors.New("initial states not sorted and unique")
    32  	errNameTooShort                 = fmt.Errorf("name is too short, minimum size is %d", minNameLen)
    33  	errNameTooLong                  = fmt.Errorf("name is too long, maximum size is %d", maxNameLen)
    34  	errSymbolTooShort               = fmt.Errorf("symbol is too short, minimum size is %d", minSymbolLen)
    35  	errSymbolTooLong                = fmt.Errorf("symbol is too long, maximum size is %d", maxSymbolLen)
    36  	errNoFxs                        = errors.New("assets must support at least one Fx")
    37  	errIllegalNameCharacter         = errors.New("asset's name must be made up of only letters and numbers")
    38  	errIllegalSymbolCharacter       = errors.New("asset's symbol must be all upper case letters")
    39  	errUnexpectedWhitespace         = errors.New("unexpected whitespace provided")
    40  	errDenominationTooLarge         = errors.New("denomination is too large")
    41  	errOperationsNotSortedUnique    = errors.New("operations not sorted and unique")
    42  	errNoOperations                 = errors.New("an operationTx must have at least one operation")
    43  	errDoubleSpend                  = errors.New("inputs attempt to double spend an input")
    44  	errNoImportInputs               = errors.New("no import inputs")
    45  	errNoExportOutputs              = errors.New("no export outputs")
    46  )
    47  
    48  type SyntacticVerifier struct {
    49  	*Backend
    50  	Tx *txs.Tx
    51  }
    52  
    53  func (v *SyntacticVerifier) BaseTx(tx *txs.BaseTx) error {
    54  	if err := tx.BaseTx.Verify(v.Ctx); err != nil {
    55  		return err
    56  	}
    57  
    58  	err := avax.VerifyTx(
    59  		v.Config.TxFee,
    60  		v.FeeAssetID,
    61  		[][]*avax.TransferableInput{tx.Ins},
    62  		[][]*avax.TransferableOutput{tx.Outs},
    63  		v.Codec,
    64  	)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	for _, cred := range v.Tx.Creds {
    70  		if err := cred.Verify(); err != nil {
    71  			return err
    72  		}
    73  	}
    74  
    75  	numCreds := len(v.Tx.Creds)
    76  	numInputs := len(tx.Ins)
    77  	if numCreds != numInputs {
    78  		return fmt.Errorf("%w: %d != %d",
    79  			errWrongNumberOfCredentials,
    80  			numCreds,
    81  			numInputs,
    82  		)
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  func (v *SyntacticVerifier) CreateAssetTx(tx *txs.CreateAssetTx) error {
    89  	switch {
    90  	case len(tx.Name) < minNameLen:
    91  		return errNameTooShort
    92  	case len(tx.Name) > maxNameLen:
    93  		return errNameTooLong
    94  	case len(tx.Symbol) < minSymbolLen:
    95  		return errSymbolTooShort
    96  	case len(tx.Symbol) > maxSymbolLen:
    97  		return errSymbolTooLong
    98  	case len(tx.States) == 0:
    99  		return errNoFxs
   100  	case tx.Denomination > maxDenomination:
   101  		return errDenominationTooLarge
   102  	case strings.TrimSpace(tx.Name) != tx.Name:
   103  		return errUnexpectedWhitespace
   104  	}
   105  
   106  	for _, r := range tx.Name {
   107  		if r > unicode.MaxASCII || !(unicode.IsLetter(r) || unicode.IsNumber(r) || r == ' ') {
   108  			return errIllegalNameCharacter
   109  		}
   110  	}
   111  	for _, r := range tx.Symbol {
   112  		if r > unicode.MaxASCII || !unicode.IsUpper(r) {
   113  			return errIllegalSymbolCharacter
   114  		}
   115  	}
   116  
   117  	if err := tx.BaseTx.BaseTx.Verify(v.Ctx); err != nil {
   118  		return err
   119  	}
   120  
   121  	err := avax.VerifyTx(
   122  		v.Config.CreateAssetTxFee,
   123  		v.FeeAssetID,
   124  		[][]*avax.TransferableInput{tx.Ins},
   125  		[][]*avax.TransferableOutput{tx.Outs},
   126  		v.Codec,
   127  	)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	for _, state := range tx.States {
   133  		if err := state.Verify(v.Codec, len(v.Fxs)); err != nil {
   134  			return err
   135  		}
   136  	}
   137  	if !utils.IsSortedAndUnique(tx.States) {
   138  		return errInitialStatesNotSortedUnique
   139  	}
   140  
   141  	for _, cred := range v.Tx.Creds {
   142  		if err := cred.Verify(); err != nil {
   143  			return err
   144  		}
   145  	}
   146  
   147  	numCreds := len(v.Tx.Creds)
   148  	numInputs := len(tx.Ins)
   149  	if numCreds != numInputs {
   150  		return fmt.Errorf("%w: %d != %d",
   151  			errWrongNumberOfCredentials,
   152  			numCreds,
   153  			numInputs,
   154  		)
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func (v *SyntacticVerifier) OperationTx(tx *txs.OperationTx) error {
   161  	if len(tx.Ops) == 0 {
   162  		return errNoOperations
   163  	}
   164  
   165  	if err := tx.BaseTx.BaseTx.Verify(v.Ctx); err != nil {
   166  		return err
   167  	}
   168  
   169  	err := avax.VerifyTx(
   170  		v.Config.TxFee,
   171  		v.FeeAssetID,
   172  		[][]*avax.TransferableInput{tx.Ins},
   173  		[][]*avax.TransferableOutput{tx.Outs},
   174  		v.Codec,
   175  	)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	inputs := set.NewSet[ids.ID](len(tx.Ins))
   181  	for _, in := range tx.Ins {
   182  		inputs.Add(in.InputID())
   183  	}
   184  
   185  	for _, op := range tx.Ops {
   186  		if err := op.Verify(); err != nil {
   187  			return err
   188  		}
   189  		for _, utxoID := range op.UTXOIDs {
   190  			inputID := utxoID.InputID()
   191  			if inputs.Contains(inputID) {
   192  				return errDoubleSpend
   193  			}
   194  			inputs.Add(inputID)
   195  		}
   196  	}
   197  	if !txs.IsSortedAndUniqueOperations(tx.Ops, v.Codec) {
   198  		return errOperationsNotSortedUnique
   199  	}
   200  
   201  	for _, cred := range v.Tx.Creds {
   202  		if err := cred.Verify(); err != nil {
   203  			return err
   204  		}
   205  	}
   206  
   207  	numCreds := len(v.Tx.Creds)
   208  	numInputs := len(tx.Ins) + len(tx.Ops)
   209  	if numCreds != numInputs {
   210  		return fmt.Errorf("%w: %d != %d",
   211  			errWrongNumberOfCredentials,
   212  			numCreds,
   213  			numInputs,
   214  		)
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  func (v *SyntacticVerifier) ImportTx(tx *txs.ImportTx) error {
   221  	if len(tx.ImportedIns) == 0 {
   222  		return errNoImportInputs
   223  	}
   224  
   225  	if err := tx.BaseTx.BaseTx.Verify(v.Ctx); err != nil {
   226  		return err
   227  	}
   228  
   229  	err := avax.VerifyTx(
   230  		v.Config.TxFee,
   231  		v.FeeAssetID,
   232  		[][]*avax.TransferableInput{
   233  			tx.Ins,
   234  			tx.ImportedIns,
   235  		},
   236  		[][]*avax.TransferableOutput{tx.Outs},
   237  		v.Codec,
   238  	)
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	for _, cred := range v.Tx.Creds {
   244  		if err := cred.Verify(); err != nil {
   245  			return err
   246  		}
   247  	}
   248  
   249  	numCreds := len(v.Tx.Creds)
   250  	numInputs := len(tx.Ins) + len(tx.ImportedIns)
   251  	if numCreds != numInputs {
   252  		return fmt.Errorf("%w: %d != %d",
   253  			errWrongNumberOfCredentials,
   254  			numCreds,
   255  			numInputs,
   256  		)
   257  	}
   258  
   259  	return nil
   260  }
   261  
   262  func (v *SyntacticVerifier) ExportTx(tx *txs.ExportTx) error {
   263  	if len(tx.ExportedOuts) == 0 {
   264  		return errNoExportOutputs
   265  	}
   266  
   267  	if err := tx.BaseTx.BaseTx.Verify(v.Ctx); err != nil {
   268  		return err
   269  	}
   270  
   271  	err := avax.VerifyTx(
   272  		v.Config.TxFee,
   273  		v.FeeAssetID,
   274  		[][]*avax.TransferableInput{tx.Ins},
   275  		[][]*avax.TransferableOutput{
   276  			tx.Outs,
   277  			tx.ExportedOuts,
   278  		},
   279  		v.Codec,
   280  	)
   281  	if err != nil {
   282  		return err
   283  	}
   284  
   285  	for _, cred := range v.Tx.Creds {
   286  		if err := cred.Verify(); err != nil {
   287  			return err
   288  		}
   289  	}
   290  
   291  	numCreds := len(v.Tx.Creds)
   292  	numInputs := len(tx.Ins)
   293  	if numCreds != numInputs {
   294  		return fmt.Errorf("%w: %d != %d",
   295  			errWrongNumberOfCredentials,
   296  			numCreds,
   297  			numInputs,
   298  		)
   299  	}
   300  
   301  	return nil
   302  }