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 }