github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/context/context_test.go (about)

     1  package context
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/nspcc-dev/neo-go/internal/testserdes"
     8  	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    10  	"github.com/nspcc-dev/neo-go/pkg/core/interop/crypto"
    11  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    12  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    13  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    14  	"github.com/nspcc-dev/neo-go/pkg/util"
    15  	"github.com/nspcc-dev/neo-go/pkg/vm"
    16  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    17  	"github.com/nspcc-dev/neo-go/pkg/wallet"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  type verifStub struct{}
    22  
    23  func (v verifStub) Hash() util.Uint256                    { return util.Uint256{1, 2, 3} }
    24  func (v verifStub) EncodeHashableFields() ([]byte, error) { return []byte{1}, nil }
    25  func (v verifStub) DecodeHashableFields([]byte) error     { return nil }
    26  
    27  func TestParameterContext_AddSignatureSimpleContract(t *testing.T) {
    28  	priv, err := keys.NewPrivateKey()
    29  	require.NoError(t, err)
    30  	pub := priv.PublicKey()
    31  	tx := getContractTx(pub.GetScriptHash())
    32  	sig := priv.SignHashable(uint32(netmode.UnitTestNet), tx)
    33  
    34  	t.Run("invalid contract", func(t *testing.T) {
    35  		c := NewParameterContext("Neo.Core.ContractTransaction", netmode.UnitTestNet, tx)
    36  		ctr := &wallet.Contract{
    37  			Script: pub.GetVerificationScript(),
    38  			Parameters: []wallet.ContractParam{
    39  				newParam(smartcontract.SignatureType, "parameter0"),
    40  				newParam(smartcontract.SignatureType, "parameter1"),
    41  			},
    42  		}
    43  		require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
    44  		if item := c.Items[ctr.ScriptHash()]; item != nil {
    45  			require.Nil(t, item.Parameters[0].Value)
    46  		}
    47  
    48  		ctr.Parameters = ctr.Parameters[:0]
    49  		require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
    50  		if item := c.Items[ctr.ScriptHash()]; item != nil {
    51  			require.Nil(t, item.Parameters[0].Value)
    52  		}
    53  	})
    54  
    55  	c := NewParameterContext("Neo.Core.ContractTransaction", netmode.UnitTestNet, tx)
    56  	ctr := &wallet.Contract{
    57  		Script:     pub.GetVerificationScript(),
    58  		Parameters: []wallet.ContractParam{newParam(smartcontract.SignatureType, "parameter0")},
    59  	}
    60  	require.NoError(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
    61  	item := c.Items[ctr.ScriptHash()]
    62  	require.NotNil(t, item)
    63  	require.Equal(t, sig, item.Parameters[0].Value)
    64  
    65  	t.Run("GetWitness", func(t *testing.T) {
    66  		w, err := c.GetWitness(ctr.ScriptHash())
    67  		require.NoError(t, err)
    68  		v := newTestVM(w, tx)
    69  		require.NoError(t, v.Run())
    70  		require.Equal(t, 1, v.Estack().Len())
    71  		require.Equal(t, true, v.Estack().Pop().Value())
    72  	})
    73  	t.Run("not found", func(t *testing.T) {
    74  		ctr := &wallet.Contract{
    75  			Script:     []byte{byte(opcode.DROP), byte(opcode.PUSHT)},
    76  			Parameters: []wallet.ContractParam{newParam(smartcontract.SignatureType, "parameter0")},
    77  		}
    78  		_, err := c.GetWitness(ctr.ScriptHash())
    79  		require.Error(t, err)
    80  	})
    81  }
    82  
    83  func TestGetCompleteTransactionForNonTx(t *testing.T) {
    84  	c := NewParameterContext("Neo.Network.P2P.Payloads.Block", netmode.UnitTestNet, verifStub{})
    85  	_, err := c.GetCompleteTransaction()
    86  	require.Error(t, err)
    87  }
    88  
    89  func TestParameterContext_AddSignatureMultisig(t *testing.T) {
    90  	privs, pubs := getPrivateKeys(t, 4)
    91  	pubsCopy := keys.PublicKeys(pubs).Copy()
    92  	script, err := smartcontract.CreateMultiSigRedeemScript(3, pubsCopy)
    93  	require.NoError(t, err)
    94  
    95  	ctr := &wallet.Contract{
    96  		Script: script,
    97  		Parameters: []wallet.ContractParam{
    98  			newParam(smartcontract.SignatureType, "parameter0"),
    99  			newParam(smartcontract.SignatureType, "parameter1"),
   100  			newParam(smartcontract.SignatureType, "parameter2"),
   101  		},
   102  	}
   103  	tx := getContractTx(ctr.ScriptHash())
   104  	c := NewParameterContext(TransactionType, netmode.UnitTestNet, tx)
   105  	priv, err := keys.NewPrivateKey()
   106  	require.NoError(t, err)
   107  	sig := priv.SignHashable(uint32(c.Network), tx)
   108  	require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, priv.PublicKey(), sig))
   109  
   110  	indices := []int{2, 3, 0, 1} // random order
   111  	testSigWit := func(t *testing.T, num int) {
   112  		t.Run("GetCompleteTransaction, bad", func(t *testing.T) {
   113  			_, err := c.GetCompleteTransaction()
   114  			require.Error(t, err)
   115  		})
   116  		for _, i := range indices[:num] {
   117  			sig := privs[i].SignHashable(uint32(c.Network), tx)
   118  			require.NoError(t, c.AddSignature(ctr.ScriptHash(), ctr, pubs[i], sig))
   119  			require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pubs[i], sig))
   120  
   121  			item := c.Items[ctr.ScriptHash()]
   122  			require.NotNil(t, item)
   123  			require.Equal(t, sig, item.GetSignature(pubs[i]))
   124  		}
   125  
   126  		t.Run("GetWitness", func(t *testing.T) {
   127  			w, err := c.GetWitness(ctr.ScriptHash())
   128  			require.NoError(t, err)
   129  			v := newTestVM(w, tx)
   130  			require.NoError(t, v.Run())
   131  			require.Equal(t, 1, v.Estack().Len())
   132  			require.Equal(t, true, v.Estack().Pop().Value())
   133  		})
   134  		t.Run("GetCompleteTransaction, good", func(t *testing.T) {
   135  			tx, err := c.GetCompleteTransaction()
   136  			require.NoError(t, err)
   137  			require.Equal(t, 1, len(tx.Scripts))
   138  			scripts1 := make([]transaction.Witness, len(tx.Scripts))
   139  			copy(scripts1, tx.Scripts)
   140  			// Doing it twice shouldn't be a problem.
   141  			tx, err = c.GetCompleteTransaction()
   142  			require.NoError(t, err)
   143  			require.Equal(t, scripts1, tx.Scripts)
   144  		})
   145  	}
   146  	t.Run("exact number of sigs", func(t *testing.T) {
   147  		testSigWit(t, 3)
   148  	})
   149  	t.Run("larger number of sigs", func(t *testing.T) {
   150  		// Clean up.
   151  		var itm = c.Items[ctr.ScriptHash()]
   152  		for i := range itm.Parameters {
   153  			itm.Parameters[i].Value = nil
   154  		}
   155  		itm.Signatures = make(map[string][]byte)
   156  		testSigWit(t, 4)
   157  	})
   158  }
   159  
   160  func newTestVM(w *transaction.Witness, tx *transaction.Transaction) *vm.VM {
   161  	ic := &interop.Context{Network: uint32(netmode.UnitTestNet), Container: tx, Functions: crypto.Interops}
   162  	v := ic.SpawnVM()
   163  	v.LoadScript(w.VerificationScript)
   164  	v.LoadScript(w.InvocationScript)
   165  	return v
   166  }
   167  
   168  func TestParameterContext_MarshalJSON(t *testing.T) {
   169  	priv, err := keys.NewPrivateKey()
   170  	require.NoError(t, err)
   171  
   172  	tx := getContractTx(priv.GetScriptHash())
   173  	sign := priv.SignHashable(uint32(netmode.UnitTestNet), tx)
   174  
   175  	expected := &ParameterContext{
   176  		Type:       "Neo.Core.ContractTransaction",
   177  		Network:    netmode.UnitTestNet,
   178  		Verifiable: tx,
   179  		Items: map[util.Uint160]*Item{
   180  			priv.GetScriptHash(): {
   181  				Script: priv.PublicKey().GetVerificationScript(),
   182  				Parameters: []smartcontract.Parameter{{
   183  					Type:  smartcontract.SignatureType,
   184  					Value: sign,
   185  				}},
   186  				Signatures: map[string][]byte{
   187  					priv.PublicKey().StringCompressed(): sign,
   188  				},
   189  			},
   190  		},
   191  	}
   192  
   193  	testserdes.MarshalUnmarshalJSON(t, expected, new(ParameterContext))
   194  
   195  	t.Run("invalid script", func(t *testing.T) {
   196  		js := `{
   197   			"script": "AQID",
   198   			"parameters": [
   199    				{
   200     					"type": "Signature",
   201     					"value": "QfOZLLqjMyPWMzRxMAKw7fcd8leLcpwiiTV2pUyC0pth/y7Iw7o7WzNpxeAJm5bmExmlF7g5pMhXz1xVT6KK3g=="
   202    				}
   203  			],
   204   			"signatures": {
   205  				"025c210bde738e0e646929ee04ec2ccb42a700356083f55386b5347b9b725c10b9": "a6c6d8a2334791888df559419f07209ee39e2f20688af8cc38010854b98abf77194e37f173bbc86b77dce4afa8ce3ae5170dd346b5265bcb9b723d83299a6f0f",
   206    				"035d4da640b3a39f19ed88855aeddd97725422b4230ccae56bd5544419d0056ea9": "058e577f23395f382194eebb83f66bb8903c8f3c5b6afd759c20f2518466124dcd9cbccfc029a42e9a7d5a3a060b091edc73dcac949fd894d7a9d10678296ac6"
   207  		}`
   208  		require.Error(t, json.Unmarshal([]byte(js), new(ParameterContext)))
   209  	})
   210  	t.Run("invalid hash", func(t *testing.T) {
   211  		js := `{
   212  		   "hash" : "0x0142f965b441b9af40a34b5cb24545b807c3ca24149201151fd93b204ea60e87",
   213  		   "type" : "Neo.Core.ContractTransaction",
   214  		   "items" : {
   215  			  "0x60bd43f6e14dc19789296143b615e75cb73e19cc" : {
   216  				 "parameters" : [
   217  					{
   218  					   "value" : "I4H7NpMj3xWczNNa31uZZDL7VvYNXrLHK6n2ARFCVVz/zW6ojrTtxgYpeFTMXfNwp+LULWjvJLQCxA6sky0yzQ==",
   219  					   "type" : "Signature"
   220  					}
   221  				 ],
   222  				 "signatures" : {
   223  					"0268f0425415a67623e1e48ab3c3bd6275319c75e44358e4ec15abc6e50213b033" : "I4H7NpMj3xWczNNa31uZZDL7VvYNXrLHK6n2ARFCVVz/zW6ojrTtxgYpeFTMXfNwp+LULWjvJLQCxA6sky0yzQ=="
   224  				 },
   225  				 "script" : "DCECaPBCVBWmdiPh5Iqzw71idTGcdeRDWOTsFavG5QITsDNBVuezJw=="
   226  			  }
   227  		   },
   228  		   "network" : 42,
   229  		   "data" : "AMYrW54AAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgMAAAAAAAAAAAAAAAAAAAAAAAAAARE="
   230  }
   231  `
   232  		require.Error(t, json.Unmarshal([]byte(js), new(ParameterContext)))
   233  	})
   234  }
   235  
   236  func TestSharpJSON(t *testing.T) {
   237  	input := []byte(`{"type":"Neo.Network.P2P.Payloads.Transaction","hash":"0x71b519998f41bbc1d37e383e01e2e6efe84d65abf3c7279820cc7c63daa29448","data":"AKTv6hJY8h4AAAAAAKwiUwEAAAAA0lEAAAFBO\u002BhSRSuucNKVX2lk7k5Wdr\u002BkOQEAMR8RwB8MEHNldEV4ZWNGZWVGYWN0b3IMFHvGgcCh9x1UNFe2i7qNX5/dTl7MQWJ9W1I=","items":{"0x39a4bf76564eee64695f95d270ae2b4552e83b41":{"script":"GwwhAwCbdUDhDyVi5f2PrJ6uwlFmpYsm5BI0j/WoaSe/rCKiDCEDAgXpzvrqWh38WAryDI1aokaLsBSPGl5GBfxiLIDmBLoMIQIUuvDO6jpm8X5\u002BHoOeol/YvtbNgua7bmglAYkGX0T/AQwhAzjSoai75eQ8YzNBYTMIaaXgqqUeYTSWGEp8xylL\u002BVafDCEDPY41\u002BM2aM4UigLbZMJPHKS7VzpDZDxSfotpQumFo384MIQI\u002BmzLqiblNBm5kmxJP1Q45bukTaejipq4bEcFw0CIlbQwhA0CNzUFjlvZHg6xYfqHhWTxX2f6ogMimoZIOkqJZR3gGDCEDScfvC0qvGB8KPhNQxSexNsxbQkmMuDq4iAwF7ZUWfhwMIQJWZM7wq8uneHrV\u002BxLzrzHFzcekeQaKoq2O54gEdov/6QwhA1tPm\u002BK4U\u002BButaCcFn4Di5a0gEI1lhUQQjJS8u49u6WDDCEDZQpoRGGmS/Rr7lYdmYGkxXrcbMvTqVErg3AUgLMCGKsMIQJqEKorTXY5xd6vpP8IFGfbELXQBDJ0mipe4dK/7SPhwAwhAn5FmyZLb34yWrSwuw\u002BmQQgftoUX/WE\u002BvXqUy3nTCB5PDCECiMrUQqh3lgx2tPaI9L4w92glbZo9okkrAYC5EkORi08MIQKkDFUnmPeWNglYF\u002ByIkk/Gy3CU5aPLBZqbO8keo78NPQwhAqeDS\u002BmzLimB0VfLW706y0LP0R6lw7ECJNekTpjFkQ8bDCECuixw9ZlvNXpDGYcFhZ\u002BuLP6hPhFyligAdys9WIqdSr0MIQLVeGqSFKij8XV9dZb9EPUkEgXiwNaDYvR2ZXm6xhiSSQwhA9jVjSJXymyxRSK3ZRPUeD99SBgBaViTeUwhhlFcbedvDCEC23nmnFGK6SVOMUtvX0tj6RTN1LJXTcL5I2wBwfwdiXMMIQLsFD8AuIUkyvNqASHC3gnu8FGd2\u002BHHEKAPDiZjIB7kwAAVQZ7Q3Do=","parameters":[{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"},{"type":"Signature"}],"signatures":{"03650a684461a64bf46bee561d9981a4c57adc6ccbd3a9512b83701480b30218ab":"QtjYFNpGOOnij\u002BLwNZLOO3fHNoVQas\u002B4\u002BAo6SdvEeP3C12ATXzgPjAZrd5mCDc3KYkce0wwveEuuoYA8mhraUA==","0288cad442a877960c76b4f688f4be30f768256d9a3da2492b0180b91243918b4f":"RmuTXfPokXWEL9RIM9DqUUsOH8iRMfrKTp6LdhdJ0KBW6rNSEuxxNOpSUMBEW1EE2CNh1c\u002BmElj2Ny3o89SzGQ==","035b4f9be2b853e06eb5a09c167e038b96b4804235961510423252f2ee3dbba583":"1VYiT\u002BPe/7syYDSOWaJ1jPyZ6JDPrdU9toDu0Cg9pRQAJW1KLSexiosLA73k7lQeVbq4YuNlWnY7U8CYIQ/ilA==","02a40c552798f79636095817ec88924fc6cb7094e5a3cb059a9b3bc91ea3bf0d3d":"/mXUPXp/tI6Y7LhudKzBE8K2soHcPgrr48YLrwgbTI4qypYpOzh\u002BNj03pkAvk8\u002B68kuefevNQb/pjmPRvs80DA=="}}},"network":877933390}`)
   238  	pc := ParameterContext{}
   239  	require.NoError(t, json.Unmarshal(input, &pc))
   240  }
   241  
   242  func getPrivateKeys(t *testing.T, n int) ([]*keys.PrivateKey, []*keys.PublicKey) {
   243  	privs := make([]*keys.PrivateKey, n)
   244  	pubs := make([]*keys.PublicKey, n)
   245  	for i := range privs {
   246  		var err error
   247  		privs[i], err = keys.NewPrivateKey()
   248  		require.NoError(t, err)
   249  		pubs[i] = privs[i].PublicKey()
   250  	}
   251  	return privs, pubs
   252  }
   253  
   254  func newParam(typ smartcontract.ParamType, name string) wallet.ContractParam {
   255  	return wallet.ContractParam{
   256  		Name: name,
   257  		Type: typ,
   258  	}
   259  }
   260  
   261  func getContractTx(signer util.Uint160) *transaction.Transaction {
   262  	tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   263  	tx.Attributes = make([]transaction.Attribute, 0)
   264  	tx.Scripts = make([]transaction.Witness, 0)
   265  	tx.Signers = []transaction.Signer{{Account: signer}}
   266  	tx.Hash()
   267  	return tx
   268  }