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 }