github.com/ava-labs/avalanchego@v1.11.11/vms/avm/txs/executor/executor_test.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 "testing" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/stretchr/testify/require" 11 12 "github.com/ava-labs/avalanchego/database" 13 "github.com/ava-labs/avalanchego/database/memdb" 14 "github.com/ava-labs/avalanchego/database/versiondb" 15 "github.com/ava-labs/avalanchego/ids" 16 "github.com/ava-labs/avalanchego/utils/constants" 17 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 18 "github.com/ava-labs/avalanchego/utils/units" 19 "github.com/ava-labs/avalanchego/vms/avm/block" 20 "github.com/ava-labs/avalanchego/vms/avm/fxs" 21 "github.com/ava-labs/avalanchego/vms/avm/state" 22 "github.com/ava-labs/avalanchego/vms/avm/txs" 23 "github.com/ava-labs/avalanchego/vms/components/avax" 24 "github.com/ava-labs/avalanchego/vms/components/verify" 25 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 26 ) 27 28 const trackChecksums = false 29 30 var ( 31 chainID = ids.ID{5, 4, 3, 2, 1} 32 assetID = ids.ID{1, 2, 3} 33 ) 34 35 func TestBaseTxExecutor(t *testing.T) { 36 require := require.New(t) 37 38 secpFx := &secp256k1fx.Fx{} 39 parser, err := block.NewParser( 40 []fxs.Fx{secpFx}, 41 ) 42 require.NoError(err) 43 codec := parser.Codec() 44 45 db := memdb.New() 46 vdb := versiondb.New(db) 47 registerer := prometheus.NewRegistry() 48 state, err := state.New(vdb, parser, registerer, trackChecksums) 49 require.NoError(err) 50 51 utxoID := avax.UTXOID{ 52 TxID: ids.GenerateTestID(), 53 OutputIndex: 1, 54 } 55 56 addr := keys[0].Address() 57 utxo := &avax.UTXO{ 58 UTXOID: utxoID, 59 Asset: avax.Asset{ID: assetID}, 60 Out: &secp256k1fx.TransferOutput{ 61 Amt: 20 * units.KiloAvax, 62 OutputOwners: secp256k1fx.OutputOwners{ 63 Threshold: 1, 64 Addrs: []ids.ShortID{ 65 addr, 66 }, 67 }, 68 }, 69 } 70 71 // Populate the UTXO that we will be consuming 72 state.AddUTXO(utxo) 73 require.NoError(state.Commit()) 74 75 baseTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 76 NetworkID: constants.UnitTestID, 77 BlockchainID: chainID, 78 Ins: []*avax.TransferableInput{{ 79 UTXOID: utxoID, 80 Asset: avax.Asset{ID: assetID}, 81 In: &secp256k1fx.TransferInput{ 82 Amt: 20 * units.KiloAvax, 83 Input: secp256k1fx.Input{ 84 SigIndices: []uint32{ 85 0, 86 }, 87 }, 88 }, 89 }}, 90 Outs: []*avax.TransferableOutput{{ 91 Asset: avax.Asset{ID: assetID}, 92 Out: &secp256k1fx.TransferOutput{ 93 Amt: 10 * units.KiloAvax, 94 OutputOwners: secp256k1fx.OutputOwners{ 95 Threshold: 1, 96 Addrs: []ids.ShortID{addr}, 97 }, 98 }, 99 }}, 100 }}} 101 require.NoError(baseTx.SignSECP256K1Fx(codec, [][]*secp256k1.PrivateKey{{keys[0]}})) 102 103 executor := &Executor{ 104 Codec: codec, 105 State: state, 106 Tx: baseTx, 107 } 108 109 // Execute baseTx 110 require.NoError(baseTx.Unsigned.Visit(executor)) 111 112 // Verify the consumed UTXO was removed from the state 113 _, err = executor.State.GetUTXO(utxoID.InputID()) 114 require.ErrorIs(err, database.ErrNotFound) 115 116 // Verify the produced UTXO was added to the state 117 expectedOutputUTXO := &avax.UTXO{ 118 UTXOID: avax.UTXOID{ 119 TxID: baseTx.TxID, 120 OutputIndex: 0, 121 }, 122 Asset: avax.Asset{ 123 ID: assetID, 124 }, 125 Out: &secp256k1fx.TransferOutput{ 126 Amt: 10 * units.KiloAvax, 127 OutputOwners: secp256k1fx.OutputOwners{ 128 Threshold: 1, 129 Addrs: []ids.ShortID{addr}, 130 }, 131 }, 132 } 133 expectedOutputUTXOID := expectedOutputUTXO.InputID() 134 outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID) 135 require.NoError(err) 136 137 outputUTXOID := outputUTXO.InputID() 138 require.Equal(expectedOutputUTXOID, outputUTXOID) 139 require.Equal(expectedOutputUTXO, outputUTXO) 140 } 141 142 func TestCreateAssetTxExecutor(t *testing.T) { 143 require := require.New(t) 144 145 secpFx := &secp256k1fx.Fx{} 146 parser, err := block.NewParser( 147 []fxs.Fx{secpFx}, 148 ) 149 require.NoError(err) 150 codec := parser.Codec() 151 152 db := memdb.New() 153 vdb := versiondb.New(db) 154 registerer := prometheus.NewRegistry() 155 state, err := state.New(vdb, parser, registerer, trackChecksums) 156 require.NoError(err) 157 158 utxoID := avax.UTXOID{ 159 TxID: ids.GenerateTestID(), 160 OutputIndex: 1, 161 } 162 163 addr := keys[0].Address() 164 utxo := &avax.UTXO{ 165 UTXOID: utxoID, 166 Asset: avax.Asset{ID: assetID}, 167 Out: &secp256k1fx.TransferOutput{ 168 Amt: 20 * units.KiloAvax, 169 OutputOwners: secp256k1fx.OutputOwners{ 170 Threshold: 1, 171 Addrs: []ids.ShortID{ 172 addr, 173 }, 174 }, 175 }, 176 } 177 178 // Populate the UTXO that we will be consuming 179 state.AddUTXO(utxo) 180 require.NoError(state.Commit()) 181 182 createAssetTx := &txs.Tx{Unsigned: &txs.CreateAssetTx{ 183 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 184 NetworkID: constants.UnitTestID, 185 BlockchainID: chainID, 186 Ins: []*avax.TransferableInput{{ 187 UTXOID: utxoID, 188 Asset: avax.Asset{ID: assetID}, 189 In: &secp256k1fx.TransferInput{ 190 Amt: 20 * units.KiloAvax, 191 Input: secp256k1fx.Input{ 192 SigIndices: []uint32{ 193 0, 194 }, 195 }, 196 }, 197 }}, 198 Outs: []*avax.TransferableOutput{{ 199 Asset: avax.Asset{ID: assetID}, 200 Out: &secp256k1fx.TransferOutput{ 201 Amt: 10 * units.KiloAvax, 202 OutputOwners: secp256k1fx.OutputOwners{ 203 Threshold: 1, 204 Addrs: []ids.ShortID{addr}, 205 }, 206 }, 207 }}, 208 }}, 209 Name: "name", 210 Symbol: "symb", 211 Denomination: 0, 212 States: []*txs.InitialState{ 213 { 214 FxIndex: 0, 215 Outs: []verify.State{ 216 &secp256k1fx.MintOutput{ 217 OutputOwners: secp256k1fx.OutputOwners{ 218 Threshold: 1, 219 Addrs: []ids.ShortID{addr}, 220 }, 221 }, 222 }, 223 }, 224 }, 225 }} 226 require.NoError(createAssetTx.SignSECP256K1Fx(codec, [][]*secp256k1.PrivateKey{{keys[0]}})) 227 228 executor := &Executor{ 229 Codec: codec, 230 State: state, 231 Tx: createAssetTx, 232 } 233 234 // Execute createAssetTx 235 require.NoError(createAssetTx.Unsigned.Visit(executor)) 236 237 // Verify the consumed UTXO was removed from the state 238 _, err = executor.State.GetUTXO(utxoID.InputID()) 239 require.ErrorIs(err, database.ErrNotFound) 240 241 // Verify the produced UTXOs were added to the state 242 txID := createAssetTx.ID() 243 expectedOutputUTXOs := []*avax.UTXO{ 244 { 245 UTXOID: avax.UTXOID{ 246 TxID: txID, 247 OutputIndex: 0, 248 }, 249 Asset: avax.Asset{ 250 ID: assetID, 251 }, 252 Out: &secp256k1fx.TransferOutput{ 253 Amt: 10 * units.KiloAvax, 254 OutputOwners: secp256k1fx.OutputOwners{ 255 Threshold: 1, 256 Addrs: []ids.ShortID{addr}, 257 }, 258 }, 259 }, 260 { 261 UTXOID: avax.UTXOID{ 262 TxID: txID, 263 OutputIndex: 1, 264 }, 265 Asset: avax.Asset{ 266 ID: txID, 267 }, 268 Out: &secp256k1fx.MintOutput{ 269 OutputOwners: secp256k1fx.OutputOwners{ 270 Threshold: 1, 271 Addrs: []ids.ShortID{addr}, 272 }, 273 }, 274 }, 275 } 276 for _, expectedOutputUTXO := range expectedOutputUTXOs { 277 expectedOutputUTXOID := expectedOutputUTXO.InputID() 278 outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID) 279 require.NoError(err) 280 281 outputUTXOID := outputUTXO.InputID() 282 require.Equal(expectedOutputUTXOID, outputUTXOID) 283 require.Equal(expectedOutputUTXO, outputUTXO) 284 } 285 } 286 287 func TestOperationTxExecutor(t *testing.T) { 288 require := require.New(t) 289 290 secpFx := &secp256k1fx.Fx{} 291 parser, err := block.NewParser( 292 []fxs.Fx{secpFx}, 293 ) 294 require.NoError(err) 295 codec := parser.Codec() 296 297 db := memdb.New() 298 vdb := versiondb.New(db) 299 registerer := prometheus.NewRegistry() 300 state, err := state.New(vdb, parser, registerer, trackChecksums) 301 require.NoError(err) 302 303 outputOwners := secp256k1fx.OutputOwners{ 304 Threshold: 1, 305 Addrs: []ids.ShortID{ 306 keys[0].Address(), 307 }, 308 } 309 310 utxoID := avax.UTXOID{ 311 TxID: ids.GenerateTestID(), 312 OutputIndex: 1, 313 } 314 utxo := &avax.UTXO{ 315 UTXOID: utxoID, 316 Asset: avax.Asset{ID: assetID}, 317 Out: &secp256k1fx.TransferOutput{ 318 Amt: 20 * units.KiloAvax, 319 OutputOwners: outputOwners, 320 }, 321 } 322 323 opUTXOID := avax.UTXOID{ 324 TxID: ids.GenerateTestID(), 325 OutputIndex: 1, 326 } 327 opUTXO := &avax.UTXO{ 328 UTXOID: opUTXOID, 329 Asset: avax.Asset{ID: assetID}, 330 Out: &secp256k1fx.MintOutput{ 331 OutputOwners: outputOwners, 332 }, 333 } 334 335 // Populate the UTXOs that we will be consuming 336 state.AddUTXO(utxo) 337 state.AddUTXO(opUTXO) 338 require.NoError(state.Commit()) 339 340 operationTx := &txs.Tx{Unsigned: &txs.OperationTx{ 341 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 342 NetworkID: constants.UnitTestID, 343 BlockchainID: chainID, 344 Ins: []*avax.TransferableInput{{ 345 UTXOID: utxoID, 346 Asset: avax.Asset{ID: assetID}, 347 In: &secp256k1fx.TransferInput{ 348 Amt: 20 * units.KiloAvax, 349 Input: secp256k1fx.Input{ 350 SigIndices: []uint32{ 351 0, 352 }, 353 }, 354 }, 355 }}, 356 Outs: []*avax.TransferableOutput{{ 357 Asset: avax.Asset{ID: assetID}, 358 Out: &secp256k1fx.TransferOutput{ 359 Amt: 10 * units.KiloAvax, 360 OutputOwners: outputOwners, 361 }, 362 }}, 363 }}, 364 Ops: []*txs.Operation{{ 365 Asset: avax.Asset{ID: assetID}, 366 UTXOIDs: []*avax.UTXOID{ 367 &opUTXOID, 368 }, 369 Op: &secp256k1fx.MintOperation{ 370 MintInput: secp256k1fx.Input{ 371 SigIndices: []uint32{0}, 372 }, 373 MintOutput: secp256k1fx.MintOutput{ 374 OutputOwners: outputOwners, 375 }, 376 TransferOutput: secp256k1fx.TransferOutput{ 377 Amt: 12345, 378 OutputOwners: outputOwners, 379 }, 380 }, 381 }}, 382 }} 383 require.NoError(operationTx.SignSECP256K1Fx( 384 codec, 385 [][]*secp256k1.PrivateKey{ 386 {keys[0]}, 387 {keys[0]}, 388 }, 389 )) 390 391 executor := &Executor{ 392 Codec: codec, 393 State: state, 394 Tx: operationTx, 395 } 396 397 // Execute operationTx 398 require.NoError(operationTx.Unsigned.Visit(executor)) 399 400 // Verify the consumed UTXOs were removed from the state 401 _, err = executor.State.GetUTXO(utxo.InputID()) 402 require.ErrorIs(err, database.ErrNotFound) 403 _, err = executor.State.GetUTXO(opUTXO.InputID()) 404 require.ErrorIs(err, database.ErrNotFound) 405 406 // Verify the produced UTXOs were added to the state 407 txID := operationTx.ID() 408 expectedOutputUTXOs := []*avax.UTXO{ 409 { 410 UTXOID: avax.UTXOID{ 411 TxID: txID, 412 OutputIndex: 0, 413 }, 414 Asset: avax.Asset{ 415 ID: assetID, 416 }, 417 Out: &secp256k1fx.TransferOutput{ 418 Amt: 10 * units.KiloAvax, 419 OutputOwners: outputOwners, 420 }, 421 }, 422 { 423 UTXOID: avax.UTXOID{ 424 TxID: txID, 425 OutputIndex: 1, 426 }, 427 Asset: avax.Asset{ 428 ID: assetID, 429 }, 430 Out: &secp256k1fx.MintOutput{ 431 OutputOwners: outputOwners, 432 }, 433 }, 434 { 435 UTXOID: avax.UTXOID{ 436 TxID: txID, 437 OutputIndex: 2, 438 }, 439 Asset: avax.Asset{ 440 ID: assetID, 441 }, 442 Out: &secp256k1fx.TransferOutput{ 443 Amt: 12345, 444 OutputOwners: outputOwners, 445 }, 446 }, 447 } 448 for _, expectedOutputUTXO := range expectedOutputUTXOs { 449 expectedOutputUTXOID := expectedOutputUTXO.InputID() 450 outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID) 451 require.NoError(err) 452 453 outputUTXOID := outputUTXO.InputID() 454 require.Equal(expectedOutputUTXOID, outputUTXOID) 455 require.Equal(expectedOutputUTXO, outputUTXO) 456 } 457 }