github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/transaction/transaction_test.go (about)

     1  package transaction
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"math"
     8  	"testing"
     9  
    10  	"github.com/nspcc-dev/neo-go/internal/random"
    11  	"github.com/nspcc-dev/neo-go/internal/testserdes"
    12  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    13  	"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
    14  	"github.com/nspcc-dev/neo-go/pkg/util"
    15  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestWitnessEncodeDecode(t *testing.T) {
    21  	verif, err := hex.DecodeString("552102486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a7021024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d2102aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e2103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c2103b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a2102ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba5542102df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e89509357ae")
    22  	assert.Nil(t, err)
    23  	invoc, err := hex.DecodeString("404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f4038a338f879930c8adc168983f60aae6f8542365d844f004976346b70fb0dd31aa1dbd4abd81e4a4aeef9941ecd4e2dd2c1a5b05e1cc74454d0403edaee6d7a4d4099d33c0b889bf6f3e6d87ab1b11140282e9a3265b0b9b918d6020b2c62d5a040c7e0c2c7c1dae3af9b19b178c71552ebd0b596e401c175067c70ea75717c8c00404e0ebd369e81093866fe29406dbf6b402c003774541799d08bf9bb0fc6070ec0f6bad908ab95f05fa64e682b485800b3c12102a8596e6c715ec76f4564d5eff34070e0521979fcd2cbbfa1456d97cc18d9b4a6ad87a97a2a0bcdedbf71b6c9676c645886056821b6f3fec8694894c66f41b762bc4e29e46ad15aee47f05d27d822")
    24  	assert.Nil(t, err)
    25  
    26  	lenInvoc := len(invoc)
    27  	lenVerif := len(verif)
    28  	t.Log(lenInvoc)
    29  	t.Log(lenVerif)
    30  
    31  	wit := &Witness{
    32  		InvocationScript:   invoc,
    33  		VerificationScript: verif,
    34  	}
    35  
    36  	witDecode := &Witness{}
    37  	testserdes.EncodeDecodeBinary(t, wit, witDecode)
    38  
    39  	t.Log(len(witDecode.VerificationScript))
    40  	t.Log(len(witDecode.InvocationScript))
    41  }
    42  
    43  func TestDecodeEncodeInvocationTX(t *testing.T) {
    44  	tx := decodeTransaction(rawInvocationTX, t)
    45  
    46  	script := "CwMAQNndiE0KAAwUgM7HtvW1b1BXj3N/Fi06sU1GZQ0MFN7uecGJ8wCYsLpqLrkLOpJYpsf/FMAfDAh0cmFuc2ZlcgwUz3bii9AGLEpHjuNVYQETGfPPpNJBYn1bUjk="
    47  	assert.Equal(t, script, base64.StdEncoding.EncodeToString(tx.Script))
    48  	assert.Equal(t, uint32(431760600), tx.Nonce)
    49  	assert.Equal(t, int64(11000000), tx.SystemFee)
    50  	assert.Equal(t, int64(4500000), tx.NetworkFee)
    51  	assert.Equal(t, uint32(1000), tx.ValidUntilBlock)
    52  	assert.Equal(t, "25426643feed564cd3e57f346d6c68692f5622b3063da11c5572d99ee1a5b49a", tx.Hash().StringLE())
    53  
    54  	assert.Equal(t, 1, len(tx.Signers))
    55  	assert.Equal(t, CalledByEntry, tx.Signers[0].Scopes)
    56  	assert.Equal(t, "ffc7a658923a0bb92e6abab09800f389c179eede", tx.Signers[0].Account.StringLE())
    57  
    58  	assert.Equal(t, 0, len(tx.Attributes))
    59  	invoc1 := "DEDWn0D7z2ELqpN8ghcM/PtfFwo56/BfEasfHuSKECJMYxvU47r2ZtSihg59lGxSZzHsvxTy6nsyvJ22ycNhINdJDECl61cg937N/HujKsLMu2wJMS7C54bzJ3q22Czqllvw3Yp809USgKDs+W+3QD7rI+SFs0OhIn0gooCUU6f/13WjDEDr9XdeT5CGTO8CL0JigzcTcucs0GBcqHs8fToO6zPuuCfS7Wh6dyxSCijT4A4S+7BUdW3dsO7828ke1fj8oNxm"
    60  	verif1 := "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl"
    61  	assert.Equal(t, 1, len(tx.Scripts))
    62  	assert.Equal(t, invoc1, base64.StdEncoding.EncodeToString(tx.Scripts[0].InvocationScript))
    63  	assert.Equal(t, verif1, base64.StdEncoding.EncodeToString(tx.Scripts[0].VerificationScript))
    64  
    65  	data, err := testserdes.EncodeBinary(tx)
    66  	assert.NoError(t, err)
    67  	assert.Equal(t, rawInvocationTX, base64.StdEncoding.EncodeToString(data))
    68  }
    69  
    70  func TestNew(t *testing.T) {
    71  	script := []byte{0x51}
    72  	tx := New(script, 1)
    73  	tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}}
    74  	tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}}
    75  	assert.Equal(t, int64(1), tx.SystemFee)
    76  	assert.Equal(t, script, tx.Script)
    77  	// Update hash fields to match tx2 that is gonna autoupdate them on decode.
    78  	_ = tx.Hash()
    79  	_ = tx.Size()
    80  	testserdes.EncodeDecodeBinary(t, tx, &Transaction{})
    81  }
    82  
    83  func TestNewTransactionFromBytes(t *testing.T) {
    84  	script := []byte{0x51}
    85  	tx := New(script, 1)
    86  	tx.NetworkFee = 123
    87  	tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}}
    88  	tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}}
    89  	data, err := testserdes.EncodeBinary(tx)
    90  	require.NoError(t, err)
    91  
    92  	// set cached fields
    93  	tx.Hash()
    94  	tx.FeePerByte()
    95  
    96  	tx1, err := NewTransactionFromBytes(data)
    97  	require.NoError(t, err)
    98  	require.Equal(t, tx, tx1)
    99  
   100  	tx2 := new(Transaction)
   101  	err = testserdes.DecodeBinary(data, tx2)
   102  	require.NoError(t, err)
   103  	require.Equal(t, tx1, tx2)
   104  
   105  	data = append(data, 42)
   106  	_, err = NewTransactionFromBytes(data)
   107  	require.Error(t, err)
   108  }
   109  
   110  func TestEncodingTXWithNoScript(t *testing.T) {
   111  	_, err := testserdes.EncodeBinary(new(Transaction))
   112  	require.NoError(t, err) // Garbage in -> garbage out.
   113  }
   114  
   115  func TestDecodingTXWithNoScript(t *testing.T) {
   116  	txBin, err := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
   117  	require.NoError(t, err)
   118  	err = testserdes.DecodeBinary(txBin, new(Transaction))
   119  	require.Error(t, err)
   120  }
   121  
   122  func TestDecodingTxWithInvalidWitnessesNumber(t *testing.T) {
   123  	tx := New([]byte{byte(opcode.RET)}, 1)
   124  	tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}}
   125  	tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}, {InvocationScript: []byte{}, VerificationScript: []byte{}}}
   126  	data, err := testserdes.EncodeBinary(tx)
   127  	require.NoError(t, err)
   128  	require.ErrorIs(t, testserdes.DecodeBinary(data, new(Transaction)), ErrInvalidWitnessNum)
   129  }
   130  
   131  func TestUnmarshalNeoFSTX(t *testing.T) {
   132  	txjson := []byte(`
   133  {
   134    "hash": "0xc90c77d6c17fbb959b16df820370204a534ca13826cbd9fc027beb1b55d31e5a",
   135    "size": 232,
   136    "version": 0,
   137    "nonce": 737880259,
   138    "sender": "NiRqSd5MtRZT5yUhgWd7oG11brkDG76Jim",
   139    "sysfee": "223719420",
   140    "netfee": "1215550",
   141    "validuntilblock": 1931,
   142    "attributes": [],
   143    "signers": [
   144      {
   145        "account": "0x8f0ecd714c31c5624b6647e5fd661e5031c8f8f6",
   146        "scopes": "Global"
   147      }
   148    ],
   149    "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIRwBESwAwEdm90ZQwUo4Hyc4fSGC6JbZtdrGb9LBbtWJtBYn1bUg==",
   150    "witnesses": [
   151      {
   152        "invocation": "DEDr2gA/8T/wxQvgOZVfCdkbj6uGrprkDgJvpOJCcbl+tvlKZkZytCZEWm6NoZhJyIlEI3VQSLtU3AHuJfShAT5L",
   153        "verification": "DCEDAS1H52IQrsc745qz0YbgpA/o2Gv6PU+r/aV7oTuI+WoLQZVEDXg="
   154      }
   155    ]
   156  }`)
   157  	tx := new(Transaction)
   158  	require.NoError(t, json.Unmarshal(txjson, tx))
   159  }
   160  
   161  func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
   162  	tx := &Transaction{
   163  		Version:    0,
   164  		Signers:    []Signer{{Account: util.Uint160{1, 2, 3}}},
   165  		Script:     []byte{1, 2, 3, 4},
   166  		Attributes: []Attribute{{Type: HighPriority}},
   167  		Scripts:    []Witness{},
   168  		SystemFee:  int64(fixedn.Fixed8FromFloat(123.45)),
   169  		NetworkFee: int64(fixedn.Fixed8FromFloat(0.123)),
   170  		Trimmed:    false,
   171  	}
   172  
   173  	testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
   174  }
   175  
   176  func TestTransaction_HasAttribute(t *testing.T) {
   177  	tx := New([]byte{1}, 0)
   178  	require.False(t, tx.HasAttribute(HighPriority))
   179  	tx.Attributes = append(tx.Attributes, Attribute{Type: HighPriority})
   180  	require.True(t, tx.HasAttribute(HighPriority))
   181  	tx.Attributes = append(tx.Attributes, Attribute{Type: HighPriority})
   182  	require.True(t, tx.HasAttribute(HighPriority))
   183  }
   184  
   185  func TestTransaction_isValid(t *testing.T) {
   186  	newTx := func() *Transaction {
   187  		return &Transaction{
   188  			Version:    0,
   189  			SystemFee:  100,
   190  			NetworkFee: 100,
   191  			Signers: []Signer{
   192  				{Account: util.Uint160{1, 2, 3}},
   193  				{
   194  					Account: util.Uint160{4, 5, 6},
   195  					Scopes:  Global,
   196  				},
   197  			},
   198  			Script:     []byte{1, 2, 3, 4},
   199  			Attributes: []Attribute{},
   200  			Scripts:    []Witness{},
   201  			Trimmed:    false,
   202  		}
   203  	}
   204  
   205  	t.Run("Valid", func(t *testing.T) {
   206  		t.Run("NoAttributes", func(t *testing.T) {
   207  			tx := newTx()
   208  			require.NoError(t, tx.isValid())
   209  		})
   210  		t.Run("HighPriority", func(t *testing.T) {
   211  			tx := newTx()
   212  			tx.Attributes = []Attribute{{Type: HighPriority}}
   213  			require.NoError(t, tx.isValid())
   214  		})
   215  	})
   216  	t.Run("InvalidVersion", func(t *testing.T) {
   217  		tx := newTx()
   218  		tx.Version = 1
   219  		require.ErrorIs(t, tx.isValid(), ErrInvalidVersion)
   220  	})
   221  	t.Run("NegativeSystemFee", func(t *testing.T) {
   222  		tx := newTx()
   223  		tx.SystemFee = -1
   224  		require.ErrorIs(t, tx.isValid(), ErrNegativeSystemFee)
   225  	})
   226  	t.Run("NegativeNetworkFee", func(t *testing.T) {
   227  		tx := newTx()
   228  		tx.NetworkFee = -1
   229  		require.ErrorIs(t, tx.isValid(), ErrNegativeNetworkFee)
   230  	})
   231  	t.Run("TooBigFees", func(t *testing.T) {
   232  		tx := newTx()
   233  		tx.SystemFee = math.MaxInt64 - tx.NetworkFee + 1
   234  		require.ErrorIs(t, tx.isValid(), ErrTooBigFees)
   235  	})
   236  	t.Run("EmptySigners", func(t *testing.T) {
   237  		tx := newTx()
   238  		tx.Signers = tx.Signers[:0]
   239  		require.ErrorIs(t, tx.isValid(), ErrEmptySigners)
   240  	})
   241  	t.Run("NonUniqueSigners", func(t *testing.T) {
   242  		tx := newTx()
   243  		tx.Signers[1].Account = tx.Signers[0].Account
   244  		require.ErrorIs(t, tx.isValid(), ErrNonUniqueSigners)
   245  	})
   246  	t.Run("MultipleHighPriority", func(t *testing.T) {
   247  		tx := newTx()
   248  		tx.Attributes = []Attribute{
   249  			{Type: HighPriority},
   250  			{Type: HighPriority},
   251  		}
   252  		require.ErrorIs(t, tx.isValid(), ErrInvalidAttribute)
   253  	})
   254  	t.Run("MultipleOracle", func(t *testing.T) {
   255  		tx := newTx()
   256  		tx.Attributes = []Attribute{
   257  			{Type: OracleResponseT},
   258  			{Type: OracleResponseT},
   259  		}
   260  		require.ErrorIs(t, tx.isValid(), ErrInvalidAttribute)
   261  	})
   262  	t.Run("NoScript", func(t *testing.T) {
   263  		tx := newTx()
   264  		tx.Script = []byte{}
   265  		require.ErrorIs(t, tx.isValid(), ErrEmptyScript)
   266  	})
   267  }
   268  
   269  func TestTransaction_GetAttributes(t *testing.T) {
   270  	attributesTypes := []AttrType{
   271  		HighPriority,
   272  		OracleResponseT,
   273  		NotValidBeforeT,
   274  	}
   275  	t.Run("no attributes", func(t *testing.T) {
   276  		tx := new(Transaction)
   277  		for _, typ := range attributesTypes {
   278  			require.Nil(t, tx.GetAttributes(typ))
   279  		}
   280  	})
   281  	t.Run("single attributes", func(t *testing.T) {
   282  		attrs := make([]Attribute, len(attributesTypes))
   283  		for i, typ := range attributesTypes {
   284  			attrs[i] = Attribute{Type: typ}
   285  		}
   286  		tx := &Transaction{Attributes: attrs}
   287  		for _, typ := range attributesTypes {
   288  			require.Equal(t, []Attribute{{Type: typ}}, tx.GetAttributes(typ))
   289  		}
   290  	})
   291  	t.Run("multiple attributes", func(t *testing.T) {
   292  		typ := AttrType(ReservedLowerBound + 1)
   293  		conflictsAttrs := []Attribute{{Type: typ}, {Type: typ}}
   294  		tx := Transaction{
   295  			Attributes: append([]Attribute{{Type: HighPriority}}, conflictsAttrs...),
   296  		}
   297  		require.Equal(t, conflictsAttrs, tx.GetAttributes(typ))
   298  	})
   299  }
   300  
   301  func TestTransaction_HasSigner(t *testing.T) {
   302  	u1, u2 := random.Uint160(), random.Uint160()
   303  	tx := Transaction{
   304  		Signers: []Signer{
   305  			{Account: u1}, {Account: u2},
   306  		},
   307  	}
   308  	require.True(t, tx.HasSigner(u1))
   309  	require.False(t, tx.HasSigner(util.Uint160{}))
   310  }
   311  
   312  func BenchmarkTxHash(b *testing.B) {
   313  	script := []byte{0x51}
   314  	tx := New(script, 1)
   315  	tx.NetworkFee = 123
   316  	tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}}
   317  	tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}}
   318  
   319  	// Prime cache.
   320  	tx.Hash()
   321  	for i := 0; i < b.N; i++ {
   322  		_ = tx.Hash()
   323  	}
   324  }
   325  
   326  func TestTransaction_DeepCopy(t *testing.T) {
   327  	origTx := New([]byte{0x01, 0x02, 0x03}, 1000)
   328  	origTx.NetworkFee = 2000
   329  	origTx.SystemFee = 500
   330  	origTx.Nonce = 12345678
   331  	origTx.ValidUntilBlock = 100
   332  	origTx.Version = 1
   333  	require.Nil(t, (*Transaction)(nil).Copy())
   334  
   335  	priv, err := keys.NewPrivateKey()
   336  	require.NoError(t, err)
   337  	origTx.Signers = []Signer{
   338  		{Account: random.Uint160(), Scopes: Global, AllowedContracts: []util.Uint160{random.Uint160()}, AllowedGroups: keys.PublicKeys{priv.PublicKey()}, Rules: []WitnessRule{{Action: 0x01, Condition: ConditionCalledByEntry{}}}},
   339  		{Account: random.Uint160(), Scopes: CalledByEntry},
   340  	}
   341  	origTx.Attributes = []Attribute{
   342  		{Type: HighPriority, Value: &OracleResponse{
   343  			ID:     0,
   344  			Code:   Success,
   345  			Result: []byte{4, 8, 15, 16, 23, 42},
   346  		}},
   347  	}
   348  	origTx.Scripts = []Witness{
   349  		{
   350  			InvocationScript:   []byte{0x04, 0x05},
   351  			VerificationScript: []byte{0x06, 0x07},
   352  		},
   353  	}
   354  	origTxHash := origTx.Hash()
   355  
   356  	copyTx := origTx.Copy()
   357  
   358  	require.Equal(t, origTx.Hash(), copyTx.Hash())
   359  	require.Equal(t, origTx, copyTx)
   360  	require.Equal(t, origTx.Size(), copyTx.Size())
   361  
   362  	copyTx.NetworkFee = 3000
   363  	copyTx.Signers[0].Scopes = None
   364  	copyTx.Attributes[0].Type = NotaryAssistedT
   365  	copyTx.Scripts[0].InvocationScript[0] = 0x08
   366  	copyTx.hashed = false
   367  	modifiedCopyTxHash := copyTx.Hash()
   368  
   369  	require.NotEqual(t, origTx.NetworkFee, copyTx.NetworkFee)
   370  	require.NotEqual(t, origTx.Signers[0].Scopes, copyTx.Signers[0].Scopes)
   371  	require.NotEqual(t, origTx.Attributes, copyTx.Attributes)
   372  	require.NotEqual(t, origTxHash, modifiedCopyTxHash)
   373  
   374  	require.NotEqual(t, &origTx.Scripts[0].InvocationScript[0], &copyTx.Scripts[0].InvocationScript[0])
   375  	require.NotEqual(t, &origTx.Scripts, &copyTx.Scripts)
   376  	require.Equal(t, origTx.Scripts[0].VerificationScript, copyTx.Scripts[0].VerificationScript)
   377  	require.Equal(t, origTx.Signers[0].AllowedContracts, copyTx.Signers[0].AllowedContracts)
   378  	require.Equal(t, origTx.Signers[0].AllowedGroups, copyTx.Signers[0].AllowedGroups)
   379  	origGroup := origTx.Signers[0].AllowedGroups[0]
   380  	copyGroup := copyTx.Signers[0].AllowedGroups[0]
   381  	require.True(t, origGroup.Equal(copyGroup))
   382  
   383  	copyTx.Signers[0].AllowedGroups[0] = nil
   384  	require.NotEqual(t, origTx.Signers[0].AllowedGroups[0], copyTx.Signers[0].AllowedGroups[0])
   385  
   386  	require.Equal(t, origTx.Signers[0].Rules[0], copyTx.Signers[0].Rules[0])
   387  	copyTx.Signers[0].Rules[0].Action = 0x02
   388  	require.NotEqual(t, origTx.Signers[0].Rules[0].Action, copyTx.Signers[0].Rules[0].Action)
   389  }