forked from TrueCloudLab/neoneo-go
nativetest: migrate Management contract tests to neotest
This commit is contained in:
parent
73ecbb2fb3
commit
e0ca05f62c
13 changed files with 731 additions and 661 deletions
|
@ -1116,7 +1116,7 @@ func TestVerifyTx(t *testing.T) {
|
|||
func TestVerifyHashAgainstScript(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, csInvalid := getTestContractState(bc)
|
||||
cs, csInvalid := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid))
|
||||
|
@ -1681,7 +1681,7 @@ func TestRemoveUntraceable(t *testing.T) {
|
|||
func TestInvalidNotification(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "invalidStack")
|
||||
|
@ -1695,7 +1695,7 @@ func TestInvalidNotification(t *testing.T) {
|
|||
func TestMPTDeleteNoKey(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "delValue", "non-existent-key")
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -674,12 +674,6 @@ func signTxWithAccounts(chain *Blockchain, sysFee int64, tx *transaction.Transac
|
|||
}
|
||||
}
|
||||
|
||||
func prepareContractMethodInvoke(chain *Blockchain, sysfee int64,
|
||||
hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) {
|
||||
return prepareContractMethodInvokeGeneric(chain, sysfee, hash,
|
||||
method, false, args...)
|
||||
}
|
||||
|
||||
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
|
||||
b := chain.newBlock(txs...)
|
||||
err := chain.AddBlock(b)
|
||||
|
@ -716,19 +710,6 @@ func invokeContractMethodGeneric(chain *Blockchain, sysfee int64, hash util.Uint
|
|||
return aers[0], nil
|
||||
}
|
||||
|
||||
func invokeContractMethodBy(t *testing.T, chain *Blockchain, signer *wallet.Account, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
||||
var (
|
||||
netfee int64 = 1000_0000
|
||||
sysfee int64 = 1_0000_0000
|
||||
)
|
||||
transferTx := transferTokenFromMultisigAccount(t, chain, signer.PrivateKey().PublicKey().GetScriptHash(), chain.contracts.GAS.Hash, sysfee+netfee+1000_0000, nil)
|
||||
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res[0].VMState)
|
||||
require.Equal(t, 0, len(res[0].Stack))
|
||||
return invokeContractMethodGeneric(chain, sysfee, hash, method, signer, args...)
|
||||
}
|
||||
|
||||
func transferTokenFromMultisigAccountCheckOK(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) {
|
||||
transferTx := transferTokenFromMultisigAccount(t, chain, to, tokenHash, amount, additionalArgs...)
|
||||
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
|
@ -36,6 +40,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -231,7 +236,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
|||
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||
v, ic, bc := createVM(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||
|
||||
ic.Invocations[hash.Hash160([]byte{2})] = 42
|
||||
|
@ -658,22 +663,28 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C
|
|||
return v, contractState, context, chain
|
||||
}
|
||||
|
||||
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
||||
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
||||
tx := transaction.New(script, 0)
|
||||
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}}
|
||||
tx.Scripts = []transaction.Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}}
|
||||
chain := newTestChain(t)
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
||||
context := chain.newInteropContext(trigger.Application, d, nil, tx)
|
||||
v := context.SpawnVM()
|
||||
return v, tx, context, chain
|
||||
}
|
||||
var (
|
||||
helper1ContractNEFPath = filepath.Join("test_data", "management_helper", "management_helper1.nef")
|
||||
helper1ContractManifestPath = filepath.Join("test_data", "management_helper", "management_helper1.manifest.json")
|
||||
helper2ContractNEFPath = filepath.Join("test_data", "management_helper", "management_helper2.nef")
|
||||
helper2ContractManifestPath = filepath.Join("test_data", "management_helper", "management_helper2.manifest.json")
|
||||
)
|
||||
|
||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
||||
func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
// TestGenerateManagementHelperContracts generates 2 helper contracts second of which is
|
||||
// allowed to call the first. It uses test chain to define Management and StdLib
|
||||
// native hashes and saves generated NEF and manifest to ../test_data/management_contract folder.
|
||||
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
|
||||
func TestGenerateManagementHelperContracts(t *testing.T) {
|
||||
const saveState = false
|
||||
|
||||
bc := newTestChain(t)
|
||||
mgmtHash := bc.contracts.Management.Hash
|
||||
stdHash := bc.contracts.Std.Hash
|
||||
neoHash := bc.contracts.NEO.Hash
|
||||
singleChainValidator := testchain.PrivateKey(testchain.IDToOrder(0))
|
||||
acc := wallet.NewAccountFromPrivateKey(singleChainValidator)
|
||||
require.NoError(t, acc.ConvertMultisig(1, keys.PublicKeys{singleChainValidator.PublicKey()}))
|
||||
singleChainValidatorHash := acc.Contract.ScriptHash()
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||
|
@ -771,7 +782,6 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
|||
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||
|
||||
script := w.Bytes()
|
||||
h := hash.Hash160(script)
|
||||
m := manifest.NewManifest("TestMain")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
|
@ -953,20 +963,14 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
|||
m.Permissions[1].Contract.Value = util.Uint160{}
|
||||
m.Permissions[1].Methods.Add("method")
|
||||
|
||||
cs := &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
Hash: h,
|
||||
Manifest: *m,
|
||||
ID: 42,
|
||||
},
|
||||
}
|
||||
// Generate NEF file.
|
||||
ne, err := nef.NewFile(script)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ne.Tokens = []nef.MethodToken{
|
||||
{
|
||||
Hash: bc.contracts.NEO.Hash,
|
||||
Hash: neoHash,
|
||||
Method: "balanceOf",
|
||||
ParamCount: 1,
|
||||
HasReturn: true,
|
||||
|
@ -980,7 +984,25 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
|||
},
|
||||
}
|
||||
ne.Checksum = ne.CalculateChecksum()
|
||||
cs.NEF = *ne
|
||||
|
||||
// Write first NEF file.
|
||||
bytes, err := ne.Bytes()
|
||||
require.NoError(t, err)
|
||||
if saveState {
|
||||
err = ioutil.WriteFile(helper1ContractNEFPath, bytes, os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Write first manifest file.
|
||||
mData, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
if saveState {
|
||||
err = ioutil.WriteFile(helper1ContractManifestPath, mData, os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Create hash of the first contract assuming that sender is single-chain validator.
|
||||
h := state.CreateContractHash(singleChainValidatorHash, ne.Checksum, m.Name)
|
||||
|
||||
currScript := []byte{byte(opcode.RET)}
|
||||
m = manifest.NewManifest("TestAux")
|
||||
|
@ -997,14 +1019,74 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
return cs, &state.Contract{
|
||||
// Write second NEF file.
|
||||
bytes, err = ne.Bytes()
|
||||
require.NoError(t, err)
|
||||
if saveState {
|
||||
err = ioutil.WriteFile(helper2ContractNEFPath, bytes, os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Write second manifest file.
|
||||
mData, err = json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
if saveState {
|
||||
err = ioutil.WriteFile(helper2ContractManifestPath, mData, os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.False(t, saveState)
|
||||
}
|
||||
|
||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
||||
func getTestContractState(t *testing.T, id1, id2 int32, sender2 util.Uint160) (*state.Contract, *state.Contract) {
|
||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
||||
|
||||
neBytes, err := ioutil.ReadFile(helper1ContractNEFPath)
|
||||
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
|
||||
ne, err := nef.FileFromBytes(neBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
mBytes, err := ioutil.ReadFile(helper1ContractManifestPath)
|
||||
require.NoError(t, err, fmt.Errorf("manifest1: %w", errNotFound))
|
||||
m := &manifest.Manifest{}
|
||||
err = json.Unmarshal(mBytes, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
cs1 := &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
NEF: *ne,
|
||||
Hash: hash.Hash160(currScript),
|
||||
NEF: ne,
|
||||
Manifest: *m,
|
||||
ID: 123,
|
||||
ID: id1,
|
||||
},
|
||||
}
|
||||
|
||||
neBytes, err = ioutil.ReadFile(helper2ContractNEFPath)
|
||||
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
|
||||
ne, err = nef.FileFromBytes(neBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
mBytes, err = ioutil.ReadFile(helper2ContractManifestPath)
|
||||
require.NoError(t, err, fmt.Errorf("manifest2: %w", errNotFound))
|
||||
m = &manifest.Manifest{}
|
||||
err = json.Unmarshal(mBytes, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve hash of the first contract from the permissions of the second contract.
|
||||
require.Equal(t, 1, len(m.Permissions))
|
||||
require.Equal(t, manifest.PermissionHash, m.Permissions[0].Contract.Type)
|
||||
cs1.Hash = m.Permissions[0].Contract.Hash()
|
||||
|
||||
cs2 := &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
NEF: ne,
|
||||
Manifest: *m,
|
||||
ID: id2,
|
||||
Hash: state.CreateContractHash(sender2, ne.Checksum, m.Name),
|
||||
},
|
||||
}
|
||||
|
||||
return cs1, cs2
|
||||
}
|
||||
|
||||
func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
|
||||
|
@ -1028,12 +1110,12 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui
|
|||
func TestContractCall(t *testing.T) {
|
||||
_, ic, bc := createVM(t)
|
||||
|
||||
cs, currCs := getTestContractState(bc)
|
||||
cs, currCs := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
||||
|
||||
currScript := currCs.NEF.Script
|
||||
h := hash.Hash160(cs.NEF.Script)
|
||||
h := cs.Hash
|
||||
|
||||
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
|
@ -1420,7 +1502,7 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
|||
func TestLoadToken(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
|
@ -1463,7 +1545,7 @@ func TestRuntimeGetNetwork(t *testing.T) {
|
|||
func TestRuntimeBurnGas(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
const sysFee = 2_000000
|
||||
|
|
590
pkg/core/native/native_test/management_test.go
Normal file
590
pkg/core/native/native_test/management_test.go
Normal file
|
@ -0,0 +1,590 @@
|
|||
package native_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||
"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/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"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"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
helper1ContractNEFPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper1.nef")
|
||||
helper1ContractManifestPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper1.manifest.json")
|
||||
helper2ContractNEFPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper2.nef")
|
||||
helper2ContractManifestPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper2.manifest.json")
|
||||
)
|
||||
|
||||
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
||||
return newNativeClient(t, nativenames.Management)
|
||||
}
|
||||
|
||||
func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
||||
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||
}
|
||||
|
||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
||||
func getTestContractState(t *testing.T, id1, id2 int32, sender2 util.Uint160) (*state.Contract, *state.Contract) {
|
||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
||||
|
||||
neBytes, err := ioutil.ReadFile(helper1ContractNEFPath)
|
||||
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
|
||||
ne, err := nef.FileFromBytes(neBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
mBytes, err := ioutil.ReadFile(helper1ContractManifestPath)
|
||||
require.NoError(t, err, fmt.Errorf("manifest1: %w", errNotFound))
|
||||
m := &manifest.Manifest{}
|
||||
err = json.Unmarshal(mBytes, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
cs1 := &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
NEF: ne,
|
||||
Manifest: *m,
|
||||
ID: id1,
|
||||
},
|
||||
}
|
||||
|
||||
neBytes, err = ioutil.ReadFile(helper2ContractNEFPath)
|
||||
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
|
||||
ne, err = nef.FileFromBytes(neBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
mBytes, err = ioutil.ReadFile(helper2ContractManifestPath)
|
||||
require.NoError(t, err, fmt.Errorf("manifest2: %w", errNotFound))
|
||||
m = &manifest.Manifest{}
|
||||
err = json.Unmarshal(mBytes, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve hash of the first contract from the permissions of the second contract.
|
||||
require.Equal(t, 1, len(m.Permissions))
|
||||
require.Equal(t, manifest.PermissionHash, m.Permissions[0].Contract.Type)
|
||||
cs1.Hash = m.Permissions[0].Contract.Hash()
|
||||
|
||||
cs2 := &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
NEF: ne,
|
||||
Manifest: *m,
|
||||
ID: id2,
|
||||
Hash: state.CreateContractHash(sender2, ne.Checksum, m.Name),
|
||||
},
|
||||
}
|
||||
|
||||
return cs1, cs2
|
||||
}
|
||||
|
||||
func TestManagement_ContractDeploy(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.Committee.ScriptHash())
|
||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefBytes, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("no NEF", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "no valid NEF provided", "deploy", nil, manifestBytes)
|
||||
})
|
||||
t.Run("no manifest", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "no valid manifest provided", "deploy", nefBytes, nil)
|
||||
})
|
||||
t.Run("int for NEF", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", int64(1), manifestBytes)
|
||||
})
|
||||
t.Run("zero-length NEF", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", []byte{}, manifestBytes)
|
||||
})
|
||||
t.Run("array for NEF", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", []interface{}{int64(1)}, manifestBytes)
|
||||
})
|
||||
t.Run("bad script in NEF", func(t *testing.T) {
|
||||
nf, err := nef.FileFromBytes(nefBytes) // make a full copy
|
||||
require.NoError(t, err)
|
||||
nf.Script[0] = 0xff
|
||||
nf.CalculateChecksum()
|
||||
nefBad, err := nf.Bytes()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", nefBad, manifestBytes)
|
||||
})
|
||||
t.Run("int for manifest", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, int64(1))
|
||||
})
|
||||
t.Run("zero-length manifest", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, []byte{})
|
||||
})
|
||||
t.Run("array for manifest", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, []interface{}{int64(1)})
|
||||
})
|
||||
t.Run("non-utf8 manifest", func(t *testing.T) {
|
||||
manifestBad := bytes.Replace(manifestBytes, []byte("TestMain"), []byte("\xff\xfe\xfd"), 1) // Replace name.
|
||||
managementInvoker.InvokeFail(t, "manifest is not UTF-8 compliant", "deploy", nefBytes, manifestBad)
|
||||
})
|
||||
t.Run("invalid manifest", func(t *testing.T) {
|
||||
pkey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
badManifest := cs1.Manifest
|
||||
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||
manifB, err := json.Marshal(&badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, manifB)
|
||||
})
|
||||
t.Run("bad methods in manifest 1", func(t *testing.T) {
|
||||
badManifest := cs1.Manifest
|
||||
badManifest.ABI.Methods = make([]manifest.Method, len(cs1.Manifest.ABI.Methods))
|
||||
copy(badManifest.ABI.Methods, cs1.Manifest.ABI.Methods)
|
||||
badManifest.ABI.Methods[0].Offset = 100500 // out of bounds
|
||||
manifB, err := json.Marshal(&badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
managementInvoker.InvokeFail(t, "out of bounds method offset", "deploy", nefBytes, manifB)
|
||||
})
|
||||
|
||||
t.Run("bad methods in manifest 2", func(t *testing.T) {
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.ABI.Methods = make([]manifest.Method, len(cs1.Manifest.ABI.Methods))
|
||||
copy(badManifest.ABI.Methods, cs1.Manifest.ABI.Methods)
|
||||
badManifest.ABI.Methods[0].Offset = len(cs1.NEF.Script) - 2 // Ends with `CALLT(X,X);RET`.
|
||||
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
managementInvoker.InvokeFail(t, "some methods point to wrong offsets (not to instruction boundary)", "deploy", nefBytes, manifB)
|
||||
})
|
||||
|
||||
t.Run("not enough GAS", func(t *testing.T) {
|
||||
tx := managementInvoker.NewUnsignedTx(t, managementInvoker.Hash, "deploy", nefBytes, manifestBytes)
|
||||
managementInvoker.SignTx(t, tx, 1_0000_0000, managementInvoker.Signers...)
|
||||
managementInvoker.AddNewBlock(t, tx)
|
||||
managementInvoker.CheckFault(t, tx.Hash(), "gas limit exceeded")
|
||||
})
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
tx1 := managementInvoker.PrepareInvoke(t, "deploy", nefBytes, manifestBytes)
|
||||
tx2 := managementInvoker.PrepareInvoke(t, "getContract", cs1.Hash.BytesBE())
|
||||
managementInvoker.AddNewBlock(t, tx1, tx2)
|
||||
managementInvoker.CheckHalt(t, tx1.Hash(), si)
|
||||
managementInvoker.CheckHalt(t, tx2.Hash(), si)
|
||||
managementInvoker.CheckTxNotificationEvent(t, tx1.Hash(), 0, state.NotificationEvent{
|
||||
ScriptHash: c.NativeHash(t, nativenames.Management),
|
||||
Name: "Deploy",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
})
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
helperInvoker := c.Executor.CommitteeInvoker(cs1.Hash)
|
||||
expected := stackitem.NewArray([]stackitem.Item{stackitem.Make("create"), stackitem.Null{}})
|
||||
expectedBytes, err := stackitem.Serialize(expected)
|
||||
require.NoError(t, err)
|
||||
helperInvoker.Invoke(t, stackitem.NewByteArray(expectedBytes), "getValue")
|
||||
})
|
||||
t.Run("get after deploy", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
t.Run("get after restore", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
require.NoError(t, chaindump.Dump(c.Executor.Chain, w.BinWriter, 0, c.Executor.Chain.BlockHeight()+1))
|
||||
require.NoError(t, w.Err)
|
||||
|
||||
r := io.NewBinReaderFromBuf(w.Bytes())
|
||||
bc2, acc := chain.NewSingle(t)
|
||||
e2 := neotest.NewExecutor(t, bc2, acc, acc)
|
||||
managementInvoker2 := e2.CommitteeInvoker(e2.NativeHash(t, nativenames.Management))
|
||||
|
||||
require.NoError(t, chaindump.Restore(bc2, r, 0, c.Executor.Chain.BlockHeight()+1, nil))
|
||||
require.NoError(t, r.Err)
|
||||
managementInvoker2.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
})
|
||||
t.Run("contract already exists", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "contract already exists", "deploy", nefBytes, manifestBytes)
|
||||
})
|
||||
t.Run("failed _deploy", func(t *testing.T) {
|
||||
deployScript := []byte{byte(opcode.ABORT)}
|
||||
m := manifest.NewManifest("TestDeployAbort")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
Name: manifest.MethodDeploy,
|
||||
Offset: 0,
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("data", smartcontract.AnyType),
|
||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||
},
|
||||
ReturnType: smartcontract.VoidType,
|
||||
},
|
||||
}
|
||||
nefD, err := nef.NewFile(deployScript)
|
||||
require.NoError(t, err)
|
||||
nefDb, err := nefD.Bytes()
|
||||
require.NoError(t, err)
|
||||
manifD, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
managementInvoker.InvokeFail(t, "ABORT", "deploy", nefDb, manifD)
|
||||
|
||||
t.Run("get after failed deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(c.CommitteeHash, nefD.Checksum, m.Name)
|
||||
managementInvoker.Invoke(t, stackitem.Null{}, "getContract", h.BytesBE())
|
||||
})
|
||||
})
|
||||
t.Run("bad _deploy", func(t *testing.T) { // invalid _deploy signature
|
||||
deployScript := []byte{byte(opcode.RET)}
|
||||
m := manifest.NewManifest("TestBadDeploy")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
Name: manifest.MethodDeploy,
|
||||
Offset: 0,
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("data", smartcontract.AnyType),
|
||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||
},
|
||||
ReturnType: smartcontract.ArrayType,
|
||||
},
|
||||
}
|
||||
nefD, err := nef.NewFile(deployScript)
|
||||
require.NoError(t, err)
|
||||
nefDb, err := nefD.Bytes()
|
||||
require.NoError(t, err)
|
||||
manifD, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
managementInvoker.InvokeFail(t, "invalid return values count: expected 0, got 2", "deploy", nefDb, manifD)
|
||||
|
||||
t.Run("get after bad _deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(c.CommitteeHash, nefD.Checksum, m.Name)
|
||||
managementInvoker.Invoke(t, stackitem.Null{}, "getContract", h.BytesBE())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestManagement_StartFromHeight(t *testing.T) {
|
||||
// Create database to be able to start another chain from the same height later.
|
||||
ldbDir := t.TempDir()
|
||||
dbConfig := storage.DBConfiguration{
|
||||
Type: "leveldb",
|
||||
LevelDBOptions: storage.LevelDBOptions{
|
||||
DataDirectoryPath: ldbDir,
|
||||
},
|
||||
}
|
||||
newLevelStore, err := storage.NewLevelDBStore(dbConfig.LevelDBOptions)
|
||||
require.Nil(t, err, "NewLevelDBStore error")
|
||||
|
||||
// Create blockchain and put contract state to it.
|
||||
bc, acc := chain.NewSingleWithCustomConfigAndStore(t, nil, newLevelStore, false)
|
||||
go bc.Run()
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefBytes, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
|
||||
managementInvoker.Invoke(t, si, "deploy", nefBytes, manifestBytes)
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
|
||||
// Close current blockchain and start the new one from the same height with the same db.
|
||||
bc.Close()
|
||||
newLevelStore, err = storage.NewLevelDBStore(dbConfig.LevelDBOptions)
|
||||
require.NoError(t, err)
|
||||
bc2, acc := chain.NewSingleWithCustomConfigAndStore(t, nil, newLevelStore, true)
|
||||
e2 := neotest.NewExecutor(t, bc2, acc, acc)
|
||||
managementInvoker2 := e2.CommitteeInvoker(e2.NativeHash(t, nativenames.Management))
|
||||
|
||||
// Check that initialisation of native Management was correctly performed.
|
||||
managementInvoker2.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
}
|
||||
|
||||
func TestManagement_DeployManifestOverflow(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := nef1.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Bytes(w.BinWriter, manif1)
|
||||
emit.Int(w.BinWriter, manifest.MaxManifestSize)
|
||||
emit.Opcodes(w.BinWriter, opcode.NEWBUFFER, opcode.CAT)
|
||||
emit.Bytes(w.BinWriter, nef1b)
|
||||
emit.Int(w.BinWriter, 2)
|
||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||
emit.AppCallNoArgs(w.BinWriter, managementInvoker.Hash, "deploy", callflag.All)
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
|
||||
tx := transaction.New(script, 0)
|
||||
tx.ValidUntilBlock = managementInvoker.Chain.BlockHeight() + 1
|
||||
managementInvoker.SignTx(t, tx, 100_0000_0000, managementInvoker.Signers...)
|
||||
managementInvoker.AddNewBlock(t, tx)
|
||||
managementInvoker.CheckFault(t, tx.Hash(), fmt.Sprintf("invalid manifest: len is %d (max %d)", manifest.MaxManifestSize+len(manif1), manifest.MaxManifestSize))
|
||||
}
|
||||
|
||||
func TestManagement_ContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(c.CommitteeHash, cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.Invoke(t, si, "deploy", nef1b, manif1)
|
||||
helperInvoker := c.Executor.CommitteeInvoker(cs1.Hash)
|
||||
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
expected := stackitem.NewArray([]stackitem.Item{stackitem.Make("create"), stackitem.Null{}})
|
||||
expectedBytes, err := stackitem.Serialize(expected)
|
||||
require.NoError(t, err)
|
||||
helperInvoker.Invoke(t, stackitem.NewByteArray(expectedBytes), "getValue")
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nef1b, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
|
||||
helperInvoker.Invoke(t, stackitem.Null{}, "update", nef1b, nil, "new data")
|
||||
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
expected := stackitem.NewArray([]stackitem.Item{stackitem.Make("update"), stackitem.Make("new data")})
|
||||
expectedBytes, err := stackitem.Serialize(expected)
|
||||
require.NoError(t, err)
|
||||
helperInvoker.Invoke(t, stackitem.NewByteArray(expectedBytes), "getValue")
|
||||
})
|
||||
}
|
||||
|
||||
func TestManagement_ContractUpdate(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
// Allow calling management contract.
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefBytes, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.Invoke(t, si, "deploy", nefBytes, manifestBytes)
|
||||
helperInvoker := c.Executor.CommitteeInvoker(cs1.Hash)
|
||||
|
||||
t.Run("unknown contract", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "contract doesn't exist", "update", nefBytes, manifestBytes)
|
||||
})
|
||||
t.Run("zero-length NEF", func(t *testing.T) {
|
||||
helperInvoker.InvokeFail(t, "invalid NEF file: empty", "update", []byte{}, manifestBytes)
|
||||
})
|
||||
t.Run("zero-length manifest", func(t *testing.T) {
|
||||
helperInvoker.InvokeFail(t, "invalid manifest: empty", "update", nefBytes, []byte{})
|
||||
})
|
||||
t.Run("no real params", func(t *testing.T) {
|
||||
helperInvoker.InvokeFail(t, "both NEF and manifest are nil", "update", nil, nil)
|
||||
})
|
||||
t.Run("invalid manifest", func(t *testing.T) {
|
||||
pkey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
helperInvoker.InvokeFail(t, "invalid manifest: incorrect group signature", "update", nefBytes, manifB)
|
||||
})
|
||||
t.Run("manifest and script mismatch", func(t *testing.T) {
|
||||
nf, err := nef.FileFromBytes(nefBytes) // Make a full copy.
|
||||
require.NoError(t, err)
|
||||
nf.Script = append(nf.Script, byte(opcode.RET))
|
||||
copy(nf.Script[1:], nf.Script) // Now all method offsets are wrong.
|
||||
nf.Script[0] = byte(opcode.RET) // Even though the script is correct.
|
||||
nf.CalculateChecksum()
|
||||
nefnew, err := nf.Bytes()
|
||||
require.NoError(t, err)
|
||||
helperInvoker.InvokeFail(t, "invalid NEF file: checksum verification failure", "update", nefnew, manifestBytes)
|
||||
})
|
||||
|
||||
t.Run("change name", func(t *testing.T) {
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.Name += "tail"
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
helperInvoker.InvokeFail(t, "contract name can't be changed", "update", nefBytes, manifB)
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nefBytes, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
si, err = cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("update script, positive", func(t *testing.T) {
|
||||
tx1 := helperInvoker.PrepareInvoke(t, "update", nefBytes, nil)
|
||||
tx2 := managementInvoker.PrepareInvoke(t, "getContract", cs1.Hash.BytesBE())
|
||||
managementInvoker.AddNewBlock(t, tx1, tx2)
|
||||
managementInvoker.CheckHalt(t, tx1.Hash(), stackitem.Null{})
|
||||
managementInvoker.CheckHalt(t, tx2.Hash(), si)
|
||||
managementInvoker.CheckTxNotificationEvent(t, tx1.Hash(), 0, state.NotificationEvent{
|
||||
ScriptHash: c.NativeHash(t, nativenames.Management),
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
})
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
helperInvoker := c.Executor.CommitteeInvoker(cs1.Hash)
|
||||
expected := stackitem.NewArray([]stackitem.Item{stackitem.Make("update"), stackitem.Null{}})
|
||||
expectedBytes, err := stackitem.Serialize(expected)
|
||||
require.NoError(t, err)
|
||||
helperInvoker.Invoke(t, stackitem.NewByteArray(expectedBytes), "getValue")
|
||||
})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
})
|
||||
|
||||
cs1.Manifest.Extra = []byte(`"update me"`)
|
||||
manifestBytes, err = json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
si, err = cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("update manifest, positive", func(t *testing.T) {
|
||||
updHash := helperInvoker.Invoke(t, stackitem.Null{}, "update", nil, manifestBytes)
|
||||
helperInvoker.CheckTxNotificationEvent(t, updHash, 0, state.NotificationEvent{
|
||||
ScriptHash: helperInvoker.NativeHash(t, nativenames.Management),
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.ABORT))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nefBytes, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.Manifest.Extra = []byte(`"update me once more"`)
|
||||
manifestBytes, err = json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
si, err = cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("update both script and manifest", func(t *testing.T) {
|
||||
updHash := helperInvoker.Invoke(t, stackitem.Null{}, "update", nefBytes, manifestBytes)
|
||||
helperInvoker.CheckTxNotificationEvent(t, updHash, 0, state.NotificationEvent{
|
||||
ScriptHash: helperInvoker.NativeHash(t, nativenames.Management),
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestManagement_GetContract(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefBytes, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.Invoke(t, si, "deploy", nefBytes, manifestBytes)
|
||||
|
||||
t.Run("bad parameter type", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "invalid conversion: Array/ByteString", "getContract", []interface{}{int64(1)})
|
||||
})
|
||||
t.Run("not a hash", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "expected byte size of 20 got 3", "getContract", []byte{1, 2, 3})
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
}
|
||||
|
||||
func TestManagement_ContractDestroy(t *testing.T) {
|
||||
c := newManagementClient(t)
|
||||
managementInvoker := c.WithSigners(c.Committee)
|
||||
|
||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
||||
// Allow calling management contract.
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefBytes, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
si, err := cs1.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.Invoke(t, si, "deploy", nefBytes, manifestBytes)
|
||||
helperInvoker := c.Executor.CommitteeInvoker(cs1.Hash)
|
||||
|
||||
t.Run("no contract", func(t *testing.T) {
|
||||
managementInvoker.InvokeFail(t, "key not found", "destroy")
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
dstrHash := helperInvoker.Invoke(t, stackitem.Null{}, "destroy")
|
||||
helperInvoker.CheckTxNotificationEvent(t, dstrHash, 0, state.NotificationEvent{
|
||||
ScriptHash: helperInvoker.NativeHash(t, nativenames.Management),
|
||||
Name: "Destroy",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
managementInvoker.Invoke(t, stackitem.Null{}, "getContract", cs1.Hash.BytesBE())
|
||||
})
|
||||
})
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
|
@ -301,7 +302,7 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
cs, _ := getTestContractState(chain)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, cs))
|
||||
|
||||
baseFee := chain.GetBaseExecFee()
|
||||
|
|
|
@ -1,617 +1,19 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/stateroot"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeployManifestOverflow(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
// nef.NewFile() cares about version a lot.
|
||||
config.Version = "0.90.0-test"
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := nef1.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Bytes(w.BinWriter, manif1)
|
||||
emit.Int(w.BinWriter, manifest.MaxManifestSize)
|
||||
emit.Opcodes(w.BinWriter, opcode.NEWBUFFER, opcode.CAT)
|
||||
emit.Bytes(w.BinWriter, nef1b)
|
||||
emit.Int(w.BinWriter, 2)
|
||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||
emit.AppCallNoArgs(w.BinWriter, mgmtHash, "deploy", callflag.All)
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
|
||||
tx := transaction.New(script, 0)
|
||||
tx.ValidUntilBlock = bc.blockHeight + 1
|
||||
addSigners(neoOwner, tx)
|
||||
setTxSystemFee(bc, 100_00000000, tx)
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
|
||||
aers, err := persistBlock(bc, tx)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, aers[0])
|
||||
}
|
||||
|
||||
type memoryStore struct {
|
||||
*storage.MemoryStore
|
||||
}
|
||||
|
||||
func (memoryStore) Close() error { return nil }
|
||||
|
||||
func TestStartFromHeight(t *testing.T) {
|
||||
st := memoryStore{storage.NewMemoryStore()}
|
||||
bc := newTestChainWithCustomCfgAndStore(t, st, nil)
|
||||
cs1, _ := getTestContractState(bc)
|
||||
func() {
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs1))
|
||||
checkContractState(t, bc, cs1.Hash, cs1)
|
||||
_, err := bc.dao.Store.Persist()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
bc2 := newTestChainWithCustomCfgAndStore(t, st, nil)
|
||||
checkContractState(t, bc2, cs1.Hash, cs1)
|
||||
}
|
||||
|
||||
func TestContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
// nef.NewFile() cares about version a lot.
|
||||
config.Version = "0.90.0-test"
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
aer, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1, int64(42))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, aer.VMState)
|
||||
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||
require.NoError(t, err)
|
||||
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Make(42)}
|
||||
require.Equal(t, stackitem.NewArray(expected), item)
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nef1b, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
|
||||
aer, err = invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, nil, "new data")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, aer.VMState)
|
||||
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||
require.NoError(t, err)
|
||||
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Make("new data")}
|
||||
require.Equal(t, stackitem.NewArray(expected), item)
|
||||
})
|
||||
}
|
||||
|
||||
func TestContractDeploy(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
// nef.NewFile() cares about version a lot.
|
||||
config.Version = "0.90.0-test"
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("no NEF", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nil, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("no manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, nil)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("int for NEF", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", int64(1), manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("zero-length NEF", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", []byte{}, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("array for NEF", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", []interface{}{int64(1)}, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("bad script in NEF", func(t *testing.T) {
|
||||
nf, err := nef.FileFromBytes(nef1b) // make a full copy
|
||||
require.NoError(t, err)
|
||||
nf.Script[0] = 0xff
|
||||
nf.CalculateChecksum()
|
||||
nefbad, err := nf.Bytes()
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nefbad, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("int for manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, int64(1))
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("zero-length manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, []byte{})
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("array for manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, []interface{}{int64(1)})
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("non-utf8 manifest", func(t *testing.T) {
|
||||
manifB := bytes.Replace(manif1, []byte("TestMain"), []byte("\xff\xfe\xfd"), 1) // Replace name.
|
||||
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("invalid manifest", func(t *testing.T) {
|
||||
pkey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("bad methods in manifest 1", func(t *testing.T) {
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.ABI.Methods = make([]manifest.Method, len(cs1.Manifest.ABI.Methods))
|
||||
copy(badManifest.ABI.Methods, cs1.Manifest.ABI.Methods)
|
||||
badManifest.ABI.Methods[0].Offset = 100500 // out of bounds
|
||||
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
|
||||
t.Run("bad methods in manifest 2", func(t *testing.T) {
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.ABI.Methods = make([]manifest.Method, len(cs1.Manifest.ABI.Methods))
|
||||
copy(badManifest.ABI.Methods, cs1.Manifest.ABI.Methods)
|
||||
badManifest.ABI.Methods[0].Offset = len(cs1.NEF.Script) - 2 // Ends with `CALLT(X,X);RET`.
|
||||
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
|
||||
t.Run("not enough GAS", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
tx1, err := prepareContractMethodInvoke(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
tx2, err := prepareContractMethodInvoke(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||
require.NoError(t, err)
|
||||
|
||||
aers, err := persistBlock(bc, tx1, tx2)
|
||||
require.NoError(t, err)
|
||||
for _, res := range aers {
|
||||
require.Equal(t, vm.HaltState, res.VMState)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
compareContractStates(t, cs1, res.Stack[0])
|
||||
}
|
||||
require.Equal(t, aers[0].Events, []state.NotificationEvent{{
|
||||
ScriptHash: mgmtHash,
|
||||
Name: "Deploy",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
}})
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||
require.NoError(t, err)
|
||||
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Null{}}
|
||||
require.Equal(t, stackitem.NewArray(expected), item)
|
||||
})
|
||||
t.Run("get after deploy", func(t *testing.T) {
|
||||
checkContractState(t, bc, cs1.Hash, cs1)
|
||||
})
|
||||
t.Run("get after restore", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
require.NoError(t, chaindump.Dump(bc, w.BinWriter, 0, bc.BlockHeight()+1))
|
||||
require.NoError(t, w.Err)
|
||||
|
||||
r := io.NewBinReaderFromBuf(w.Bytes())
|
||||
bc2 := newTestChain(t)
|
||||
|
||||
require.NoError(t, chaindump.Restore(bc2, r, 0, bc.BlockHeight()+1, nil))
|
||||
require.NoError(t, r.Err)
|
||||
checkContractState(t, bc2, cs1.Hash, cs1)
|
||||
})
|
||||
})
|
||||
t.Run("contract already exists", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("failed _deploy", func(t *testing.T) {
|
||||
deployScript := []byte{byte(opcode.ABORT)}
|
||||
m := manifest.NewManifest("TestDeployAbort")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
Name: manifest.MethodDeploy,
|
||||
Offset: 0,
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("data", smartcontract.AnyType),
|
||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||
},
|
||||
ReturnType: smartcontract.VoidType,
|
||||
},
|
||||
}
|
||||
nefD, err := nef.NewFile(deployScript)
|
||||
require.NoError(t, err)
|
||||
nefDb, err := nefD.Bytes()
|
||||
require.NoError(t, err)
|
||||
manifD, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nefDb, manifD)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
|
||||
t.Run("get after failed deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(neoOwner, nefD.Checksum, m.Name)
|
||||
checkContractState(t, bc, h, nil)
|
||||
})
|
||||
})
|
||||
t.Run("bad _deploy", func(t *testing.T) { // invalid _deploy signature
|
||||
deployScript := []byte{byte(opcode.RET)}
|
||||
m := manifest.NewManifest("TestBadDeploy")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
Name: manifest.MethodDeploy,
|
||||
Offset: 0,
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("data", smartcontract.AnyType),
|
||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||
},
|
||||
ReturnType: smartcontract.ArrayType,
|
||||
},
|
||||
}
|
||||
nefD, err := nef.NewFile(deployScript)
|
||||
require.NoError(t, err)
|
||||
nefDb, err := nefD.Bytes()
|
||||
require.NoError(t, err)
|
||||
manifD, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nefDb, manifD)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
|
||||
t.Run("get after bad _deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(neoOwner, nefD.Checksum, m.Name)
|
||||
checkContractState(t, bc, h, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func checkContractState(t *testing.T, bc *Blockchain, h util.Uint160, cs *state.Contract) {
|
||||
mgmtHash := bc.contracts.Management.Hash
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", h.BytesBE())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res.VMState)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
if cs == nil {
|
||||
require.Equal(t, stackitem.Null{}, res.Stack[0])
|
||||
} else {
|
||||
compareContractStates(t, cs, res.Stack[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestContractUpdate(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
// nef.NewFile() cares about version a lot.
|
||||
config.Version = "0.90.0-test"
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
// Allow calling management contract.
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||
require.NoError(t, err)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := nef1.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("no contract", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "update", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("zero-length NEF", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", []byte{}, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("zero-length manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, []byte{})
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("not enough GAS", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "update", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("no real params", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, nil)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("invalid manifest", func(t *testing.T) {
|
||||
pkey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("manifest and script mismatch", func(t *testing.T) {
|
||||
nf, err := nef.FileFromBytes(nef1b) // Make a full copy.
|
||||
require.NoError(t, err)
|
||||
nf.Script = append(nf.Script, byte(opcode.RET))
|
||||
copy(nf.Script[1:], nf.Script) // Now all method offsets are wrong.
|
||||
nf.Script[0] = byte(opcode.RET) // Even though the script is correct.
|
||||
nf.CalculateChecksum()
|
||||
nefnew, err := nf.Bytes()
|
||||
require.NoError(t, err)
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nefnew, manif1)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
|
||||
t.Run("change name", func(t *testing.T) {
|
||||
var badManifest = cs1.Manifest
|
||||
badManifest.Name += "tail"
|
||||
manifB, err := json.Marshal(badManifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manifB)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nef1b, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
|
||||
t.Run("update script, positive", func(t *testing.T) {
|
||||
tx1, err := prepareContractMethodInvoke(bc, 10_00000000, cs1.Hash, "update", nef1b, nil)
|
||||
require.NoError(t, err)
|
||||
tx2, err := prepareContractMethodInvoke(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||
require.NoError(t, err)
|
||||
|
||||
aers, err := persistBlock(bc, tx1, tx2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, aers[0].VMState)
|
||||
require.Equal(t, vm.HaltState, aers[1].VMState)
|
||||
require.Equal(t, 1, len(aers[1].Stack))
|
||||
compareContractStates(t, cs1, aers[1].Stack[0])
|
||||
require.Equal(t, aers[0].Events, []state.NotificationEvent{{
|
||||
ScriptHash: mgmtHash,
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
}})
|
||||
t.Run("_deploy called", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||
require.NoError(t, err)
|
||||
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Null{}}
|
||||
require.Equal(t, stackitem.NewArray(expected), item)
|
||||
})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
checkContractState(t, bc, cs1.Hash, cs1)
|
||||
})
|
||||
})
|
||||
|
||||
cs1.Manifest.Extra = []byte(`"update me"`)
|
||||
manif1, err = json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
|
||||
t.Run("update manifest, positive", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, manif1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res.VMState)
|
||||
require.Equal(t, res.Events, []state.NotificationEvent{{
|
||||
ScriptHash: mgmtHash,
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
}})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
checkContractState(t, bc, cs1.Hash, cs1)
|
||||
})
|
||||
})
|
||||
|
||||
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.ABORT))
|
||||
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
|
||||
nef1b, err = cs1.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
cs1.Manifest.Extra = []byte(`"update me once more"`)
|
||||
manif1, err = json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
cs1.UpdateCounter++
|
||||
|
||||
t.Run("update both script and manifest", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manif1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res.VMState)
|
||||
require.Equal(t, res.Events, []state.NotificationEvent{{
|
||||
ScriptHash: mgmtHash,
|
||||
Name: "Update",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
}})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
checkContractState(t, bc, cs1.Hash, cs1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetContract(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("bad parameter type", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", []interface{}{int64(1)})
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("not a hash", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", []byte{1, 2, 3})
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
compareContractStates(t, cs1, res.Stack[0])
|
||||
})
|
||||
}
|
||||
|
||||
func TestContractDestroy(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
// Allow calling management contract.
|
||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||
require.NoError(t, err)
|
||||
err = bc.dao.PutStorageItem(cs1.ID, []byte{1, 2, 3}, state.StorageItem{3, 2, 1})
|
||||
require.NoError(t, err)
|
||||
b := bc.dao.GetMPTBatch()
|
||||
_, _, err = bc.GetStateModule().(*stateroot.Module).AddMPTBatch(bc.BlockHeight(), b, bc.dao.Store)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("no contract", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "destroy")
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "destroy")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res.VMState)
|
||||
require.Equal(t, res.Events, []state.NotificationEvent{{
|
||||
ScriptHash: mgmtHash,
|
||||
Name: "Destroy",
|
||||
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
|
||||
}})
|
||||
t.Run("check contract", func(t *testing.T) {
|
||||
checkContractState(t, bc, cs1.Hash, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func compareContractStates(t *testing.T, expected *state.Contract, actual stackitem.Item) {
|
||||
act, ok := actual.Value().([]stackitem.Item)
|
||||
require.True(t, ok)
|
||||
|
||||
expectedManifest, err := expected.Manifest.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
expectedNef, err := expected.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 5, len(act))
|
||||
require.Equal(t, expected.ID, int32(act[0].Value().(*big.Int).Int64()))
|
||||
require.Equal(t, expected.UpdateCounter, uint16(act[1].Value().(*big.Int).Int64()))
|
||||
require.Equal(t, expected.Hash.BytesBE(), act[2].Value().([]byte))
|
||||
require.Equal(t, expectedNef, act[3].Value().([]byte))
|
||||
require.Equal(t, expectedManifest, act[4])
|
||||
}
|
||||
|
||||
func TestMinimumDeploymentFee(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
|
||||
|
@ -619,8 +21,6 @@ func TestMinimumDeploymentFee(t *testing.T) {
|
|||
n := chain.contracts.Management.GetMinimumDeploymentFee(chain.dao)
|
||||
require.Equal(t, 10_00000000, int(n))
|
||||
})
|
||||
|
||||
testGetSet(t, chain, chain.contracts.Management.Hash, "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||
}
|
||||
|
||||
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||
|
|
|
@ -318,7 +318,7 @@ func TestNEO_CommitteeBountyOnPersist(t *testing.T) {
|
|||
func TestNEO_TransferOnPayment(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(bc)
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
const amount = 2
|
||||
|
|
9
pkg/core/test_data/management_helper/README.md
Normal file
9
pkg/core/test_data/management_helper/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Management helper contracts
|
||||
|
||||
Management helper contracts NEF and manifest files are generated automatically by
|
||||
`TestGenerateManagementHelperContracts` and are used in tests. Do not modify these files manually.
|
||||
To regenerate these files:
|
||||
|
||||
1. Open `TestGenerateManagementHelperContracts` and set `saveState` flag to `true`.
|
||||
2. Run `TestGenerateManagementHelperContracts`.
|
||||
3. Set `saveState` back to `false`.
|
1
pkg/core/test_data/management_helper/management_helper1.manifest.json
Executable file
1
pkg/core/test_data/management_helper/management_helper1.manifest.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"TestMain","abi":{"methods":[{"name":"add","offset":1,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"add","offset":3,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"},{"name":"addend3","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"ret7","offset":6,"parameters":[],"returntype":"Integer","safe":false},{"name":"drop","offset":8,"parameters":null,"returntype":"Void","safe":false},{"name":"_initialize","offset":10,"parameters":null,"returntype":"Void","safe":false},{"name":"add3","offset":15,"parameters":[{"name":"addend","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"invalidReturn","offset":18,"parameters":null,"returntype":"Integer","safe":false},{"name":"justReturn","offset":21,"parameters":null,"returntype":"Void","safe":false},{"name":"verify","offset":22,"parameters":null,"returntype":"Boolean","safe":false},{"name":"_deploy","offset":27,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"getValue","offset":158,"parameters":null,"returntype":"String","safe":false},{"name":"putValue","offset":138,"parameters":[{"name":"value","type":"String"}],"returntype":"Void","safe":false},{"name":"delValue","offset":178,"parameters":[{"name":"key","type":"String"}],"returntype":"Void","safe":false},{"name":"onNEP11Payment","offset":215,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"tokenid","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":189,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"update","offset":244,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":241,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"destroy","offset":284,"parameters":null,"returntype":"Void","safe":false},{"name":"invalidStack","offset":325,"parameters":null,"returntype":"Void","safe":false},{"name":"callT0","offset":335,"parameters":[{"name":"address","type":"Hash160"}],"returntype":"Integer","safe":false},{"name":"callT1","offset":341,"parameters":null,"returntype":"Integer","safe":false},{"name":"callT2","offset":345,"parameters":null,"returntype":"Integer","safe":false},{"name":"burnGas","offset":349,"parameters":[{"name":"amount","type":"Integer"}],"returntype":"Void","safe":false},{"name":"invocCounter","offset":355,"parameters":null,"returntype":"Integer","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","methods":["balanceOf"]},{"contract":"0x0000000000000000000000000000000000000000","methods":["method"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
BIN
pkg/core/test_data/management_helper/management_helper1.nef
Executable file
BIN
pkg/core/test_data/management_helper/management_helper1.nef
Executable file
Binary file not shown.
1
pkg/core/test_data/management_helper/management_helper2.manifest.json
Executable file
1
pkg/core/test_data/management_helper/management_helper2.manifest.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"TestAux","abi":{"methods":[],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0x00ecaa2f079b65e3b31572e4c2c160a1abd02997","methods":["add","drop","add3","invalidReturn","justReturn","getValue"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
BIN
pkg/core/test_data/management_helper/management_helper2.nef
Executable file
BIN
pkg/core/test_data/management_helper/management_helper2.nef
Executable file
Binary file not shown.
|
@ -114,6 +114,11 @@ func NewSingle(t *testing.T) (*core.Blockchain, neotest.Signer) {
|
|||
// NewSingleWithCustomConfig creates new blockchain instance with custom protocol
|
||||
// configuration and a single validator. It also setups cleanup functions.
|
||||
func NewSingleWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
|
||||
st := storage.NewMemoryStore()
|
||||
return NewSingleWithCustomConfigAndStore(t, f, st, true)
|
||||
}
|
||||
|
||||
func NewSingleWithCustomConfigAndStore(t *testing.T, f func(cfg *config.ProtocolConfiguration), st storage.Store, run bool) (*core.Blockchain, neotest.Signer) {
|
||||
protoCfg := config.ProtocolConfiguration{
|
||||
Magic: netmode.UnitTestNet,
|
||||
MaxTraceableBlocks: 1000, // We don't need a lot of traceable blocks for tests.
|
||||
|
@ -126,13 +131,13 @@ func NewSingleWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguratio
|
|||
if f != nil {
|
||||
f(&protoCfg)
|
||||
}
|
||||
|
||||
st := storage.NewMemoryStore()
|
||||
log := zaptest.NewLogger(t)
|
||||
bc, err := core.NewBlockchain(st, protoCfg, log)
|
||||
require.NoError(t, err)
|
||||
if run {
|
||||
go bc.Run()
|
||||
t.Cleanup(bc.Close)
|
||||
}
|
||||
return bc, neotest.NewMultiSigner(committeeAcc)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue