core: refactor helper test contracts generation

* Move generator to a separate package.
* Move loader to a separate package and get rid of the code duplications.
This commit is contained in:
Anna Shaleva 2022-03-15 19:16:39 +03:00
parent 889a7ec378
commit 13252bb941
19 changed files with 648 additions and 702 deletions

View file

@ -0,0 +1,103 @@
package contracts
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"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/stretchr/testify/require"
)
var (
helper1ContractNEFPath = filepath.Join("management_helper", "management_helper1.nef")
helper1ContractManifestPath = filepath.Join("management_helper", "management_helper1.manifest.json")
helper2ContractNEFPath = filepath.Join("management_helper", "management_helper2.nef")
helper2ContractManifestPath = filepath.Join("management_helper", "management_helper2.manifest.json")
oracleContractNEFPath = filepath.Join("oracle_contract", "oracle.nef")
oracleContractManifestPath = filepath.Join("oracle_contract", "oracle.manifest.json")
)
// GetTestContractState reads 2 pre-compiled contracts generated by
// TestGenerateHelperContracts second of which is allowed to call the first.
func GetTestContractState(t *testing.T, pathToInternalContracts string, id1, id2 int32, sender2 util.Uint160) (*state.Contract, *state.Contract) {
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateHelperContracts to regenerate")
neBytes, err := os.ReadFile(filepath.Join(pathToInternalContracts, helper1ContractNEFPath))
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.ReadFile(filepath.Join(pathToInternalContracts, 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 = os.ReadFile(filepath.Join(pathToInternalContracts, helper2ContractNEFPath))
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
ne, err = nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err = os.ReadFile(filepath.Join(pathToInternalContracts, 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
}
// GetOracleContractState reads pre-compiled oracle contract generated by
// TestGenerateHelperContracts and returns its state.
func GetOracleContractState(t *testing.T, pathToInternalContracts string, sender util.Uint160, id int32) *state.Contract {
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateHelperContracts to regenerate")
neBytes, err := os.ReadFile(filepath.Join(pathToInternalContracts, oracleContractNEFPath))
require.NoError(t, err, fmt.Errorf("nef: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.ReadFile(filepath.Join(pathToInternalContracts, oracleContractManifestPath))
require.NoError(t, err, fmt.Errorf("manifest: %w", errNotFound))
m := &manifest.Manifest{}
err = json.Unmarshal(mBytes, m)
require.NoError(t, err)
return &state.Contract{
ContractBase: state.ContractBase{
NEF: ne,
Hash: state.CreateContractHash(sender, ne.Checksum, m.Name),
Manifest: *m,
ID: id,
},
}
}

View file

@ -0,0 +1,495 @@
package contracts
import (
"encoding/json"
"os"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"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/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"
)
// TestGenerateHelperContracts generates contract states that are used in tests.
// See generateOracleContract and generateManagementHelperContracts comments for
// details.
func TestGenerateHelperContracts(t *testing.T) {
const saveState = false
generateOracleContract(t, saveState)
generateManagementHelperContracts(t, saveState)
require.False(t, saveState)
}
// generateOracleContract generates helper contract that is able to call
// native Oracle contract and has callback method. It uses test chain to define
// Oracle and StdLib native hashes and saves generated NEF and manifest to `oracle_contract` folder.
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
func generateOracleContract(t *testing.T, saveState bool) {
bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
c.P2PSigExtensions = true
})
e := neotest.NewExecutor(t, bc, validator, committee)
oracleHash := e.NativeHash(t, nativenames.Oracle)
stdHash := e.NativeHash(t, nativenames.StdLib)
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(callflag.All))
emit.String(w.BinWriter, "request")
emit.Bytes(w.BinWriter, oracleHash.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
// `handle` method aborts if len(userData) == 2 and does NOT perform witness checks
// for the sake of contract code simplicity (the contract is used in multiple testchains).
offset := w.Len()
emit.Opcodes(w.BinWriter, opcode.OVER)
emit.Opcodes(w.BinWriter, opcode.SIZE)
emit.Int(w.BinWriter, 2)
emit.Instruction(w.BinWriter, opcode.JMPNE, []byte{3})
emit.Opcodes(w.BinWriter, opcode.ABORT)
emit.Int(w.BinWriter, 4) // url, userData, code, result
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.String(w.BinWriter, "lastOracleResponse")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcodes(w.BinWriter, opcode.RET)
m := manifest.NewManifest("TestOracle")
m.ABI.Methods = []manifest.Method{
{
Name: "requestURL",
Offset: 0,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("filter", smartcontract.StringType),
manifest.NewParameter("callback", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("gasForResponse", smartcontract.IntegerType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "handle",
Offset: offset,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("code", smartcontract.IntegerType),
manifest.NewParameter("result", smartcontract.ByteArrayType),
},
ReturnType: smartcontract.VoidType,
},
}
perm := manifest.NewPermission(manifest.PermissionHash, oracleHash)
perm.Methods.Add("request")
m.Permissions = append(m.Permissions, *perm)
// Generate NEF file.
script := w.Bytes()
ne, err := nef.NewFile(script)
require.NoError(t, err)
// Write NEF file.
bytes, err := ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractNEFPath, bytes, os.ModePerm)
require.NoError(t, err)
}
// Write manifest file.
mData, err := json.Marshal(m)
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractManifestPath, mData, os.ModePerm)
require.NoError(t, err)
}
}
// generateManagementHelperContracts 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 `management_contract` folder.
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
func generateManagementHelperContracts(t *testing.T, saveState bool) {
bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
c.P2PSigExtensions = true
})
e := neotest.NewExecutor(t, bc, validator, committee)
mgmtHash := e.NativeHash(t, nativenames.Management)
stdHash := e.NativeHash(t, nativenames.StdLib)
neoHash := e.NativeHash(t, nativenames.Neo)
singleChainValidatorAcc := e.Validator.(neotest.MultiSigner).Single(2).Account() // priv0
require.NoError(t, singleChainValidatorAcc.ConvertMultisig(1, keys.PublicKeys{singleChainValidatorAcc.PrivateKey().PublicKey()}))
singleChainValidatorHash := singleChainValidatorAcc.Contract.ScriptHash()
w := io.NewBufBinWriter()
emit.Opcodes(w.BinWriter, opcode.ABORT)
addOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.RET)
addMultiOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.ADD, opcode.RET)
ret7Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.PUSH7, opcode.RET)
dropOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.DROP, opcode.RET)
initOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.INITSSLOT, 1, opcode.PUSH3, opcode.STSFLD0, opcode.RET)
add3Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.ADD, opcode.RET)
invalidRetOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.PUSH1, opcode.PUSH2, opcode.RET)
justRetOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.RET)
verifyOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB,
opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET)
deployOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+1+1+39+3)
emit.String(w.BinWriter, "create") // 8 bytes
emit.Int(w.BinWriter, 2) // 1 byte
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+1+1+39+3, opcode.RET)
emit.String(w.BinWriter, "update") // 8 bytes
emit.Int(w.BinWriter, 2) // 1 byte
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET)
putValOff := w.Len()
emit.String(w.BinWriter, "initial")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcodes(w.BinWriter, opcode.RET)
getValOff := w.Len()
emit.String(w.BinWriter, "initial")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStorageGet)
emit.Opcodes(w.BinWriter, opcode.RET)
delValOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStorageDelete)
emit.Opcodes(w.BinWriter, opcode.RET)
onNEP17PaymentOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
emit.Int(w.BinWriter, 4)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "LastPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcodes(w.BinWriter, opcode.RET)
onNEP11PaymentOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "LostPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcodes(w.BinWriter, opcode.RET)
update3Off := w.Len()
emit.Int(w.BinWriter, 3)
emit.Opcodes(w.BinWriter, opcode.JMP, 2+1)
updateOff := w.Len()
emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.AppCallNoArgs(w.BinWriter, mgmtHash, "update", callflag.All)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
destroyOff := w.Len()
emit.AppCall(w.BinWriter, mgmtHash, "destroy", callflag.All)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
invalidStackOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND) // recursive array
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetReadOnlyContext) // interop item
emit.Opcodes(w.BinWriter, opcode.RET)
callT0Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.PUSH1, opcode.ADD, opcode.RET)
callT1Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 1, 0, opcode.RET)
callT2Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.RET)
burnGasOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeBurnGas)
emit.Opcodes(w.BinWriter, opcode.RET)
invocCounterOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetInvocationCounter)
emit.Opcodes(w.BinWriter, opcode.RET)
script := w.Bytes()
m := manifest.NewManifest("TestMain")
m.ABI.Methods = []manifest.Method{
{
Name: "add",
Offset: addOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend1", smartcontract.IntegerType),
manifest.NewParameter("addend2", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "add",
Offset: addMultiOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend1", smartcontract.IntegerType),
manifest.NewParameter("addend2", smartcontract.IntegerType),
manifest.NewParameter("addend3", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "ret7",
Offset: ret7Off,
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
},
{
Name: "drop",
Offset: dropOff,
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodInit,
Offset: initOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "add3",
Offset: add3Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "invalidReturn",
Offset: invalidRetOff,
ReturnType: smartcontract.IntegerType,
},
{
Name: "justReturn",
Offset: justRetOff,
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodVerify,
Offset: verifyOff,
ReturnType: smartcontract.BoolType,
},
{
Name: manifest.MethodDeploy,
Offset: deployOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "getValue",
Offset: getValOff,
ReturnType: smartcontract.StringType,
},
{
Name: "putValue",
Offset: putValOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("value", smartcontract.StringType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "delValue",
Offset: delValOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("key", smartcontract.StringType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodOnNEP11Payment,
Offset: onNEP11PaymentOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodOnNEP17Payment,
Offset: onNEP17PaymentOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "update",
Offset: updateOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("nef", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "update",
Offset: update3Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("nef", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "destroy",
Offset: destroyOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "invalidStack",
Offset: invalidStackOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "callT0",
Offset: callT0Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("address", smartcontract.Hash160Type),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "callT1",
Offset: callT1Off,
ReturnType: smartcontract.IntegerType,
},
{
Name: "callT2",
Offset: callT2Off,
ReturnType: smartcontract.IntegerType,
},
{
Name: "burnGas",
Offset: burnGasOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("amount", smartcontract.IntegerType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "invocCounter",
Offset: invocCounterOff,
ReturnType: smartcontract.IntegerType,
},
}
m.Permissions = make([]manifest.Permission, 2)
m.Permissions[0].Contract.Type = manifest.PermissionHash
m.Permissions[0].Contract.Value = neoHash
m.Permissions[0].Methods.Add("balanceOf")
m.Permissions[1].Contract.Type = manifest.PermissionHash
m.Permissions[1].Contract.Value = util.Uint160{}
m.Permissions[1].Methods.Add("method")
// Generate NEF file.
ne, err := nef.NewFile(script)
require.NoError(t, err)
ne.Tokens = []nef.MethodToken{
{
Hash: neoHash,
Method: "balanceOf",
ParamCount: 1,
HasReturn: true,
CallFlag: callflag.ReadStates,
},
{
Hash: util.Uint160{},
Method: "method",
HasReturn: true,
CallFlag: callflag.ReadStates,
},
}
ne.Checksum = ne.CalculateChecksum()
// Write first NEF file.
bytes, err := ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.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 = os.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")
perm := manifest.NewPermission(manifest.PermissionHash, h)
perm.Methods.Add("add")
perm.Methods.Add("drop")
perm.Methods.Add("add3")
perm.Methods.Add("invalidReturn")
perm.Methods.Add("justReturn")
perm.Methods.Add("getValue")
m.Permissions = append(m.Permissions, *perm)
ne, err = nef.NewFile(currScript)
require.NoError(t, err)
// Write second NEF file.
bytes, err = ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.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 = os.WriteFile(helper2ContractManifestPath, mData, os.ModePerm)
require.NoError(t, err)
}
}

View file

@ -0,0 +1,9 @@
## Management helper contracts
Management helper contracts NEF and manifest files are generated automatically by
`TestGenerateHelperContracts` and are used in tests. Do not modify these files manually.
To regenerate these files:
1. Open `TestGenerateHelperContracts` and set `saveState` flag to `true`.
2. Run `TestGenerateHelperContracts`.
3. Set `saveState` back to `false`.

View file

@ -0,0 +1,9 @@
## Oracle helper contract
Oracle helper contract NEF and manifest files are generated automatically by
`TestGenerateHelperContracts` and are used in tests. Do not modify these files manually.
To regenerate these files:
1. Open `TestGenerateHelperContracts` and set `saveState` flag to `true`.
2. Run `TestGenerateHelperContracts`.
3. Set `saveState` back to `false`.

View file

@ -11,6 +11,7 @@ import (
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config"
@ -1118,7 +1119,7 @@ func TestVerifyTx(t *testing.T) {
func TestVerifyHashAgainstScript(t *testing.T) {
bc := newTestChain(t)
cs, csInvalid := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, csInvalid := contracts.GetTestContractState(t, pathToInternalContracts, 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))
@ -1695,7 +1696,7 @@ func TestRemoveUntraceable(t *testing.T) {
func TestInvalidNotification(t *testing.T) {
bc := newTestChain(t)
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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")
@ -1709,7 +1710,7 @@ func TestInvalidNotification(t *testing.T) {
func TestMPTDeleteNoKey(t *testing.T) {
bc := newTestChain(t)
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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)

View file

@ -1,15 +1,13 @@
package core
import (
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config"
@ -39,7 +37,6 @@ 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"
)
@ -235,7 +232,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
func TestRuntimeGetInvocationCounter(t *testing.T) {
v, ic, bc := createVM(t)
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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
@ -661,432 +658,6 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C
return v, contractState, 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")
)
// 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)
addOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.RET)
addMultiOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.ADD, opcode.RET)
ret7Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.PUSH7, opcode.RET)
dropOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.DROP, opcode.RET)
initOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.INITSSLOT, 1, opcode.PUSH3, opcode.STSFLD0, opcode.RET)
add3Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.ADD, opcode.RET)
invalidRetOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.PUSH1, opcode.PUSH2, opcode.RET)
justRetOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.RET)
verifyOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB,
opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET)
deployOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+1+1+39+3)
emit.String(w.BinWriter, "create") // 8 bytes
emit.Int(w.BinWriter, 2) // 1 byte
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+1+1+39+3, opcode.RET)
emit.String(w.BinWriter, "update") // 8 bytes
emit.Int(w.BinWriter, 2) // 1 byte
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET)
putValOff := w.Len()
emit.String(w.BinWriter, "initial")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcodes(w.BinWriter, opcode.RET)
getValOff := w.Len()
emit.String(w.BinWriter, "initial")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStorageGet)
emit.Opcodes(w.BinWriter, opcode.RET)
delValOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStorageDelete)
emit.Opcodes(w.BinWriter, opcode.RET)
onNEP17PaymentOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
emit.Int(w.BinWriter, 4)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "LastPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcodes(w.BinWriter, opcode.RET)
onNEP11PaymentOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "LostPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcodes(w.BinWriter, opcode.RET)
update3Off := w.Len()
emit.Int(w.BinWriter, 3)
emit.Opcodes(w.BinWriter, opcode.JMP, 2+1)
updateOff := w.Len()
emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.AppCallNoArgs(w.BinWriter, mgmtHash, "update", callflag.All)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
destroyOff := w.Len()
emit.AppCall(w.BinWriter, mgmtHash, "destroy", callflag.All)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
invalidStackOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND) // recursive array
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetReadOnlyContext) // interop item
emit.Opcodes(w.BinWriter, opcode.RET)
callT0Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.PUSH1, opcode.ADD, opcode.RET)
callT1Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 1, 0, opcode.RET)
callT2Off := w.Len()
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.RET)
burnGasOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeBurnGas)
emit.Opcodes(w.BinWriter, opcode.RET)
invocCounterOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetInvocationCounter)
emit.Opcodes(w.BinWriter, opcode.RET)
script := w.Bytes()
m := manifest.NewManifest("TestMain")
m.ABI.Methods = []manifest.Method{
{
Name: "add",
Offset: addOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend1", smartcontract.IntegerType),
manifest.NewParameter("addend2", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "add",
Offset: addMultiOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend1", smartcontract.IntegerType),
manifest.NewParameter("addend2", smartcontract.IntegerType),
manifest.NewParameter("addend3", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "ret7",
Offset: ret7Off,
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
},
{
Name: "drop",
Offset: dropOff,
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodInit,
Offset: initOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "add3",
Offset: add3Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("addend", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "invalidReturn",
Offset: invalidRetOff,
ReturnType: smartcontract.IntegerType,
},
{
Name: "justReturn",
Offset: justRetOff,
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodVerify,
Offset: verifyOff,
ReturnType: smartcontract.BoolType,
},
{
Name: manifest.MethodDeploy,
Offset: deployOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "getValue",
Offset: getValOff,
ReturnType: smartcontract.StringType,
},
{
Name: "putValue",
Offset: putValOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("value", smartcontract.StringType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "delValue",
Offset: delValOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("key", smartcontract.StringType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodOnNEP11Payment,
Offset: onNEP11PaymentOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: manifest.MethodOnNEP17Payment,
Offset: onNEP17PaymentOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "update",
Offset: updateOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("nef", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "update",
Offset: update3Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("nef", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "destroy",
Offset: destroyOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "invalidStack",
Offset: invalidStackOff,
ReturnType: smartcontract.VoidType,
},
{
Name: "callT0",
Offset: callT0Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("address", smartcontract.Hash160Type),
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "callT1",
Offset: callT1Off,
ReturnType: smartcontract.IntegerType,
},
{
Name: "callT2",
Offset: callT2Off,
ReturnType: smartcontract.IntegerType,
},
{
Name: "burnGas",
Offset: burnGasOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("amount", smartcontract.IntegerType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "invocCounter",
Offset: invocCounterOff,
ReturnType: smartcontract.IntegerType,
},
}
m.Permissions = make([]manifest.Permission, 2)
m.Permissions[0].Contract.Type = manifest.PermissionHash
m.Permissions[0].Contract.Value = bc.contracts.NEO.Hash
m.Permissions[0].Methods.Add("balanceOf")
m.Permissions[1].Contract.Type = manifest.PermissionHash
m.Permissions[1].Contract.Value = util.Uint160{}
m.Permissions[1].Methods.Add("method")
// Generate NEF file.
ne, err := nef.NewFile(script)
if err != nil {
panic(err)
}
ne.Tokens = []nef.MethodToken{
{
Hash: neoHash,
Method: "balanceOf",
ParamCount: 1,
HasReturn: true,
CallFlag: callflag.ReadStates,
},
{
Hash: util.Uint160{},
Method: "method",
HasReturn: true,
CallFlag: callflag.ReadStates,
},
}
ne.Checksum = ne.CalculateChecksum()
// Write first NEF file.
bytes, err := ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.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 = os.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")
perm := manifest.NewPermission(manifest.PermissionHash, h)
perm.Methods.Add("add")
perm.Methods.Add("drop")
perm.Methods.Add("add3")
perm.Methods.Add("invalidReturn")
perm.Methods.Add("justReturn")
perm.Methods.Add("getValue")
m.Permissions = append(m.Permissions, *perm)
ne, err = nef.NewFile(currScript)
if err != nil {
panic(err)
}
// Write second NEF file.
bytes, err = ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.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 = os.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 := os.ReadFile(helper1ContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.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 = os.ReadFile(helper2ContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
ne, err = nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err = os.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{}) {
ic.SpawnVM()
ic.VM.LoadScriptWithFlags(script, callflag.AllowCall)
@ -1108,7 +679,7 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui
func TestContractCall(t *testing.T) {
_, ic, bc := createVM(t)
cs, currCs := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, currCs := contracts.GetTestContractState(t, pathToInternalContracts, 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))
@ -1500,7 +1071,7 @@ func TestRuntimeCheckWitness(t *testing.T) {
func TestLoadToken(t *testing.T) {
bc := newTestChain(t)
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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) {
@ -1543,7 +1114,7 @@ func TestRuntimeGetNetwork(t *testing.T) {
func TestRuntimeBurnGas(t *testing.T) {
bc := newTestChain(t)
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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

View file

@ -3,17 +3,14 @@ package native_test
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/internal/contracts"
"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/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"
@ -23,20 +20,12 @@ import (
"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)
}
@ -45,62 +34,11 @@ 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 := os.ReadFile(helper1ContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.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 = os.ReadFile(helper2ContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
ne, err = nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err = os.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())
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.Committee.ScriptHash())
manifestBytes, err := json.Marshal(cs1.Manifest)
require.NoError(t, err)
nefBytes, err := cs1.NEF.Bytes()
@ -301,7 +239,7 @@ func TestManagement_StartFromHeight(t *testing.T) {
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
manifestBytes, err := json.Marshal(cs1.Manifest)
require.NoError(t, err)
nefBytes, err := cs1.NEF.Bytes()
@ -329,7 +267,7 @@ func TestManagement_DeployManifestOverflow(t *testing.T) {
c := newManagementClient(t)
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
manif1, err := json.Marshal(cs1.Manifest)
require.NoError(t, err)
nef1, err := nef.NewFile(cs1.NEF.Script)
@ -359,7 +297,7 @@ func TestManagement_ContractDeployAndUpdateWithParameter(t *testing.T) {
c := newManagementClient(t)
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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)
@ -400,7 +338,7 @@ func TestManagement_ContractUpdate(t *testing.T) {
c := newManagementClient(t)
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
// Allow calling management contract.
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
manifestBytes, err := json.Marshal(cs1.Manifest)
@ -535,7 +473,7 @@ func TestManagement_GetContract(t *testing.T) {
c := newManagementClient(t)
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
manifestBytes, err := json.Marshal(cs1.Manifest)
require.NoError(t, err)
nefBytes, err := cs1.NEF.Bytes()
@ -560,7 +498,7 @@ func TestManagement_ContractDestroy(t *testing.T) {
c := newManagementClient(t)
managementInvoker := c.WithSigners(c.Committee)
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
// Allow calling management contract.
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
manifestBytes, err := json.Marshal(cs1.Manifest)

View file

@ -7,6 +7,7 @@ import (
"sort"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
@ -276,7 +277,7 @@ func TestNEO_TransferOnPayment(t *testing.T) {
e := neoValidatorsInvoker.Executor
managementValidatorsInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
cs, _ := getTestContractState(t, 1, 2, e.CommitteeHash)
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, e.CommitteeHash)
cs.Hash = state.CreateContractHash(e.Validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
manifB, err := json.Marshal(cs.Manifest)
require.NoError(t, err)

View file

@ -2,32 +2,28 @@ package native_test
import (
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/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/stackitem"
"github.com/stretchr/testify/require"
)
var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal", "contracts")
func newOracleClient(t *testing.T) *neotest.ContractInvoker {
return newNativeClient(t, nativenames.Oracle)
}
@ -36,36 +32,6 @@ func TestGetSetPrice(t *testing.T) {
testGetSet(t, newOracleClient(t), "Price", native.DefaultOracleRequestPrice, 1, math.MaxInt64)
}
// getOracleContractState reads pre-compiled oracle contract generated by
// TestGenerateOracleContract and returns its state.
func getOracleContractState(t *testing.T, sender util.Uint160, id int32) *state.Contract {
var (
oracleContractNEFPath = filepath.Join("..", "..", "test_data", "oracle_contract", "oracle.nef")
oracleContractManifestPath = filepath.Join("..", "..", "test_data", "oracle_contract", "oracle.manifest.json")
)
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
neBytes, err := os.ReadFile(oracleContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.ReadFile(oracleContractManifestPath)
require.NoError(t, err, fmt.Errorf("manifest: %w", errNotFound))
m := &manifest.Manifest{}
err = json.Unmarshal(mBytes, m)
require.NoError(t, err)
return &state.Contract{
ContractBase: state.ContractBase{
NEF: ne,
Hash: state.CreateContractHash(sender, ne.Checksum, m.Name),
Manifest: *m,
ID: id,
},
}
}
func putOracleRequest(t *testing.T, oracleInvoker *neotest.ContractInvoker,
url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) {
var filtItem interface{}
@ -86,7 +52,7 @@ func TestOracle_Request(t *testing.T) {
designationCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
cs := getOracleContractState(t, e.Validator.ScriptHash(), 1)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, e.Validator.ScriptHash(), 1)
nBytes, err := cs.NEF.Bytes()
require.NoError(t, err)
mBytes, err := json.Marshal(cs.Manifest)

View file

@ -5,6 +5,7 @@ import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"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"
@ -302,7 +303,7 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) {
}
}
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 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()

View file

@ -2,12 +2,10 @@ package core
import (
"bytes"
"encoding/json"
"errors"
"fmt"
gio "io"
"net/http"
"os"
"path"
"path/filepath"
"strings"
@ -15,22 +13,16 @@ import (
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/services/oracle"
"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/wallet"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -38,132 +30,10 @@ import (
)
var (
oracleModulePath = filepath.Join("..", "services", "oracle")
oracleContractNEFPath = filepath.Join("test_data", "oracle_contract", "oracle.nef")
oracleContractManifestPath = filepath.Join("test_data", "oracle_contract", "oracle.manifest.json")
oracleModulePath = filepath.Join("..", "services", "oracle")
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
)
// TestGenerateOracleContract generates helper contract that is able to call
// native Oracle contract and has callback method. It uses test chain to define
// Oracle and StdLib native hashes and saves generated NEF and manifest to ... folder.
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
func TestGenerateOracleContract(t *testing.T) {
const saveState = false
bc := newTestChain(t)
oracleHash := bc.contracts.Oracle.Hash
stdHash := bc.contracts.Std.Hash
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(callflag.All))
emit.String(w.BinWriter, "request")
emit.Bytes(w.BinWriter, oracleHash.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
// `handle` method aborts if len(userData) == 2 and does NOT perform witness checks
// for the sake of contract code simplicity (the contract is used in multiple testchains).
offset := w.Len()
emit.Opcodes(w.BinWriter, opcode.OVER)
emit.Opcodes(w.BinWriter, opcode.SIZE)
emit.Int(w.BinWriter, 2)
emit.Instruction(w.BinWriter, opcode.JMPNE, []byte{3})
emit.Opcodes(w.BinWriter, opcode.ABORT)
emit.Int(w.BinWriter, 4) // url, userData, code, result
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.String(w.BinWriter, "lastOracleResponse")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcodes(w.BinWriter, opcode.RET)
m := manifest.NewManifest("TestOracle")
m.ABI.Methods = []manifest.Method{
{
Name: "requestURL",
Offset: 0,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("filter", smartcontract.StringType),
manifest.NewParameter("callback", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("gasForResponse", smartcontract.IntegerType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "handle",
Offset: offset,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("code", smartcontract.IntegerType),
manifest.NewParameter("result", smartcontract.ByteArrayType),
},
ReturnType: smartcontract.VoidType,
},
}
perm := manifest.NewPermission(manifest.PermissionHash, oracleHash)
perm.Methods.Add("request")
m.Permissions = append(m.Permissions, *perm)
// Generate NEF file.
script := w.Bytes()
ne, err := nef.NewFile(script)
require.NoError(t, err)
// Write NEF file.
bytes, err := ne.Bytes()
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractNEFPath, bytes, os.ModePerm)
require.NoError(t, err)
}
// Write manifest file.
mData, err := json.Marshal(m)
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractManifestPath, mData, os.ModePerm)
require.NoError(t, err)
}
require.False(t, saveState)
}
// getOracleContractState reads pre-compiled oracle contract generated by
// TestGenerateOracleContract and returns its state.
func getOracleContractState(t *testing.T, sender util.Uint160, id int32) *state.Contract {
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
neBytes, err := os.ReadFile(oracleContractNEFPath)
require.NoError(t, err, fmt.Errorf("nef: %w", errNotFound))
ne, err := nef.FileFromBytes(neBytes)
require.NoError(t, err)
mBytes, err := os.ReadFile(oracleContractManifestPath)
require.NoError(t, err, fmt.Errorf("manifest: %w", errNotFound))
m := &manifest.Manifest{}
err = json.Unmarshal(mBytes, m)
require.NoError(t, err)
return &state.Contract{
ContractBase: state.ContractBase{
NEF: ne,
Hash: state.CreateContractHash(sender, ne.Checksum, m.Name),
Manifest: *m,
ID: id,
},
}
}
func putOracleRequest(t *testing.T, h util.Uint160, bc *Blockchain,
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
var filtItem interface{}
@ -274,7 +144,7 @@ func TestOracle(t *testing.T) {
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
cs := getOracleContractState(t, util.Uint160{}, 42)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
putOracleRequest(t, cs.Hash, bc, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
@ -442,7 +312,7 @@ func TestOracleFull(t *testing.T) {
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc)
cs := getOracleContractState(t, util.Uint160{}, 42)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
go bc.Run()
@ -467,7 +337,7 @@ func TestNotYetRunningOracle(t *testing.T) {
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc)
cs := getOracleContractState(t, util.Uint160{}, 42)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
go bc.Run()

View file

@ -1,9 +0,0 @@
## 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`.

View file

@ -1,9 +0,0 @@
## Oracle helper contract
Oracle helper contract NEF and manifest files are generated automatically by
`TestGenerateOracleContract` and are used in tests. Do not modify these files manually.
To regenerate these files:
1. Open `TestGenerateOracleContract` and set `saveState` flag to `true`.
2. Run `TestGenerateOracleContract`.
3. Set `saveState` back to `false`.