forked from TrueCloudLab/neoneo-go
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:
parent
889a7ec378
commit
13252bb941
19 changed files with 648 additions and 702 deletions
103
internal/contracts/contracts.go
Normal file
103
internal/contracts/contracts.go
Normal 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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
495
internal/contracts/contracts_test.go
Normal file
495
internal/contracts/contracts_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
9
internal/contracts/management_helper/README.md
Normal file
9
internal/contracts/management_helper/README.md
Normal 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`.
|
0
pkg/core/test_data/management_helper/management_helper1.nef → internal/contracts/management_helper/management_helper1.nef
Executable file → Normal file
0
pkg/core/test_data/management_helper/management_helper1.nef → internal/contracts/management_helper/management_helper1.nef
Executable file → Normal file
0
pkg/core/test_data/management_helper/management_helper2.nef → internal/contracts/management_helper/management_helper2.nef
Executable file → Normal file
0
pkg/core/test_data/management_helper/management_helper2.nef → internal/contracts/management_helper/management_helper2.nef
Executable file → Normal file
9
internal/contracts/oracle_contract/README.md
Normal file
9
internal/contracts/oracle_contract/README.md
Normal 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`.
|
0
pkg/core/test_data/oracle_contract/oracle.manifest.json → internal/contracts/oracle_contract/oracle.manifest.json
Executable file → Normal file
0
pkg/core/test_data/oracle_contract/oracle.manifest.json → internal/contracts/oracle_contract/oracle.manifest.json
Executable file → Normal file
0
pkg/core/test_data/oracle_contract/oracle.nef → internal/contracts/oracle_contract/oracle.nef
Executable file → Normal file
0
pkg/core/test_data/oracle_contract/oracle.nef → internal/contracts/oracle_contract/oracle.nef
Executable file → Normal file
|
@ -11,6 +11,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
@ -1118,7 +1119,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
func TestVerifyHashAgainstScript(t *testing.T) {
|
func TestVerifyHashAgainstScript(t *testing.T) {
|
||||||
bc := newTestChain(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)
|
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, cs))
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid))
|
||||||
|
@ -1695,7 +1696,7 @@ func TestRemoveUntraceable(t *testing.T) {
|
||||||
func TestInvalidNotification(t *testing.T) {
|
func TestInvalidNotification(t *testing.T) {
|
||||||
bc := newTestChain(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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "invalidStack")
|
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "invalidStack")
|
||||||
|
@ -1709,7 +1710,7 @@ func TestInvalidNotification(t *testing.T) {
|
||||||
func TestMPTDeleteNoKey(t *testing.T) {
|
func TestMPTDeleteNoKey(t *testing.T) {
|
||||||
bc := newTestChain(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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "delValue", "non-existent-key")
|
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "delValue", "non-existent-key")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"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/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"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/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -235,7 +232,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||||
v, ic, bc := createVM(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))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
|
|
||||||
ic.Invocations[hash.Hash160([]byte{2})] = 42
|
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
|
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{}) {
|
func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
|
||||||
ic.SpawnVM()
|
ic.SpawnVM()
|
||||||
ic.VM.LoadScriptWithFlags(script, callflag.AllowCall)
|
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) {
|
func TestContractCall(t *testing.T) {
|
||||||
_, ic, bc := createVM(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, cs))
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
||||||
|
|
||||||
|
@ -1500,7 +1071,7 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
||||||
func TestLoadToken(t *testing.T) {
|
func TestLoadToken(t *testing.T) {
|
||||||
bc := newTestChain(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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
|
@ -1543,7 +1114,7 @@ func TestRuntimeGetNetwork(t *testing.T) {
|
||||||
func TestRuntimeBurnGas(t *testing.T) {
|
func TestRuntimeBurnGas(t *testing.T) {
|
||||||
bc := newTestChain(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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
const sysFee = 2_000000
|
const sysFee = 2_000000
|
||||||
|
|
|
@ -3,17 +3,14 @@ package native_test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"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/chaindump"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"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/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/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"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/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"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/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/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"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/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"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 {
|
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Management)
|
return newNativeClient(t, nativenames.Management)
|
||||||
}
|
}
|
||||||
|
@ -45,62 +34,11 @@ func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
||||||
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
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) {
|
func TestManagement_ContractDeploy(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -301,7 +239,7 @@ func TestManagement_StartFromHeight(t *testing.T) {
|
||||||
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
|
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -329,7 +267,7 @@ func TestManagement_DeployManifestOverflow(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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)
|
manif1, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||||
|
@ -359,7 +297,7 @@ func TestManagement_ContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
cs1.ID = 1
|
cs1.ID = 1
|
||||||
cs1.Hash = state.CreateContractHash(c.CommitteeHash, cs1.NEF.Checksum, cs1.Manifest.Name)
|
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)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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.
|
// Allow calling management contract.
|
||||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
|
@ -535,7 +473,7 @@ func TestManagement_GetContract(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -560,7 +498,7 @@ func TestManagement_ContractDestroy(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
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.
|
// Allow calling management contract.
|
||||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
@ -276,7 +277,7 @@ func TestNEO_TransferOnPayment(t *testing.T) {
|
||||||
e := neoValidatorsInvoker.Executor
|
e := neoValidatorsInvoker.Executor
|
||||||
managementValidatorsInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
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
|
cs.Hash = state.CreateContractHash(e.Validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
||||||
manifB, err := json.Marshal(cs.Manifest)
|
manifB, err := json.Marshal(cs.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -2,32 +2,28 @@ package native_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"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/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/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
"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/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/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal", "contracts")
|
||||||
|
|
||||||
func newOracleClient(t *testing.T) *neotest.ContractInvoker {
|
func newOracleClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Oracle)
|
return newNativeClient(t, nativenames.Oracle)
|
||||||
}
|
}
|
||||||
|
@ -36,36 +32,6 @@ func TestGetSetPrice(t *testing.T) {
|
||||||
testGetSet(t, newOracleClient(t), "Price", native.DefaultOracleRequestPrice, 1, math.MaxInt64)
|
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,
|
func putOracleRequest(t *testing.T, oracleInvoker *neotest.ContractInvoker,
|
||||||
url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) {
|
url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) {
|
||||||
var filtItem interface{}
|
var filtItem interface{}
|
||||||
|
@ -86,7 +52,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
designationCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
|
designationCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
|
||||||
gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
|
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()
|
nBytes, err := cs.NEF.Bytes()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mBytes, err := json.Marshal(cs.Manifest)
|
mBytes, err := json.Marshal(cs.Manifest)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"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/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"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))
|
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, cs))
|
||||||
|
|
||||||
baseFee := chain.GetBaseExecFee()
|
baseFee := chain.GetBaseExecFee()
|
||||||
|
|
|
@ -2,12 +2,10 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
gio "io"
|
gio "io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,22 +13,16 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"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/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"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/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/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/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/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/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -38,132 +30,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
oracleModulePath = filepath.Join("..", "services", "oracle")
|
oracleModulePath = filepath.Join("..", "services", "oracle")
|
||||||
oracleContractNEFPath = filepath.Join("test_data", "oracle_contract", "oracle.nef")
|
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
|
||||||
oracleContractManifestPath = filepath.Join("test_data", "oracle_contract", "oracle.manifest.json")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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,
|
func putOracleRequest(t *testing.T, h util.Uint160, bc *Blockchain,
|
||||||
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
|
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
|
||||||
var filtItem interface{}
|
var filtItem interface{}
|
||||||
|
@ -274,7 +144,7 @@ func TestOracle(t *testing.T) {
|
||||||
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
||||||
orc2.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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
putOracleRequest(t, cs.Hash, bc, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
|
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) }
|
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||||
bc.SetOracle(orc)
|
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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
|
@ -467,7 +337,7 @@ func TestNotYetRunningOracle(t *testing.T) {
|
||||||
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||||
bc.SetOracle(orc)
|
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))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
|
|
|
@ -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`.
|
|
|
@ -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`.
|
|
Loading…
Add table
Reference in a new issue