github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/invocation_test.go (about) 1 package native_test 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/nspcc-dev/neo-go/internal/contracts" 11 "github.com/nspcc-dev/neo-go/pkg/config" 12 "github.com/nspcc-dev/neo-go/pkg/core/fee" 13 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 14 "github.com/nspcc-dev/neo-go/pkg/core/state" 15 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 16 "github.com/nspcc-dev/neo-go/pkg/neotest" 17 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 18 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 19 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 20 "github.com/nspcc-dev/neo-go/pkg/util" 21 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 22 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 23 "github.com/stretchr/testify/require" 24 ) 25 26 var pathToInternalContracts = filepath.Join("..", "..", "..", "internal", "contracts") 27 28 func TestNativeContract_Invoke(t *testing.T) { 29 const ( 30 transferCPUFee = 1 << 17 31 transferStorageFee = 50 32 systemContractCallPrice = 1 << 15 33 ) 34 bc, validator, committee := chain.NewMulti(t) 35 e := neotest.NewExecutor(t, bc, validator, committee) 36 gasHash := e.NativeHash(t, nativenames.Gas) 37 38 baseExecFee := bc.GetBaseExecFee() 39 price := fee.Opcode(baseExecFee, opcode.SYSCALL, // System.Contract.Call 40 opcode.PUSHDATA1, // contract hash (20 byte) 41 opcode.PUSHDATA1, // method 42 opcode.PUSH15, // call flags 43 // `transfer` args: 44 opcode.PUSHDATA1, // from 45 opcode.PUSHDATA1, // to 46 opcode.PUSH1, // amount 47 opcode.PUSHNULL, // data 48 // end args 49 opcode.PUSH4, // amount of args 50 opcode.PACK, // pack args 51 ) 52 price += systemContractCallPrice*baseExecFee + // System.Contract.Call price 53 transferCPUFee*baseExecFee + // `transfer` itself 54 transferStorageFee*bc.GetStoragePrice() // `transfer` storage price 55 56 tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil) 57 e.SignTx(t, tx, -1, validator) 58 e.AddNewBlock(t, tx) 59 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) 60 61 // Enough for Call and other opcodes, but not enough for "transfer" call. 62 tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil) 63 e.SignTx(t, tx, price-1, validator) 64 e.AddNewBlock(t, tx) 65 e.CheckFault(t, tx.Hash(), "gas limit exceeded") 66 } 67 68 func TestNativeContract_InvokeInternal(t *testing.T) { 69 bc, validator, committee := chain.NewMulti(t) 70 e := neotest.NewExecutor(t, bc, validator, committee) 71 clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib)) 72 require.NotNil(t, clState) 73 md := clState.Manifest.ABI.GetMethod("ripemd160", 1) 74 require.NotNil(t, md) 75 76 t.Run("fail, bad current script hash", func(t *testing.T) { 77 ic, err := bc.GetTestVM(trigger.Application, nil, nil) 78 require.NoError(t, err) 79 v := ic.SpawnVM() 80 fakeH := util.Uint160{1, 2, 3} 81 v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All) 82 input := []byte{1, 2, 3, 4} 83 v.Estack().PushVal(input) 84 v.Context().Jump(md.Offset) 85 86 // Bad current script hash 87 err = v.Run() 88 require.Error(t, err) 89 require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error()) 90 }) 91 92 /* 93 t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) { 94 bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 95 c.NativeUpdateHistories = map[string][]uint32{ 96 nativenames.Policy: {0}, 97 nativenames.Neo: {0}, 98 nativenames.Gas: {0}, 99 nativenames.Designation: {0}, 100 nativenames.StdLib: {0}, 101 nativenames.Management: {0}, 102 nativenames.Oracle: {0}, 103 nativenames.Ledger: {0}, 104 nativenames.CryptoLib: {1}, 105 } 106 }) 107 eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 108 109 ic, err := bcBad.GetTestVM(trigger.Application, nil, nil) 110 require.NoError(t, err) 111 v := ic.SpawnVM() 112 v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history 113 input := []byte{1, 2, 3, 4} 114 v.Estack().PushVal(input) 115 v.Context().Jump(md.Offset) 116 117 // It's prohibited to call natives before NativeUpdateHistory[0] height. 118 err = v.Run() 119 require.Error(t, err) 120 require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1")) 121 122 // Add new block => CryptoLib should be active now. 123 eBad.AddNewBlock(t) 124 ic, err = bcBad.GetTestVM(trigger.Application, nil, nil) 125 require.NoError(t, err) 126 v = ic.SpawnVM() 127 v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history 128 v.Estack().PushVal(input) 129 v.Context().Jump(md.Offset) 130 131 require.NoError(t, v.Run()) 132 value := v.Estack().Pop().Bytes() 133 require.Equal(t, hash.RipeMD160(input).BytesBE(), value) 134 }) 135 */ 136 137 manState := bc.GetContractState(e.NativeHash(t, nativenames.Management)) 138 require.NotNil(t, manState) 139 mdDeploy := manState.Manifest.ABI.GetMethod("deploy", 2) 140 require.NotNil(t, mdDeploy) 141 t.Run("fail, bad call flag", func(t *testing.T) { 142 ic, err := bc.GetTestVM(trigger.Application, nil, nil) 143 require.NoError(t, err) 144 v := ic.SpawnVM() 145 v.LoadScriptWithHash(manState.NEF.Script, manState.Hash, callflag.States|callflag.AllowNotify) 146 input := []byte{1, 2, 3, 4} 147 v.Estack().PushVal(input) 148 v.Estack().PushVal(input) 149 v.Context().Jump(mdDeploy.Offset) 150 151 // Can't call with these flags, Aspidochelone is active. 152 err = v.Run() 153 require.Error(t, err) 154 require.True(t, strings.Contains(err.Error(), "missing call flags for native 0 `deploy` operation call")) 155 }) 156 157 t.Run("good, pre-aspidochelone deploy", func(t *testing.T) { 158 bc, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 159 c.Hardforks = map[string]uint32{ 160 config.HFAspidochelone.String(): 100500, 161 } 162 }) 163 164 ic, err := bc.GetTestVM(trigger.Application, nil, nil) 165 require.NoError(t, err) 166 v := ic.SpawnVM() 167 v.LoadScriptWithHash(manState.NEF.Script, manState.Hash, callflag.States|callflag.AllowNotify) 168 input := []byte{1, 2, 3, 4} 169 v.Estack().PushVal(input) 170 v.Estack().PushVal(input) 171 v.Context().Jump(mdDeploy.Offset) 172 173 // We have an invalid input, but call flags are OK. 174 err = v.Run() 175 require.Error(t, err) 176 require.True(t, strings.Contains(err.Error(), "invalid NEF file")) 177 }) 178 179 t.Run("success", func(t *testing.T) { 180 ic, err := bc.GetTestVM(trigger.Application, nil, nil) 181 require.NoError(t, err) 182 v := ic.SpawnVM() 183 v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) 184 input := []byte{1, 2, 3, 4} 185 v.Estack().PushVal(input) 186 v.Context().Jump(md.Offset) 187 188 require.NoError(t, v.Run()) 189 190 value := v.Estack().Pop().Bytes() 191 require.Equal(t, hash.RipeMD160(input).BytesBE(), value) 192 }) 193 } 194 195 func TestNativeContract_InvokeOtherContract(t *testing.T) { 196 bc, validator, committee := chain.NewMulti(t) 197 e := neotest.NewExecutor(t, bc, validator, committee) 198 managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management)) 199 gasInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)) 200 201 cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash()) 202 cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash 203 manifB, err := json.Marshal(cs.Manifest) 204 require.NoError(t, err) 205 nefB, err := cs.NEF.Bytes() 206 require.NoError(t, err) 207 si, err := cs.ToStackItem() 208 require.NoError(t, err) 209 managementInvoker.Invoke(t, si, "deploy", nefB, manifB) 210 211 t.Run("non-native, no return", func(t *testing.T) { 212 // `onNEP17Payment` will be invoked on test contract from GAS contract. 213 gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil) 214 }) 215 }