github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/base_tx_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 txs
     5  
     6  import (
     7  	"encoding/json"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/snow"
    14  	"github.com/MetalBlockchain/metalgo/utils"
    15  	"github.com/MetalBlockchain/metalgo/utils/constants"
    16  	"github.com/MetalBlockchain/metalgo/utils/units"
    17  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    18  	"github.com/MetalBlockchain/metalgo/vms/platformvm/stakeable"
    19  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    20  	"github.com/MetalBlockchain/metalgo/vms/types"
    21  )
    22  
    23  func TestBaseTxSerialization(t *testing.T) {
    24  	require := require.New(t)
    25  
    26  	addr := ids.ShortID{
    27  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
    28  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
    29  		0x44, 0x55, 0x66, 0x77,
    30  	}
    31  
    32  	avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z")
    33  	require.NoError(err)
    34  
    35  	customAssetID := ids.ID{
    36  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    37  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    38  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    39  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    40  	}
    41  
    42  	txID := ids.ID{
    43  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    44  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    45  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    46  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    47  	}
    48  
    49  	simpleBaseTx := &BaseTx{
    50  		BaseTx: avax.BaseTx{
    51  			NetworkID:    constants.MainnetID,
    52  			BlockchainID: constants.PlatformChainID,
    53  			Outs:         []*avax.TransferableOutput{},
    54  			Ins: []*avax.TransferableInput{
    55  				{
    56  					UTXOID: avax.UTXOID{
    57  						TxID:        txID,
    58  						OutputIndex: 1,
    59  					},
    60  					Asset: avax.Asset{
    61  						ID: avaxAssetID,
    62  					},
    63  					In: &secp256k1fx.TransferInput{
    64  						Amt: units.MilliAvax,
    65  						Input: secp256k1fx.Input{
    66  							SigIndices: []uint32{5},
    67  						},
    68  					},
    69  				},
    70  			},
    71  			Memo: types.JSONByteSlice{},
    72  		},
    73  	}
    74  	require.NoError(simpleBaseTx.SyntacticVerify(&snow.Context{
    75  		NetworkID:   1,
    76  		ChainID:     constants.PlatformChainID,
    77  		AVAXAssetID: avaxAssetID,
    78  	}))
    79  
    80  	expectedUnsignedSimpleBaseTxBytes := []byte{
    81  		// Codec version
    82  		0x00, 0x00,
    83  		// BaseTx Type ID
    84  		0x00, 0x00, 0x00, 0x22,
    85  		// Mainnet network ID
    86  		0x00, 0x00, 0x00, 0x01,
    87  		// P-chain blockchain ID
    88  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    89  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    90  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    91  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    92  		// Number of outputs
    93  		0x00, 0x00, 0x00, 0x00,
    94  		// Number of inputs
    95  		0x00, 0x00, 0x00, 0x01,
    96  		// Inputs[0]
    97  		// TxID
    98  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    99  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   100  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   101  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   102  		// Tx output index
   103  		0x00, 0x00, 0x00, 0x01,
   104  		// Mainnet AVAX assetID
   105  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   106  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   107  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   108  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   109  		// secp256k1fx transfer input type ID
   110  		0x00, 0x00, 0x00, 0x05,
   111  		// input amount = 1 MilliAvax
   112  		0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40,
   113  		// number of signatures needed in input
   114  		0x00, 0x00, 0x00, 0x01,
   115  		// index of signer
   116  		0x00, 0x00, 0x00, 0x05,
   117  		// length of memo
   118  		0x00, 0x00, 0x00, 0x00,
   119  	}
   120  	var unsignedSimpleBaseTx UnsignedTx = simpleBaseTx
   121  	unsignedSimpleBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleBaseTx)
   122  	require.NoError(err)
   123  	require.Equal(expectedUnsignedSimpleBaseTxBytes, unsignedSimpleBaseTxBytes)
   124  
   125  	complexBaseTx := &BaseTx{
   126  		BaseTx: avax.BaseTx{
   127  			NetworkID:    constants.MainnetID,
   128  			BlockchainID: constants.PlatformChainID,
   129  			Outs: []*avax.TransferableOutput{
   130  				{
   131  					Asset: avax.Asset{
   132  						ID: avaxAssetID,
   133  					},
   134  					Out: &stakeable.LockOut{
   135  						Locktime: 87654321,
   136  						TransferableOut: &secp256k1fx.TransferOutput{
   137  							Amt: 1,
   138  							OutputOwners: secp256k1fx.OutputOwners{
   139  								Locktime:  12345678,
   140  								Threshold: 0,
   141  								Addrs:     []ids.ShortID{},
   142  							},
   143  						},
   144  					},
   145  				},
   146  				{
   147  					Asset: avax.Asset{
   148  						ID: customAssetID,
   149  					},
   150  					Out: &stakeable.LockOut{
   151  						Locktime: 876543210,
   152  						TransferableOut: &secp256k1fx.TransferOutput{
   153  							Amt: 0xffffffffffffffff,
   154  							OutputOwners: secp256k1fx.OutputOwners{
   155  								Locktime:  0,
   156  								Threshold: 1,
   157  								Addrs: []ids.ShortID{
   158  									addr,
   159  								},
   160  							},
   161  						},
   162  					},
   163  				},
   164  			},
   165  			Ins: []*avax.TransferableInput{
   166  				{
   167  					UTXOID: avax.UTXOID{
   168  						TxID:        txID,
   169  						OutputIndex: 1,
   170  					},
   171  					Asset: avax.Asset{
   172  						ID: avaxAssetID,
   173  					},
   174  					In: &secp256k1fx.TransferInput{
   175  						Amt: units.Avax,
   176  						Input: secp256k1fx.Input{
   177  							SigIndices: []uint32{2, 5},
   178  						},
   179  					},
   180  				},
   181  				{
   182  					UTXOID: avax.UTXOID{
   183  						TxID:        txID,
   184  						OutputIndex: 2,
   185  					},
   186  					Asset: avax.Asset{
   187  						ID: customAssetID,
   188  					},
   189  					In: &stakeable.LockIn{
   190  						Locktime: 876543210,
   191  						TransferableIn: &secp256k1fx.TransferInput{
   192  							Amt: 0xefffffffffffffff,
   193  							Input: secp256k1fx.Input{
   194  								SigIndices: []uint32{0},
   195  							},
   196  						},
   197  					},
   198  				},
   199  				{
   200  					UTXOID: avax.UTXOID{
   201  						TxID:        txID,
   202  						OutputIndex: 3,
   203  					},
   204  					Asset: avax.Asset{
   205  						ID: customAssetID,
   206  					},
   207  					In: &secp256k1fx.TransferInput{
   208  						Amt: 0x1000000000000000,
   209  						Input: secp256k1fx.Input{
   210  							SigIndices: []uint32{},
   211  						},
   212  					},
   213  				},
   214  			},
   215  			Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"),
   216  		},
   217  	}
   218  	avax.SortTransferableOutputs(complexBaseTx.Outs, Codec)
   219  	utils.Sort(complexBaseTx.Ins)
   220  	require.NoError(complexBaseTx.SyntacticVerify(&snow.Context{
   221  		NetworkID:   1,
   222  		ChainID:     constants.PlatformChainID,
   223  		AVAXAssetID: avaxAssetID,
   224  	}))
   225  
   226  	expectedUnsignedComplexBaseTxBytes := []byte{
   227  		// Codec version
   228  		0x00, 0x00,
   229  		// BaseTx Type ID
   230  		0x00, 0x00, 0x00, 0x22,
   231  		// Mainnet network ID
   232  		0x00, 0x00, 0x00, 0x01,
   233  		// P-chain blockchain ID
   234  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   235  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   236  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   237  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   238  		// Number of outputs
   239  		0x00, 0x00, 0x00, 0x02,
   240  		// Outputs[0]
   241  		// Mainnet AVAX assetID
   242  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   243  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   244  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   245  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   246  		// Stakeable locked output type ID
   247  		0x00, 0x00, 0x00, 0x16,
   248  		// Locktime
   249  		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
   250  		// secp256k1fx transfer output type ID
   251  		0x00, 0x00, 0x00, 0x07,
   252  		// amount
   253  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   254  		// secp256k1fx output locktime
   255  		0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e,
   256  		// threshold
   257  		0x00, 0x00, 0x00, 0x00,
   258  		// number of addresses
   259  		0x00, 0x00, 0x00, 0x00,
   260  		// Outputs[1]
   261  		// custom asset ID
   262  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   263  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   264  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   265  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   266  		// Stakeable locked output type ID
   267  		0x00, 0x00, 0x00, 0x16,
   268  		// Locktime
   269  		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
   270  		// secp256k1fx transfer output type ID
   271  		0x00, 0x00, 0x00, 0x07,
   272  		// amount
   273  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   274  		// secp256k1fx output locktime
   275  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   276  		// threshold
   277  		0x00, 0x00, 0x00, 0x01,
   278  		// number of addresses
   279  		0x00, 0x00, 0x00, 0x01,
   280  		// address[0]
   281  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
   282  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
   283  		0x44, 0x55, 0x66, 0x77,
   284  		// number of inputs
   285  		0x00, 0x00, 0x00, 0x03,
   286  		// inputs[0]
   287  		// TxID
   288  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   289  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   290  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   291  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   292  		// Tx output index
   293  		0x00, 0x00, 0x00, 0x01,
   294  		// Mainnet AVAX assetID
   295  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   296  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   297  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   298  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   299  		// secp256k1fx transfer input type ID
   300  		0x00, 0x00, 0x00, 0x05,
   301  		// input amount = 1 Avax
   302  		0x00, 0x00, 0x00, 0x00, 0x3b, 0x9a, 0xca, 0x00,
   303  		// number of signatures needed in input
   304  		0x00, 0x00, 0x00, 0x02,
   305  		// index of first signer
   306  		0x00, 0x00, 0x00, 0x02,
   307  		// index of second signer
   308  		0x00, 0x00, 0x00, 0x05,
   309  		// inputs[1]
   310  		// TxID
   311  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   312  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   313  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   314  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   315  		// Tx output index
   316  		0x00, 0x00, 0x00, 0x02,
   317  		// Custom asset ID
   318  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   319  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   320  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   321  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   322  		// Stakeable locked input type ID
   323  		0x00, 0x00, 0x00, 0x15,
   324  		// Locktime
   325  		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
   326  		// secp256k1fx transfer input type ID
   327  		0x00, 0x00, 0x00, 0x05,
   328  		// input amount
   329  		0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   330  		// number of signatures needed in input
   331  		0x00, 0x00, 0x00, 0x01,
   332  		// index of signer
   333  		0x00, 0x00, 0x00, 0x00,
   334  		// inputs[2]
   335  		// TxID
   336  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   337  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   338  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   339  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   340  		// Tx output index
   341  		0x00, 0x00, 0x00, 0x03,
   342  		// custom asset ID
   343  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   344  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   345  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   346  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   347  		// secp256k1fx transfer input type ID
   348  		0x00, 0x00, 0x00, 0x05,
   349  		// input amount
   350  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   351  		// number of signatures needed in input
   352  		0x00, 0x00, 0x00, 0x00,
   353  		// length of memo
   354  		0x00, 0x00, 0x00, 0x14,
   355  		// memo
   356  		0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c,
   357  		0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73,
   358  		0x01, 0x23, 0x45, 0x21,
   359  	}
   360  	var unsignedComplexBaseTx UnsignedTx = complexBaseTx
   361  	unsignedComplexBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexBaseTx)
   362  	require.NoError(err)
   363  	require.Equal(expectedUnsignedComplexBaseTxBytes, unsignedComplexBaseTxBytes)
   364  
   365  	aliaser := ids.NewAliaser()
   366  	require.NoError(aliaser.Alias(constants.PlatformChainID, "P"))
   367  
   368  	unsignedComplexBaseTx.InitCtx(&snow.Context{
   369  		NetworkID:   1,
   370  		ChainID:     constants.PlatformChainID,
   371  		AVAXAssetID: avaxAssetID,
   372  		BCLookup:    aliaser,
   373  	})
   374  
   375  	unsignedComplexBaseTxJSONBytes, err := json.MarshalIndent(unsignedComplexBaseTx, "", "\t")
   376  	require.NoError(err)
   377  	require.Equal(`{
   378  	"networkID": 1,
   379  	"blockchainID": "11111111111111111111111111111111LpoYY",
   380  	"outputs": [
   381  		{
   382  			"assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z",
   383  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   384  			"output": {
   385  				"locktime": 87654321,
   386  				"output": {
   387  					"addresses": [],
   388  					"amount": 1,
   389  					"locktime": 12345678,
   390  					"threshold": 0
   391  				}
   392  			}
   393  		},
   394  		{
   395  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   396  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   397  			"output": {
   398  				"locktime": 876543210,
   399  				"output": {
   400  					"addresses": [
   401  						"P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex"
   402  					],
   403  					"amount": 18446744073709551615,
   404  					"locktime": 0,
   405  					"threshold": 1
   406  				}
   407  			}
   408  		}
   409  	],
   410  	"inputs": [
   411  		{
   412  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   413  			"outputIndex": 1,
   414  			"assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z",
   415  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   416  			"input": {
   417  				"amount": 1000000000,
   418  				"signatureIndices": [
   419  					2,
   420  					5
   421  				]
   422  			}
   423  		},
   424  		{
   425  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   426  			"outputIndex": 2,
   427  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   428  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   429  			"input": {
   430  				"locktime": 876543210,
   431  				"input": {
   432  					"amount": 17293822569102704639,
   433  					"signatureIndices": [
   434  						0
   435  					]
   436  				}
   437  			}
   438  		},
   439  		{
   440  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   441  			"outputIndex": 3,
   442  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   443  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   444  			"input": {
   445  				"amount": 1152921504606846976,
   446  				"signatureIndices": []
   447  			}
   448  		}
   449  	],
   450  	"memo": "0xf09f98850a77656c6c2074686174277301234521"
   451  }`, string(unsignedComplexBaseTxJSONBytes))
   452  }