mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 03:41:45 +00:00
7589733017
And include some node-specific configurations there with backwards compatibility. Note that in the future we'll remove Ledger's fields from the ProtocolConfiguration and it'll be possible to access them in Blockchain directly (not via .Ledger). The other option tried was using two configuration types separately, but that incurs more changes to the codebase, single structure that behaves almost like the old one is better for backwards compatibility. Fixes #2676.
171 lines
6.1 KiB
Go
171 lines
6.1 KiB
Go
package native_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var pathToInternalContracts = filepath.Join("..", "..", "..", "internal", "contracts")
|
|
|
|
func TestNativeContract_Invoke(t *testing.T) {
|
|
const (
|
|
transferCPUFee = 1 << 17
|
|
transferStorageFee = 50
|
|
systemContractCallPrice = 1 << 15
|
|
)
|
|
bc, validator, committee := chain.NewMulti(t)
|
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
|
gasHash := e.NativeHash(t, nativenames.Gas)
|
|
|
|
baseExecFee := bc.GetBaseExecFee()
|
|
price := fee.Opcode(baseExecFee, opcode.SYSCALL, // System.Contract.Call
|
|
opcode.PUSHDATA1, // contract hash (20 byte)
|
|
opcode.PUSHDATA1, // method
|
|
opcode.PUSH15, // call flags
|
|
// `transfer` args:
|
|
opcode.PUSHDATA1, // from
|
|
opcode.PUSHDATA1, // to
|
|
opcode.PUSH1, // amount
|
|
opcode.PUSHNULL, // data
|
|
// end args
|
|
opcode.PUSH4, // amount of args
|
|
opcode.PACK, // pack args
|
|
)
|
|
price += systemContractCallPrice*baseExecFee + // System.Contract.Call price
|
|
transferCPUFee*baseExecFee + // `transfer` itself
|
|
transferStorageFee*bc.GetStoragePrice() // `transfer` storage price
|
|
|
|
tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
|
e.SignTx(t, tx, -1, validator)
|
|
e.AddNewBlock(t, tx)
|
|
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
|
|
|
|
// Enough for Call and other opcodes, but not enough for "transfer" call.
|
|
tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
|
e.SignTx(t, tx, price-1, validator)
|
|
e.AddNewBlock(t, tx)
|
|
e.CheckFault(t, tx.Hash(), "gas limit exceeded")
|
|
}
|
|
|
|
func TestNativeContract_InvokeInternal(t *testing.T) {
|
|
bc, validator, committee := chain.NewMulti(t)
|
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
|
clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib))
|
|
require.NotNil(t, clState)
|
|
md := clState.Manifest.ABI.GetMethod("ripemd160", 1)
|
|
require.NotNil(t, md)
|
|
|
|
t.Run("fail, bad current script hash", func(t *testing.T) {
|
|
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
|
|
require.NoError(t, err)
|
|
v := ic.SpawnVM()
|
|
fakeH := util.Uint160{1, 2, 3}
|
|
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
|
|
input := []byte{1, 2, 3, 4}
|
|
v.Estack().PushVal(input)
|
|
v.Context().Jump(md.Offset)
|
|
|
|
// Bad current script hash
|
|
err = v.Run()
|
|
require.Error(t, err)
|
|
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
|
|
})
|
|
|
|
t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) {
|
|
bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
|
c.NativeUpdateHistories = map[string][]uint32{
|
|
nativenames.Policy: {0},
|
|
nativenames.Neo: {0},
|
|
nativenames.Gas: {0},
|
|
nativenames.Designation: {0},
|
|
nativenames.StdLib: {0},
|
|
nativenames.Management: {0},
|
|
nativenames.Oracle: {0},
|
|
nativenames.Ledger: {0},
|
|
nativenames.CryptoLib: {1},
|
|
}
|
|
})
|
|
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
|
|
|
|
ic, err := bcBad.GetTestVM(trigger.Application, nil, nil)
|
|
require.NoError(t, err)
|
|
v := ic.SpawnVM()
|
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
|
input := []byte{1, 2, 3, 4}
|
|
v.Estack().PushVal(input)
|
|
v.Context().Jump(md.Offset)
|
|
|
|
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
|
err = v.Run()
|
|
require.Error(t, err)
|
|
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
|
|
|
// Add new block => CryptoLib should be active now.
|
|
eBad.AddNewBlock(t)
|
|
ic, err = bcBad.GetTestVM(trigger.Application, nil, nil)
|
|
require.NoError(t, err)
|
|
v = ic.SpawnVM()
|
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
|
v.Estack().PushVal(input)
|
|
v.Context().Jump(md.Offset)
|
|
|
|
require.NoError(t, v.Run())
|
|
value := v.Estack().Pop().Bytes()
|
|
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
|
})
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
|
|
require.NoError(t, err)
|
|
v := ic.SpawnVM()
|
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
|
|
input := []byte{1, 2, 3, 4}
|
|
v.Estack().PushVal(input)
|
|
v.Context().Jump(md.Offset)
|
|
|
|
require.NoError(t, v.Run())
|
|
|
|
value := v.Estack().Pop().Bytes()
|
|
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
|
})
|
|
}
|
|
|
|
func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
|
bc, validator, committee := chain.NewMulti(t)
|
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
|
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
|
gasInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
|
|
|
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash())
|
|
cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
|
manifB, err := json.Marshal(cs.Manifest)
|
|
require.NoError(t, err)
|
|
nefB, err := cs.NEF.Bytes()
|
|
require.NoError(t, err)
|
|
si, err := cs.ToStackItem()
|
|
require.NoError(t, err)
|
|
managementInvoker.Invoke(t, si, "deploy", nefB, manifB)
|
|
|
|
t.Run("non-native, no return", func(t *testing.T) {
|
|
// `onNEP17Payment` will be invoked on test contract from GAS contract.
|
|
gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil)
|
|
})
|
|
}
|