Merge pull request #2393 from nspcc-dev/core/refactor-TestCreateBasicChain
*: rebase core tests onto neotest
This commit is contained in:
commit
df2bca0f40
48 changed files with 3767 additions and 4100 deletions
|
@ -17,12 +17,12 @@ commands:
|
|||
gomod:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys: [deps-]
|
||||
keys: [deps-v2-]
|
||||
- run:
|
||||
name: Download go module dependencies
|
||||
command: go mod download
|
||||
- save_cache:
|
||||
key: deps-{{ checksum "go.sum" }}-{{ checksum "go.sum" }}
|
||||
key: deps-v2-{{ checksum "go.sum" }}-{{ checksum "go.sum" }}
|
||||
paths: [/home/circleci/go/pkg/mod]
|
||||
|
||||
jobs:
|
||||
|
|
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,
|
||||
},
|
||||
}
|
||||
}
|
509
internal/contracts/contracts_test.go
Normal file
509
internal/contracts/contracts_test.go
Normal file
|
@ -0,0 +1,509 @@
|
|||
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)
|
||||
invalidStack1Off := w.Len()
|
||||
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND) // recursive array
|
||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||
invalidStack2Off := w.Len()
|
||||
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: "invalidStack1",
|
||||
Offset: invalidStack1Off,
|
||||
ReturnType: smartcontract.AnyType,
|
||||
},
|
||||
{
|
||||
Name: "invalidStack2",
|
||||
Offset: invalidStack2Off,
|
||||
ReturnType: smartcontract.AnyType,
|
||||
},
|
||||
{
|
||||
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")
|
||||
m.ABI.Methods = []manifest.Method{
|
||||
{
|
||||
Name: "simpleMethod",
|
||||
Offset: 0,
|
||||
ReturnType: smartcontract.VoidType,
|
||||
},
|
||||
}
|
||||
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`.
|
|
@ -1 +1 @@
|
|||
{"name":"TestMain","abi":{"methods":[{"name":"add","offset":1,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"add","offset":3,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"},{"name":"addend3","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"ret7","offset":6,"parameters":[],"returntype":"Integer","safe":false},{"name":"drop","offset":8,"parameters":null,"returntype":"Void","safe":false},{"name":"_initialize","offset":10,"parameters":null,"returntype":"Void","safe":false},{"name":"add3","offset":15,"parameters":[{"name":"addend","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"invalidReturn","offset":18,"parameters":null,"returntype":"Integer","safe":false},{"name":"justReturn","offset":21,"parameters":null,"returntype":"Void","safe":false},{"name":"verify","offset":22,"parameters":null,"returntype":"Boolean","safe":false},{"name":"_deploy","offset":27,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"getValue","offset":158,"parameters":null,"returntype":"String","safe":false},{"name":"putValue","offset":138,"parameters":[{"name":"value","type":"String"}],"returntype":"Void","safe":false},{"name":"delValue","offset":178,"parameters":[{"name":"key","type":"String"}],"returntype":"Void","safe":false},{"name":"onNEP11Payment","offset":215,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"tokenid","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":189,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"update","offset":244,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":241,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"destroy","offset":284,"parameters":null,"returntype":"Void","safe":false},{"name":"invalidStack","offset":325,"parameters":null,"returntype":"Void","safe":false},{"name":"callT0","offset":335,"parameters":[{"name":"address","type":"Hash160"}],"returntype":"Integer","safe":false},{"name":"callT1","offset":341,"parameters":null,"returntype":"Integer","safe":false},{"name":"callT2","offset":345,"parameters":null,"returntype":"Integer","safe":false},{"name":"burnGas","offset":349,"parameters":[{"name":"amount","type":"Integer"}],"returntype":"Void","safe":false},{"name":"invocCounter","offset":355,"parameters":null,"returntype":"Integer","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","methods":["balanceOf"]},{"contract":"0x0000000000000000000000000000000000000000","methods":["method"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
||||
{"name":"TestMain","abi":{"methods":[{"name":"add","offset":1,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"add","offset":3,"parameters":[{"name":"addend1","type":"Integer"},{"name":"addend2","type":"Integer"},{"name":"addend3","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"ret7","offset":6,"parameters":[],"returntype":"Integer","safe":false},{"name":"drop","offset":8,"parameters":null,"returntype":"Void","safe":false},{"name":"_initialize","offset":10,"parameters":null,"returntype":"Void","safe":false},{"name":"add3","offset":15,"parameters":[{"name":"addend","type":"Integer"}],"returntype":"Integer","safe":false},{"name":"invalidReturn","offset":18,"parameters":null,"returntype":"Integer","safe":false},{"name":"justReturn","offset":21,"parameters":null,"returntype":"Void","safe":false},{"name":"verify","offset":22,"parameters":null,"returntype":"Boolean","safe":false},{"name":"_deploy","offset":27,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"getValue","offset":158,"parameters":null,"returntype":"String","safe":false},{"name":"putValue","offset":138,"parameters":[{"name":"value","type":"String"}],"returntype":"Void","safe":false},{"name":"delValue","offset":178,"parameters":[{"name":"key","type":"String"}],"returntype":"Void","safe":false},{"name":"onNEP11Payment","offset":215,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"tokenid","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":189,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"update","offset":244,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":241,"parameters":[{"name":"nef","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"destroy","offset":284,"parameters":null,"returntype":"Void","safe":false},{"name":"invalidStack1","offset":324,"parameters":null,"returntype":"Any","safe":false},{"name":"invalidStack2","offset":329,"parameters":null,"returntype":"Any","safe":false},{"name":"callT0","offset":335,"parameters":[{"name":"address","type":"Hash160"}],"returntype":"Integer","safe":false},{"name":"callT1","offset":341,"parameters":null,"returntype":"Integer","safe":false},{"name":"callT2","offset":345,"parameters":null,"returntype":"Integer","safe":false},{"name":"burnGas","offset":349,"parameters":[{"name":"amount","type":"Integer"}],"returntype":"Void","safe":false},{"name":"invocCounter","offset":355,"parameters":null,"returntype":"Integer","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","methods":["balanceOf"]},{"contract":"0x0000000000000000000000000000000000000000","methods":["method"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
BIN
pkg/core/test_data/management_helper/management_helper1.nef → internal/contracts/management_helper/management_helper1.nef
Executable file → Normal file
BIN
pkg/core/test_data/management_helper/management_helper1.nef → internal/contracts/management_helper/management_helper1.nef
Executable file → Normal file
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
{"name":"TestAux","abi":{"methods":[{"name":"simpleMethod","offset":0,"parameters":null,"returntype":"Void","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0x5a6a3b6c716e31465ed9be0b234d6223498c9f4e","methods":["add","drop","add3","invalidReturn","justReturn","getValue"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
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
282
pkg/core/basic_chain_test.go
Normal file
282
pkg/core/basic_chain_test.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||
"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/rpc/client/nns"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
// examplesPrefix is a prefix of the example smart-contracts.
|
||||
examplesPrefix = "../../examples/"
|
||||
// basicChainPrefix is a prefix used to store Basic chain .acc file for tests.
|
||||
// It is also used to retrieve smart contracts that should be deployed to
|
||||
// Basic chain.
|
||||
basicChainPrefix = "../rpc/server/testdata/"
|
||||
// bcPersistInterval is the time period Blockchain persists changes to the
|
||||
// underlying storage.
|
||||
bcPersistInterval = time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
notaryModulePath = filepath.Join("..", "services", "notary")
|
||||
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
|
||||
)
|
||||
|
||||
// TestCreateBasicChain generates "../rpc/testdata/testblocks.acc" file which
|
||||
// contains data for RPC unit tests. It also is a nice integration test.
|
||||
// To generate new "../rpc/testdata/testblocks.acc", follow the steps:
|
||||
// 1. Set saveChain down below to true
|
||||
// 2. Run tests with `$ make test`
|
||||
func TestCreateBasicChain(t *testing.T) {
|
||||
const saveChain = false
|
||||
|
||||
bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(cfg *config.ProtocolConfiguration) {
|
||||
cfg.P2PSigExtensions = true
|
||||
})
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
|
||||
initBasicChain(t, e)
|
||||
|
||||
if saveChain {
|
||||
outStream, err := os.Create(basicChainPrefix + "testblocks.acc")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
outStream.Close()
|
||||
})
|
||||
|
||||
writer := io.NewBinWriterFromIO(outStream)
|
||||
writer.WriteU32LE(bc.BlockHeight())
|
||||
err = chaindump.Dump(bc, writer, 1, bc.BlockHeight())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.False(t, saveChain)
|
||||
}
|
||||
|
||||
func initBasicChain(t *testing.T, e *neotest.Executor) {
|
||||
if !e.Chain.GetConfig().P2PSigExtensions {
|
||||
t.Fatal("P2PSitExtensions should be enabled to init basic chain")
|
||||
}
|
||||
|
||||
const neoAmount = 99999000
|
||||
|
||||
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||
neoHash := e.NativeHash(t, nativenames.Neo)
|
||||
policyHash := e.NativeHash(t, nativenames.Policy)
|
||||
notaryHash := e.NativeHash(t, nativenames.Notary)
|
||||
designationHash := e.NativeHash(t, nativenames.Designation)
|
||||
t.Logf("native GAS hash: %v", gasHash)
|
||||
t.Logf("native NEO hash: %v", neoHash)
|
||||
t.Logf("native Policy hash: %v", policyHash)
|
||||
t.Logf("native Notary hash: %v", notaryHash)
|
||||
t.Logf("Block0 hash: %s", e.Chain.GetHeaderHash(0).StringLE())
|
||||
|
||||
acc0 := e.Validator.(neotest.MultiSigner).Single(2) // priv0 index->order and order->index conversion
|
||||
priv0ScriptHash := acc0.ScriptHash()
|
||||
acc1 := e.Validator.(neotest.MultiSigner).Single(0) // priv1 index->order and order->index conversion
|
||||
priv1ScriptHash := acc1.ScriptHash()
|
||||
neoValidatorInvoker := e.ValidatorInvoker(neoHash)
|
||||
gasValidatorInvoker := e.ValidatorInvoker(gasHash)
|
||||
neoPriv0Invoker := e.NewInvoker(neoHash, acc0)
|
||||
gasPriv0Invoker := e.NewInvoker(gasHash, acc0)
|
||||
designateSuperInvoker := e.NewInvoker(designationHash, e.Validator, e.Committee)
|
||||
|
||||
deployContractFromPriv0 := func(t *testing.T, path, contractName string, configPath string, expectedID int32) (util.Uint256, util.Uint256, util.Uint160) {
|
||||
txDeployHash, cH := newDeployTx(t, e, acc0, path, configPath, true)
|
||||
b := e.TopBlock(t)
|
||||
return b.Hash(), txDeployHash, cH
|
||||
}
|
||||
|
||||
e.CheckGASBalance(t, priv0ScriptHash, big.NewInt(5000_0000)) // gas bounty
|
||||
|
||||
// Block #1: move 1000 GAS and neoAmount NEO to priv0.
|
||||
txMoveNeo := neoValidatorInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), priv0ScriptHash, neoAmount, nil)
|
||||
txMoveGas := gasValidatorInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), priv0ScriptHash, int64(fixedn.Fixed8FromInt64(1000)), nil)
|
||||
b := e.AddNewBlock(t, txMoveNeo, txMoveGas)
|
||||
e.CheckHalt(t, txMoveNeo.Hash(), stackitem.Make(true))
|
||||
e.CheckHalt(t, txMoveGas.Hash(), stackitem.Make(true))
|
||||
t.Logf("Block1 hash: %s", b.Hash().StringLE())
|
||||
bw := io.NewBufBinWriter()
|
||||
b.EncodeBinary(bw.BinWriter)
|
||||
require.NoError(t, bw.Err)
|
||||
jsonB, err := b.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("Block1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
||||
t.Logf("Block1 JSON: %s", string(jsonB))
|
||||
bw.Reset()
|
||||
b.Header.EncodeBinary(bw.BinWriter)
|
||||
require.NoError(t, bw.Err)
|
||||
jsonH, err := b.Header.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("Header1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
||||
t.Logf("Header1 JSON: %s", string(jsonH))
|
||||
jsonTxMoveNeo, err := txMoveNeo.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("txMoveNeo hash: %s", txMoveNeo.Hash().StringLE())
|
||||
t.Logf("txMoveNeo JSON: %s", string(jsonTxMoveNeo))
|
||||
t.Logf("txMoveNeo base64: %s", base64.StdEncoding.EncodeToString(txMoveNeo.Bytes()))
|
||||
t.Logf("txMoveGas hash: %s", txMoveGas.Hash().StringLE())
|
||||
|
||||
e.EnsureGASBalance(t, priv0ScriptHash, func(balance *big.Int) bool { return balance.Cmp(big.NewInt(1000*native.GASFactor)) >= 0 })
|
||||
// info for getblockheader rpc tests
|
||||
t.Logf("header hash: %s", b.Hash().StringLE())
|
||||
buf := io.NewBufBinWriter()
|
||||
b.Header.EncodeBinary(buf.BinWriter)
|
||||
t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
|
||||
|
||||
// Block #2: deploy test_contract (Rubles contract).
|
||||
cfgPath := basicChainPrefix + "test_contract.yml"
|
||||
block2H, txDeployH, cHash := deployContractFromPriv0(t, basicChainPrefix+"test_contract.go", "Rubl", cfgPath, 1)
|
||||
t.Logf("txDeploy: %s", txDeployH.StringLE())
|
||||
t.Logf("Block2 hash: %s", block2H.StringLE())
|
||||
|
||||
// Block #3: invoke `putValue` method on the test_contract.
|
||||
rublPriv0Invoker := e.NewInvoker(cHash, acc0)
|
||||
txInvH := rublPriv0Invoker.Invoke(t, true, "putValue", "testkey", "testvalue")
|
||||
t.Logf("txInv: %s", txInvH.StringLE())
|
||||
|
||||
// Block #4: transfer 1000 NEO from priv0 to priv1.
|
||||
neoPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, priv1ScriptHash, 1000, nil)
|
||||
|
||||
// Block #5: initialize rubles contract and transfer 1000 rubles from the contract to priv0.
|
||||
initTx := rublPriv0Invoker.PrepareInvoke(t, "init")
|
||||
transferTx := e.NewUnsignedTx(t, rublPriv0Invoker.Hash, "transfer", cHash, priv0ScriptHash, 1000, nil)
|
||||
e.SignTx(t, transferTx, 1500_0000, acc0) // Set system fee manually to avoid verification failure.
|
||||
e.AddNewBlock(t, initTx, transferTx)
|
||||
e.CheckHalt(t, initTx.Hash(), stackitem.NewBool(true))
|
||||
e.CheckHalt(t, transferTx.Hash(), stackitem.Make(true))
|
||||
t.Logf("receiveRublesTx: %v", transferTx.Hash().StringLE())
|
||||
|
||||
// Block #6: transfer 123 rubles from priv0 to priv1
|
||||
transferTxH := rublPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, priv1ScriptHash, 123, nil)
|
||||
t.Logf("sendRublesTx: %v", transferTxH.StringLE())
|
||||
|
||||
// Block #7: push verification contract into the chain.
|
||||
verifyPath := filepath.Join(basicChainPrefix, "verify", "verification_contract.go")
|
||||
verifyCfg := filepath.Join(basicChainPrefix, "verify", "verification_contract.yml")
|
||||
_, _, _ = deployContractFromPriv0(t, verifyPath, "Verify", verifyCfg, 2)
|
||||
|
||||
// Block #8: deposit some GAS to notary contract for priv0.
|
||||
transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []interface{}{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)})
|
||||
t.Logf("notaryDepositTxPriv0: %v", transferTxH.StringLE())
|
||||
|
||||
// Block #9: designate new Notary node.
|
||||
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
|
||||
designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.P2PNotary), []interface{}{ntr.Accounts[0].PrivateKey().PublicKey().Bytes()})
|
||||
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
|
||||
|
||||
// Block #10: push verification contract with arguments into the chain.
|
||||
verifyPath = filepath.Join(basicChainPrefix, "verify_args", "verification_with_args_contract.go")
|
||||
verifyCfg = filepath.Join(basicChainPrefix, "verify_args", "verification_with_args_contract.yml")
|
||||
_, _, _ = deployContractFromPriv0(t, verifyPath, "VerifyWithArgs", verifyCfg, 3) // block #10
|
||||
|
||||
// Block #11: push NameService contract into the chain.
|
||||
nsPath := examplesPrefix + "nft-nd-nns/"
|
||||
nsConfigPath := nsPath + "nns.yml"
|
||||
_, _, nsHash := deployContractFromPriv0(t, nsPath, nsPath, nsConfigPath, 4) // block #11
|
||||
nsCommitteeInvoker := e.CommitteeInvoker(nsHash)
|
||||
nsPriv0Invoker := e.NewInvoker(nsHash, acc0)
|
||||
|
||||
// Block #12: transfer funds to committee for further NS record registration.
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer",
|
||||
e.Validator.ScriptHash(), e.Committee.ScriptHash(), 1000_00000000, nil) // block #12
|
||||
|
||||
// Block #13: add `.com` root to NNS.
|
||||
nsCommitteeInvoker.Invoke(t, stackitem.Null{}, "addRoot", "com") // block #13
|
||||
|
||||
// Block #14: register `neo.com` via NNS.
|
||||
registerTxH := nsPriv0Invoker.Invoke(t, true, "register",
|
||||
"neo.com", priv0ScriptHash) // block #14
|
||||
res := e.GetTxExecResult(t, registerTxH)
|
||||
require.Equal(t, 1, len(res.Events)) // transfer
|
||||
tokenID, err := res.Events[0].Item.Value().([]stackitem.Item)[3].TryBytes()
|
||||
require.NoError(t, err)
|
||||
t.Logf("NNS token #1 ID (hex): %s", hex.EncodeToString(tokenID))
|
||||
|
||||
// Block #15: set A record type with priv0 owner via NNS.
|
||||
nsPriv0Invoker.Invoke(t, stackitem.Null{}, "setRecord", "neo.com", int64(nns.A), "1.2.3.4") // block #15
|
||||
|
||||
// Block #16: invoke `test_contract.go`: put new value with the same key to check `getstate` RPC call
|
||||
txPutNewValue := rublPriv0Invoker.PrepareInvoke(t, "putValue", "testkey", "newtestvalue") // tx1
|
||||
// Invoke `test_contract.go`: put values to check `findstates` RPC call.
|
||||
txPut1 := rublPriv0Invoker.PrepareInvoke(t, "putValue", "aa", "v1") // tx2
|
||||
txPut2 := rublPriv0Invoker.PrepareInvoke(t, "putValue", "aa10", "v2") // tx3
|
||||
txPut3 := rublPriv0Invoker.PrepareInvoke(t, "putValue", "aa50", "v3") // tx4
|
||||
e.AddNewBlock(t, txPutNewValue, txPut1, txPut2, txPut3) // block #16
|
||||
e.CheckHalt(t, txPutNewValue.Hash(), stackitem.NewBool(true))
|
||||
e.CheckHalt(t, txPut1.Hash(), stackitem.NewBool(true))
|
||||
e.CheckHalt(t, txPut2.Hash(), stackitem.NewBool(true))
|
||||
e.CheckHalt(t, txPut3.Hash(), stackitem.NewBool(true))
|
||||
|
||||
// Block #17: deploy NeoFS Object contract (NEP11-Divisible).
|
||||
nfsPath := examplesPrefix + "nft-d/"
|
||||
nfsConfigPath := nfsPath + "nft.yml"
|
||||
_, _, nfsHash := deployContractFromPriv0(t, nfsPath, nfsPath, nfsConfigPath, 5) // block #17
|
||||
nfsPriv0Invoker := e.NewInvoker(nfsHash, acc0)
|
||||
nfsPriv1Invoker := e.NewInvoker(nfsHash, acc1)
|
||||
|
||||
// Block #18: mint 1.00 NFSO token by transferring 10 GAS to NFSO contract.
|
||||
containerID := util.Uint256{1, 2, 3}
|
||||
objectID := util.Uint256{4, 5, 6}
|
||||
txGas0toNFSH := gasPriv0Invoker.Invoke(t, true, "transfer",
|
||||
priv0ScriptHash, nfsHash, 10_0000_0000, []interface{}{containerID.BytesBE(), objectID.BytesBE()}) // block #18
|
||||
res = e.GetTxExecResult(t, txGas0toNFSH)
|
||||
require.Equal(t, 2, len(res.Events)) // GAS transfer + NFSO transfer
|
||||
tokenID, err = res.Events[1].Item.Value().([]stackitem.Item)[3].TryBytes()
|
||||
require.NoError(t, err)
|
||||
t.Logf("NFSO token #1 ID (hex): %s", hex.EncodeToString(tokenID))
|
||||
|
||||
// Block #19: transfer 0.25 NFSO from priv0 to priv1.
|
||||
nfsPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, priv1ScriptHash, 25, tokenID, nil) // block #19
|
||||
|
||||
// Block #20: transfer 1000 GAS to priv1.
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(),
|
||||
priv1ScriptHash, int64(fixedn.Fixed8FromInt64(1000)), nil) // block #20
|
||||
|
||||
// Block #21: transfer 0.05 NFSO from priv1 back to priv0.
|
||||
nfsPriv1Invoker.Invoke(t, true, "transfer", priv1ScriptHash, priv0ScriptHash, 5, tokenID, nil) // block #21
|
||||
|
||||
// Compile contract to test `invokescript` RPC call
|
||||
invokePath := filepath.Join(basicChainPrefix, "invoke", "invokescript_contract.go")
|
||||
invokeCfg := filepath.Join(basicChainPrefix, "invoke", "invoke.yml")
|
||||
_, _ = newDeployTx(t, e, acc0, invokePath, invokeCfg, false)
|
||||
|
||||
// Prepare some transaction for future submission.
|
||||
txSendRaw := neoPriv0Invoker.PrepareInvoke(t, "transfer", priv0ScriptHash, priv1ScriptHash, int64(fixedn.Fixed8FromInt64(1000)), nil)
|
||||
bw.Reset()
|
||||
txSendRaw.EncodeBinary(bw.BinWriter)
|
||||
t.Logf("sendrawtransaction: \n\tbase64: %s\n\tHash LE: %s", base64.StdEncoding.EncodeToString(bw.Bytes()), txSendRaw.Hash().StringLE())
|
||||
}
|
||||
|
||||
func newDeployTx(t *testing.T, e *neotest.Executor, sender neotest.Signer, sourcePath, configPath string, deploy bool) (util.Uint256, util.Uint160) {
|
||||
c := neotest.CompileFile(t, sender.ScriptHash(), sourcePath, configPath)
|
||||
t.Logf("contract (%s): \n\tHash: %s\n\tAVM: %s", sourcePath, c.Hash.StringLE(), base64.StdEncoding.EncodeToString(c.NEF.Script))
|
||||
if deploy {
|
||||
return e.DeployContractBy(t, sender, c, nil), c.Hash
|
||||
}
|
||||
return util.Uint256{}, c.Hash
|
||||
}
|
|
@ -1,34 +1,33 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"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/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/callflag"
|
||||
"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/require"
|
||||
)
|
||||
|
||||
func BenchmarkVerifyWitness(t *testing.B) {
|
||||
bc := newTestChain(t)
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
|
||||
tx := bc.newTestTx(acc.Contract.ScriptHash(), []byte{byte(opcode.PUSH1)})
|
||||
require.NoError(t, acc.SignTx(netmode.UnitTestNet, tx))
|
||||
func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
|
||||
|
||||
t.ResetTimer()
|
||||
for n := 0; n < t.N; n++ {
|
||||
_, _ = bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
|
||||
_, err := bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,38 +56,35 @@ func BenchmarkBlockchain_ForEachNEP17Transfer(t *testing.B) {
|
|||
|
||||
func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBlock, nBlocksToTake int) {
|
||||
var (
|
||||
nonce uint32 = 1
|
||||
chainHeight = 2_100 // constant chain height to be able to compare paging results
|
||||
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
|
||||
chainHeight = 2_100 // constant chain height to be able to compare paging results
|
||||
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
|
||||
state.TokenTransferBatchSize/32 // shift
|
||||
)
|
||||
|
||||
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
|
||||
gasHash := bc.contracts.GAS.Hash
|
||||
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
|
||||
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||
|
||||
acc := random.Uint160()
|
||||
from := e.Validator.ScriptHash()
|
||||
|
||||
for j := 0; j < chainHeight; j++ {
|
||||
w := io.NewBufBinWriter()
|
||||
for i := 0; i < transfersPerBlock; i++ {
|
||||
emit.AppCall(w.BinWriter, gasHash, "transfer", callflag.All, testchain.MultisigScriptHash(), acc, 1, nil)
|
||||
emit.AppCall(w.BinWriter, gasHash, "transfer", callflag.All, from, acc, 1, nil)
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
require.NoError(t, w.Err)
|
||||
}
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
|
||||
tx.NetworkFee = 1_0000_000
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
tx.Nonce = nonce
|
||||
nonce++
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: testchain.MultisigScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
b := bc.newBlock(tx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, tx.Hash())
|
||||
tx.Nonce = neotest.Nonce()
|
||||
tx.Signers = []transaction.Signer{{Account: from, Scopes: transaction.CalledByEntry}}
|
||||
require.NoError(t, validators.SignTx(netmode.UnitTestNet, tx))
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
}
|
||||
|
||||
newestB, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()) - startFromBlock + 1))
|
||||
|
|
|
@ -2342,6 +2342,14 @@ func (bc *Blockchain) GetMaxVerificationGAS() int64 {
|
|||
return bc.contracts.Policy.GetMaxVerificationGas(bc.dao)
|
||||
}
|
||||
|
||||
// GetMaxNotValidBeforeDelta returns maximum NotValidBeforeDelta Notary limit.
|
||||
func (bc *Blockchain) GetMaxNotValidBeforeDelta() uint32 {
|
||||
if !bc.config.P2PSigExtensions {
|
||||
panic("disallowed call to Notary")
|
||||
}
|
||||
return bc.contracts.Notary.GetMaxNotValidBeforeDelta(bc.dao)
|
||||
}
|
||||
|
||||
// GetStoragePrice returns current storage price.
|
||||
func (bc *Blockchain) GetStoragePrice() int64 {
|
||||
if bc.BlockHeight() == 0 {
|
||||
|
|
322
pkg/core/blockchain_core_test.go
Normal file
322
pkg/core/blockchain_core_test.go
Normal file
|
@ -0,0 +1,322 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
func TestVerifyHeader(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
prev := bc.topBlock.Load().(*block.Block).Header
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("Hash", func(t *testing.T) {
|
||||
h := prev.Hash()
|
||||
h[0] = ^h[0]
|
||||
hdr := newBlock(bc.config, 1, h).Header
|
||||
require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrHashMismatch))
|
||||
})
|
||||
t.Run("Index", func(t *testing.T) {
|
||||
hdr := newBlock(bc.config, 3, prev.Hash()).Header
|
||||
require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrIndexMismatch))
|
||||
})
|
||||
t.Run("Timestamp", func(t *testing.T) {
|
||||
hdr := newBlock(bc.config, 1, prev.Hash()).Header
|
||||
hdr.Timestamp = 0
|
||||
require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrInvalidTimestamp))
|
||||
})
|
||||
})
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
hdr := newBlock(bc.config, 1, prev.Hash()).Header
|
||||
require.NoError(t, bc.verifyHeader(&hdr, &prev))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddBlock(t *testing.T) {
|
||||
const size = 3
|
||||
bc := newTestChain(t)
|
||||
blocks, err := bc.genBlocks(size)
|
||||
require.NoError(t, err)
|
||||
|
||||
lastBlock := blocks[len(blocks)-1]
|
||||
assert.Equal(t, lastBlock.Index, bc.HeaderHeight())
|
||||
assert.Equal(t, lastBlock.Hash(), bc.CurrentHeaderHash())
|
||||
|
||||
// This one tests persisting blocks, so it does need to persist()
|
||||
_, err = bc.persist(false)
|
||||
require.NoError(t, err)
|
||||
|
||||
key := make([]byte, 1+util.Uint256Size)
|
||||
key[0] = byte(storage.DataExecutable)
|
||||
for _, block := range blocks {
|
||||
copy(key[1:], block.Hash().BytesBE())
|
||||
_, err := bc.dao.Store.Get(key)
|
||||
require.NoErrorf(t, err, "block %s not persisted", block.Hash())
|
||||
}
|
||||
|
||||
assert.Equal(t, lastBlock.Index, bc.BlockHeight())
|
||||
assert.Equal(t, lastBlock.Hash(), bc.CurrentHeaderHash())
|
||||
}
|
||||
|
||||
func TestRemoveOldTransfers(t *testing.T) {
|
||||
// Creating proper number of transfers/blocks takes unneccessary time, so emulate
|
||||
// some DB with stale entries.
|
||||
bc := newTestChain(t)
|
||||
h, err := bc.GetHeader(bc.GetHeaderHash(0))
|
||||
require.NoError(t, err)
|
||||
older := h.Timestamp - 1000
|
||||
newer := h.Timestamp + 1000
|
||||
acc1 := util.Uint160{1}
|
||||
acc2 := util.Uint160{2}
|
||||
acc3 := util.Uint160{3}
|
||||
ttl := state.TokenTransferLog{Raw: []byte{1}} // It's incorrect, but who cares.
|
||||
|
||||
for i := uint32(0); i < 3; i++ {
|
||||
bc.dao.PutTokenTransferLog(acc1, older, i, false, &ttl)
|
||||
}
|
||||
for i := uint32(0); i < 3; i++ {
|
||||
bc.dao.PutTokenTransferLog(acc2, newer, i, false, &ttl)
|
||||
}
|
||||
for i := uint32(0); i < 2; i++ {
|
||||
bc.dao.PutTokenTransferLog(acc3, older, i, true, &ttl)
|
||||
}
|
||||
for i := uint32(0); i < 2; i++ {
|
||||
bc.dao.PutTokenTransferLog(acc3, newer, i, true, &ttl)
|
||||
}
|
||||
|
||||
_, err = bc.dao.Persist()
|
||||
require.NoError(t, err)
|
||||
_ = bc.removeOldTransfers(0)
|
||||
|
||||
for i := uint32(0); i < 2; i++ {
|
||||
log, err := bc.dao.GetTokenTransferLog(acc1, older, i, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(log.Raw))
|
||||
}
|
||||
|
||||
log, err := bc.dao.GetTokenTransferLog(acc1, older, 2, false)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, len(log.Raw))
|
||||
|
||||
for i := uint32(0); i < 3; i++ {
|
||||
log, err = bc.dao.GetTokenTransferLog(acc2, newer, i, false)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, len(log.Raw))
|
||||
}
|
||||
|
||||
log, err = bc.dao.GetTokenTransferLog(acc3, older, 0, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(log.Raw))
|
||||
|
||||
log, err = bc.dao.GetTokenTransferLog(acc3, older, 1, true)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, len(log.Raw))
|
||||
|
||||
for i := uint32(0); i < 2; i++ {
|
||||
log, err = bc.dao.GetTokenTransferLog(acc3, newer, i, true)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, len(log.Raw))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) {
|
||||
var (
|
||||
stateSyncInterval = 4
|
||||
maxTraceable uint32 = 6
|
||||
)
|
||||
spountCfg := func(c *config.Config) {
|
||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = true
|
||||
c.ProtocolConfiguration.StateRootInHeader = true
|
||||
c.ProtocolConfiguration.P2PStateExchangeExtensions = true
|
||||
c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval
|
||||
c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable
|
||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
||||
}
|
||||
bcSpout := newTestChainWithCustomCfg(t, spountCfg)
|
||||
|
||||
// Generate some content.
|
||||
for i := 0; i < len(bcSpout.GetConfig().StandbyCommittee); i++ {
|
||||
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
|
||||
}
|
||||
|
||||
// reach next to the latest state sync point and pretend that we've just restored
|
||||
stateSyncPoint := (int(bcSpout.BlockHeight())/stateSyncInterval + 1) * stateSyncInterval
|
||||
for i := bcSpout.BlockHeight() + 1; i <= uint32(stateSyncPoint); i++ {
|
||||
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
|
||||
}
|
||||
require.Equal(t, uint32(stateSyncPoint), bcSpout.BlockHeight())
|
||||
b := bcSpout.newBlock()
|
||||
require.NoError(t, bcSpout.AddHeaders(&b.Header))
|
||||
|
||||
// put storage items with STTemp prefix
|
||||
batch := storage.NewMemCachedStore(bcSpout.dao.Store)
|
||||
tempPrefix := storage.STTempStorage
|
||||
if bcSpout.dao.Version.StoragePrefix == tempPrefix {
|
||||
tempPrefix = storage.STStorage
|
||||
}
|
||||
bPrefix := make([]byte, 1)
|
||||
bPrefix[0] = byte(bcSpout.dao.Version.StoragePrefix)
|
||||
bcSpout.dao.Store.Seek(storage.SeekRange{Prefix: bPrefix}, func(k, v []byte) bool {
|
||||
key := slice.Copy(k)
|
||||
key[0] = byte(tempPrefix)
|
||||
value := slice.Copy(v)
|
||||
batch.Put(key, value)
|
||||
return true
|
||||
})
|
||||
_, err := batch.Persist()
|
||||
require.NoError(t, err)
|
||||
|
||||
checkNewBlockchainErr := func(t *testing.T, cfg func(c *config.Config), store storage.Store, errText string) {
|
||||
unitTestNetCfg, err := config.Load("../../config", testchain.Network())
|
||||
require.NoError(t, err)
|
||||
cfg(&unitTestNetCfg)
|
||||
log := zaptest.NewLogger(t)
|
||||
_, err = NewBlockchain(store, unitTestNetCfg.ProtocolConfiguration, log)
|
||||
if len(errText) != 0 {
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), errText))
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
boltCfg := func(c *config.Config) {
|
||||
spountCfg(c)
|
||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
||||
}
|
||||
// manually store statejump stage to check statejump recover process
|
||||
bPrefix[0] = byte(storage.SYSStateJumpStage)
|
||||
t.Run("invalid RemoveUntraceableBlocks setting", func(t *testing.T) {
|
||||
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)})
|
||||
checkNewBlockchainErr(t, func(c *config.Config) {
|
||||
boltCfg(c)
|
||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = false
|
||||
}, bcSpout.dao.Store, "state jump was not completed, but P2PStateExchangeExtensions are disabled or archival node capability is on")
|
||||
})
|
||||
t.Run("invalid state jump stage format", func(t *testing.T) {
|
||||
bcSpout.dao.Store.Put(bPrefix, []byte{0x01, 0x02})
|
||||
checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state jump stage format")
|
||||
})
|
||||
t.Run("missing state sync point", func(t *testing.T) {
|
||||
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)})
|
||||
checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "failed to get state sync point from the storage")
|
||||
})
|
||||
t.Run("invalid state sync point", func(t *testing.T) {
|
||||
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)})
|
||||
point := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(point, uint32(len(bcSpout.headerHashes)))
|
||||
bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point)
|
||||
checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state sync point")
|
||||
})
|
||||
for _, stage := range []stateJumpStage{stateJumpStarted, newStorageItemsAdded, genesisStateRemoved, 0x03} {
|
||||
t.Run(fmt.Sprintf("state jump stage %d", stage), func(t *testing.T) {
|
||||
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stage)})
|
||||
point := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(point, uint32(stateSyncPoint))
|
||||
bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point)
|
||||
var errText string
|
||||
if stage == 0x03 {
|
||||
errText = "unknown state jump stage"
|
||||
}
|
||||
checkNewBlockchainErr(t, spountCfg, bcSpout.dao.Store, errText)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainWithVolatileNumOfValidators(t *testing.T) {
|
||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||
c.ProtocolConfiguration.ValidatorsCount = 0
|
||||
c.ProtocolConfiguration.CommitteeHistory = map[uint32]int{
|
||||
0: 1,
|
||||
4: 4,
|
||||
24: 6,
|
||||
}
|
||||
c.ProtocolConfiguration.ValidatorsHistory = map[uint32]int{
|
||||
0: 1,
|
||||
4: 4,
|
||||
}
|
||||
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||
})
|
||||
require.Equal(t, uint32(0), bc.BlockHeight())
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
|
||||
vals, err := bc.GetValidators()
|
||||
require.NoError(t, err)
|
||||
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(vals)
|
||||
require.NoError(t, err)
|
||||
curWit := transaction.Witness{
|
||||
VerificationScript: script,
|
||||
}
|
||||
for i := 1; i < 26; i++ {
|
||||
comm, err := bc.GetCommittee()
|
||||
require.NoError(t, err)
|
||||
if i < 5 {
|
||||
require.Equal(t, 1, len(comm))
|
||||
} else if i < 25 {
|
||||
require.Equal(t, 4, len(comm))
|
||||
} else {
|
||||
require.Equal(t, 6, len(comm))
|
||||
}
|
||||
// Mimic consensus.
|
||||
if bc.config.ShouldUpdateCommitteeAt(uint32(i)) {
|
||||
vals, err = bc.GetValidators()
|
||||
} else {
|
||||
vals, err = bc.GetNextBlockValidators()
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if i < 4 {
|
||||
require.Equalf(t, 1, len(vals), "at %d", i)
|
||||
} else {
|
||||
require.Equalf(t, 4, len(vals), "at %d", i)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(vals)
|
||||
require.NoError(t, err)
|
||||
nextWit := transaction.Witness{
|
||||
VerificationScript: script,
|
||||
}
|
||||
b := &block.Block{
|
||||
Header: block.Header{
|
||||
NextConsensus: nextWit.ScriptHash(),
|
||||
Script: curWit,
|
||||
},
|
||||
}
|
||||
curWit = nextWit
|
||||
b.PrevHash = bc.GetHeaderHash(i - 1)
|
||||
b.Timestamp = uint64(time.Now().UTC().Unix())*1000 + uint64(i)
|
||||
b.Index = uint32(i)
|
||||
b.RebuildMerkleRoot()
|
||||
if i < 5 {
|
||||
signa := priv0.SignHashable(uint32(bc.config.Magic), b)
|
||||
b.Script.InvocationScript = append([]byte{byte(opcode.PUSHDATA1), byte(len(signa))}, signa...)
|
||||
} else {
|
||||
b.Script.InvocationScript = testchain.Sign(b)
|
||||
}
|
||||
err = bc.AddBlock(b)
|
||||
require.NoErrorf(t, err, "at %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func setSigner(tx *transaction.Transaction, h util.Uint160) {
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: h,
|
||||
Scopes: transaction.Global,
|
||||
}}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,55 +1,21 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||
"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/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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client/nns"
|
||||
"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/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
// multisig address which possess all NEO.
|
||||
var neoOwner = testchain.MultisigScriptHash()
|
||||
|
||||
// examplesPrefix is a prefix of the example smart-contracts.
|
||||
const examplesPrefix = "../../examples/"
|
||||
|
||||
// newTestChain should be called before newBlock invocation to properly setup
|
||||
// global state.
|
||||
func newTestChain(t testing.TB) *Blockchain {
|
||||
|
@ -67,38 +33,6 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c
|
|||
return chain
|
||||
}
|
||||
|
||||
func newLevelDBForTesting(t testing.TB) storage.Store {
|
||||
newLevelStore, _ := newLevelDBForTestingWithPath(t, "")
|
||||
return newLevelStore
|
||||
}
|
||||
|
||||
func newLevelDBForTestingWithPath(t testing.TB, dbPath string) (storage.Store, string) {
|
||||
if dbPath == "" {
|
||||
dbPath = t.TempDir()
|
||||
}
|
||||
dbOptions := storage.LevelDBOptions{
|
||||
DataDirectoryPath: dbPath,
|
||||
}
|
||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||
require.Nil(t, err, "NewLevelDBStore error")
|
||||
return newLevelStore, dbPath
|
||||
}
|
||||
|
||||
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
||||
boltDBStore, _ := newBoltStoreForTestingWithPath(t, "")
|
||||
return boltDBStore
|
||||
}
|
||||
|
||||
func newBoltStoreForTestingWithPath(t testing.TB, dbPath string) (storage.Store, string) {
|
||||
if dbPath == "" {
|
||||
d := t.TempDir()
|
||||
dbPath = filepath.Join(d, "test_bolt_db")
|
||||
}
|
||||
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
|
||||
require.NoError(t, err)
|
||||
return boltDBStore, dbPath
|
||||
}
|
||||
|
||||
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
||||
chain, err := initTestChainNoCheck(t, st, f)
|
||||
require.NoError(t, err)
|
||||
|
@ -192,646 +126,3 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
|
|||
}
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func TestBug1728(t *testing.T) {
|
||||
src := `package example
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
func init() { if true { } else { } }
|
||||
func _deploy(_ interface{}, isUpdate bool) {
|
||||
runtime.Log("Deploy")
|
||||
}`
|
||||
nf, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"})
|
||||
require.NoError(t, err)
|
||||
|
||||
rawManifest, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
rawNef, err := nf.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
bc := newTestChain(t)
|
||||
|
||||
aer, err := invokeContractMethod(bc, 10000000000,
|
||||
bc.contracts.Management.Hash, "deploy", rawNef, rawManifest)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, aer.VMState, vm.HaltState)
|
||||
}
|
||||
|
||||
// This function generates "../rpc/testdata/testblocks.acc" file which contains data
|
||||
// for RPC unit tests. It also is a nice integration test.
|
||||
// To generate new "../rpc/testdata/testblocks.acc", follow the steps:
|
||||
// 1. Set saveChain down below to true
|
||||
// 2. Run tests with `$ make test`
|
||||
func TestCreateBasicChain(t *testing.T) {
|
||||
const saveChain = false
|
||||
const prefix = "../rpc/server/testdata/"
|
||||
|
||||
bc := newTestChain(t)
|
||||
initBasicChain(t, bc)
|
||||
|
||||
if saveChain {
|
||||
outStream, err := os.Create(prefix + "testblocks.acc")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
outStream.Close()
|
||||
})
|
||||
|
||||
writer := io.NewBinWriterFromIO(outStream)
|
||||
writer.WriteU32LE(bc.BlockHeight())
|
||||
err = chaindump.Dump(bc, writer, 1, bc.BlockHeight())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
priv1 := testchain.PrivateKeyByID(1)
|
||||
priv0ScriptHash := priv0.GetScriptHash()
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||
|
||||
// Prepare some transaction for future submission.
|
||||
txSendRaw := newNEP17Transfer(bc.contracts.NEO.Hash, priv0ScriptHash, priv1.GetScriptHash(), int64(fixedn.Fixed8FromInt64(1000)))
|
||||
txSendRaw.ValidUntilBlock = bc.config.MaxValidUntilBlockIncrement
|
||||
txSendRaw.Nonce = 0x1234
|
||||
txSendRaw.Signers = []transaction.Signer{{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
require.NoError(t, addNetworkFee(bc, txSendRaw, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txSendRaw))
|
||||
bw := io.NewBufBinWriter()
|
||||
txSendRaw.EncodeBinary(bw.BinWriter)
|
||||
t.Logf("sendrawtransaction: \n\tbase64: %s\n\tHash LE: %s", base64.StdEncoding.EncodeToString(bw.Bytes()), txSendRaw.Hash().StringLE())
|
||||
require.False(t, saveChain)
|
||||
}
|
||||
|
||||
func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||
const prefix = "../rpc/server/testdata/"
|
||||
// Increase in case if you need more blocks
|
||||
const validUntilBlock = 1200
|
||||
|
||||
// To be incremented after each created transaction to keep chain constant.
|
||||
var testNonce uint32 = 1
|
||||
|
||||
// Use as nonce when new transaction is created to avoid random data in tests.
|
||||
getNextNonce := func() uint32 {
|
||||
testNonce++
|
||||
return testNonce
|
||||
}
|
||||
|
||||
const neoAmount = 99999000
|
||||
|
||||
gasHash := bc.contracts.GAS.Hash
|
||||
neoHash := bc.contracts.NEO.Hash
|
||||
policyHash := bc.contracts.Policy.Hash
|
||||
notaryHash := bc.contracts.Notary.Hash
|
||||
t.Logf("native GAS hash: %v", gasHash)
|
||||
t.Logf("native NEO hash: %v", neoHash)
|
||||
t.Logf("native Policy hash: %v", policyHash)
|
||||
t.Logf("native Notary hash: %v", notaryHash)
|
||||
t.Logf("Block0 hash: %s", bc.GetHeaderHash(0).StringLE())
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
priv0ScriptHash := priv0.GetScriptHash()
|
||||
priv1 := testchain.PrivateKeyByID(1)
|
||||
priv1ScriptHash := priv1.GetScriptHash()
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||
acc1 := wallet.NewAccountFromPrivateKey(priv1)
|
||||
|
||||
deployContractFromPriv0 := func(t *testing.T, path, contractName string, configPath *string, expectedID int32) (util.Uint256, util.Uint256, util.Uint160) {
|
||||
txDeploy, _ := newDeployTx(t, bc, priv0ScriptHash, path, contractName, configPath)
|
||||
txDeploy.Nonce = getNextNonce()
|
||||
txDeploy.ValidUntilBlock = validUntilBlock
|
||||
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy))
|
||||
b := bc.newBlock(txDeploy)
|
||||
require.NoError(t, bc.AddBlock(b)) // block #11
|
||||
checkTxHalt(t, bc, txDeploy.Hash())
|
||||
sh, err := bc.GetContractScriptHash(expectedID)
|
||||
require.NoError(t, err)
|
||||
return b.Hash(), txDeploy.Hash(), sh
|
||||
}
|
||||
|
||||
require.Equal(t, big.NewInt(5000_0000), bc.GetUtilityTokenBalance(priv0ScriptHash)) // gas bounty
|
||||
|
||||
// Block #1: move 1000 GAS and neoAmount NEO to priv0.
|
||||
txMoveNeo, err := testchain.NewTransferFromOwner(bc, neoHash, priv0ScriptHash, neoAmount, getNextNonce(), validUntilBlock)
|
||||
require.NoError(t, err)
|
||||
// Move some GAS to one simple account.
|
||||
txMoveGas, err := testchain.NewTransferFromOwner(bc, gasHash, priv0ScriptHash, int64(fixedn.Fixed8FromInt64(1000)),
|
||||
getNextNonce(), validUntilBlock)
|
||||
require.NoError(t, err)
|
||||
b := bc.newBlock(txMoveNeo, txMoveGas)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, txMoveGas.Hash())
|
||||
checkTxHalt(t, bc, txMoveNeo.Hash())
|
||||
t.Logf("Block1 hash: %s", b.Hash().StringLE())
|
||||
bw := io.NewBufBinWriter()
|
||||
b.EncodeBinary(bw.BinWriter)
|
||||
require.NoError(t, bw.Err)
|
||||
jsonB, err := b.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("Block1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
||||
t.Logf("Block1 JSON: %s", string(jsonB))
|
||||
bw.Reset()
|
||||
b.Header.EncodeBinary(bw.BinWriter)
|
||||
require.NoError(t, bw.Err)
|
||||
jsonH, err := b.Header.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("Header1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
||||
t.Logf("Header1 JSON: %s", string(jsonH))
|
||||
jsonTxMoveNeo, err := txMoveNeo.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
t.Logf("txMoveNeo hash: %s", txMoveNeo.Hash().StringLE())
|
||||
t.Logf("txMoveNeo JSON: %s", string(jsonTxMoveNeo))
|
||||
t.Logf("txMoveNeo base64: %s", base64.StdEncoding.EncodeToString(txMoveNeo.Bytes()))
|
||||
t.Logf("txMoveGas hash: %s", txMoveGas.Hash().StringLE())
|
||||
|
||||
require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash).Cmp(big.NewInt(1000*native.GASFactor)) >= 0)
|
||||
// info for getblockheader rpc tests
|
||||
t.Logf("header hash: %s", b.Hash().StringLE())
|
||||
buf := io.NewBufBinWriter()
|
||||
b.Header.EncodeBinary(buf.BinWriter)
|
||||
t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
|
||||
|
||||
// Block #2: deploy test_contract.
|
||||
cfgPath := prefix + "test_contract.yml"
|
||||
block2H, txDeployH, cHash := deployContractFromPriv0(t, prefix+"test_contract.go", "Rubl", &cfgPath, 1)
|
||||
t.Logf("txDeploy: %s", txDeployH.StringLE())
|
||||
t.Logf("Block2 hash: %s", block2H.StringLE())
|
||||
|
||||
// Block #3: invoke `putValue` method on the test_contract.
|
||||
script := io.NewBufBinWriter()
|
||||
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "testvalue")
|
||||
txInv := transaction.New(script.Bytes(), 1*native.GASFactor)
|
||||
txInv.Nonce = getNextNonce()
|
||||
txInv.ValidUntilBlock = validUntilBlock
|
||||
txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||
require.NoError(t, addNetworkFee(bc, txInv, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txInv))
|
||||
b = bc.newBlock(txInv)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, txInv.Hash())
|
||||
t.Logf("txInv: %s", txInv.Hash().StringLE())
|
||||
|
||||
// Block #4: transfer 0.0000_1 NEO from priv0 to priv1.
|
||||
txNeo0to1 := newNEP17Transfer(neoHash, priv0ScriptHash, priv1ScriptHash, 1000)
|
||||
txNeo0to1.Nonce = getNextNonce()
|
||||
txNeo0to1.ValidUntilBlock = validUntilBlock
|
||||
txNeo0to1.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, txNeo0to1, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txNeo0to1))
|
||||
b = bc.newBlock(txNeo0to1)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, txNeo0to1.Hash())
|
||||
|
||||
// Block #5: initialize rubles contract and transfer 1000 rubles from the contract to priv0.
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, cHash, "init", callflag.All)
|
||||
initTx := transaction.New(w.Bytes(), 1*native.GASFactor)
|
||||
initTx.Nonce = getNextNonce()
|
||||
initTx.ValidUntilBlock = validUntilBlock
|
||||
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||
require.NoError(t, addNetworkFee(bc, initTx, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), initTx))
|
||||
transferTx := newNEP17Transfer(cHash, cHash, priv0ScriptHash, 1000)
|
||||
transferTx.Nonce = getNextNonce()
|
||||
transferTx.ValidUntilBlock = validUntilBlock
|
||||
transferTx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
||||
transferTx.SystemFee += 1000000
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
||||
b = bc.newBlock(initTx, transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, initTx.Hash())
|
||||
checkTxHalt(t, bc, transferTx.Hash())
|
||||
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
|
||||
|
||||
// Block #6: transfer 123 rubles from priv0 to priv1
|
||||
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1ScriptHash, 123)
|
||||
transferTx.Nonce = getNextNonce()
|
||||
transferTx.ValidUntilBlock = validUntilBlock
|
||||
transferTx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
||||
transferTx.SystemFee += 1000000
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
||||
b = bc.newBlock(transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, transferTx.Hash())
|
||||
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
||||
|
||||
// Block #7: push verification contract into the chain.
|
||||
verifyPath := filepath.Join(prefix, "verify", "verification_contract.go")
|
||||
_, _, _ = deployContractFromPriv0(t, verifyPath, "Verify", nil, 2)
|
||||
|
||||
// Block #8: deposit some GAS to notary contract for priv0.
|
||||
transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000))
|
||||
transferTx.Nonce = getNextNonce()
|
||||
transferTx.ValidUntilBlock = validUntilBlock
|
||||
transferTx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
||||
transferTx.SystemFee += 10_0000
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
||||
b = bc.newBlock(transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
checkTxHalt(t, bc, transferTx.Hash())
|
||||
t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE())
|
||||
|
||||
// Block #9: designate new Notary node.
|
||||
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
|
||||
bc.setNodesByRole(t, true, noderoles.P2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()})
|
||||
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
|
||||
|
||||
// Block #10: push verification contract with arguments into the chain.
|
||||
verifyPath = filepath.Join(prefix, "verify_args", "verification_with_args_contract.go")
|
||||
_, _, _ = deployContractFromPriv0(t, verifyPath, "VerifyWithArgs", nil, 3) // block #10
|
||||
|
||||
// Block #11: push NameService contract into the chain.
|
||||
nsPath := examplesPrefix + "nft-nd-nns/"
|
||||
nsConfigPath := nsPath + "nns.yml"
|
||||
_, _, nsHash := deployContractFromPriv0(t, nsPath, nsPath, &nsConfigPath, 4) // block #11
|
||||
|
||||
// Block #12: transfer funds to committee for futher NS record registration.
|
||||
transferFundsToCommittee(t, bc) // block #12
|
||||
|
||||
// Block #13: add `.com` root to NNS.
|
||||
res, err := invokeContractMethodGeneric(bc, -1,
|
||||
nsHash, "addRoot", true, "com") // block #13
|
||||
require.NoError(t, err)
|
||||
checkResult(t, res, stackitem.Null{})
|
||||
|
||||
// Block #14: register `neo.com` via NNS.
|
||||
res, err = invokeContractMethodGeneric(bc, -1,
|
||||
nsHash, "register", acc0, "neo.com", priv0ScriptHash) // block #14
|
||||
require.NoError(t, err)
|
||||
checkResult(t, res, stackitem.NewBool(true))
|
||||
require.Equal(t, 1, len(res.Events)) // transfer
|
||||
tokenID, err := res.Events[0].Item.Value().([]stackitem.Item)[3].TryBytes()
|
||||
require.NoError(t, err)
|
||||
t.Logf("NNS token #1 ID (hex): %s", hex.EncodeToString(tokenID))
|
||||
|
||||
// Block #15: set A record type with priv0 owner via NNS.
|
||||
res, err = invokeContractMethodGeneric(bc, -1, nsHash,
|
||||
"setRecord", acc0, "neo.com", int64(nns.A), "1.2.3.4") // block #15
|
||||
require.NoError(t, err)
|
||||
checkResult(t, res, stackitem.Null{})
|
||||
|
||||
// Block #16: invoke `test_contract.go`: put new value with the same key to check `getstate` RPC call
|
||||
script.Reset()
|
||||
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "newtestvalue")
|
||||
// Invoke `test_contract.go`: put values to check `findstates` RPC call
|
||||
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa", "v1")
|
||||
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa10", "v2")
|
||||
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa50", "v3")
|
||||
txInv = transaction.New(script.Bytes(), 1*native.GASFactor)
|
||||
txInv.Nonce = getNextNonce()
|
||||
txInv.ValidUntilBlock = validUntilBlock
|
||||
txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||
require.NoError(t, addNetworkFee(bc, txInv, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txInv))
|
||||
b = bc.newBlock(txInv)
|
||||
require.NoError(t, bc.AddBlock(b)) // block #16
|
||||
checkTxHalt(t, bc, txInv.Hash())
|
||||
|
||||
// Block #17: deploy NeoFS Object contract (NEP11-Divisible).
|
||||
nfsPath := examplesPrefix + "nft-d/"
|
||||
nfsConfigPath := nfsPath + "nft.yml"
|
||||
_, _, nfsHash := deployContractFromPriv0(t, nfsPath, nfsPath, &nfsConfigPath, 5) // block #17
|
||||
|
||||
// Block #18: mint 1.00 NFSO token by transferring 10 GAS to NFSO contract.
|
||||
containerID := util.Uint256{1, 2, 3}
|
||||
objectID := util.Uint256{4, 5, 6}
|
||||
txGas0toNFS := newNEP17Transfer(gasHash, priv0ScriptHash, nfsHash, 10_0000_0000, containerID.BytesBE(), objectID.BytesBE())
|
||||
txGas0toNFS.SystemFee += 4000_0000
|
||||
txGas0toNFS.Nonce = getNextNonce()
|
||||
txGas0toNFS.ValidUntilBlock = validUntilBlock
|
||||
txGas0toNFS.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, txGas0toNFS, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txGas0toNFS))
|
||||
b = bc.newBlock(txGas0toNFS)
|
||||
require.NoError(t, bc.AddBlock(b)) // block #18
|
||||
checkTxHalt(t, bc, txGas0toNFS.Hash())
|
||||
aer, _ := bc.GetAppExecResults(txGas0toNFS.Hash(), trigger.Application)
|
||||
require.Equal(t, 2, len(aer[0].Events)) // GAS transfer + NFSO transfer
|
||||
tokenID, err = aer[0].Events[1].Item.Value().([]stackitem.Item)[3].TryBytes()
|
||||
require.NoError(t, err)
|
||||
t.Logf("NFSO token #1 ID (hex): %s", hex.EncodeToString(tokenID))
|
||||
|
||||
// Block #19: transfer 0.25 NFSO from priv0 to priv1.
|
||||
script.Reset()
|
||||
emit.AppCall(script.BinWriter, nfsHash, "transfer", callflag.All, priv0ScriptHash, priv1ScriptHash, 25, tokenID, nil)
|
||||
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
||||
require.NoError(t, script.Err)
|
||||
txNFS0to1 := transaction.New(script.Bytes(), 1*native.GASFactor)
|
||||
txNFS0to1.Nonce = getNextNonce()
|
||||
txNFS0to1.ValidUntilBlock = validUntilBlock
|
||||
txNFS0to1.Signers = []transaction.Signer{{Account: priv0ScriptHash, Scopes: transaction.CalledByEntry}}
|
||||
require.NoError(t, addNetworkFee(bc, txNFS0to1, acc0))
|
||||
require.NoError(t, acc0.SignTx(testchain.Network(), txNFS0to1))
|
||||
b = bc.newBlock(txNFS0to1)
|
||||
require.NoError(t, bc.AddBlock(b)) // block #19
|
||||
checkTxHalt(t, bc, txNFS0to1.Hash())
|
||||
|
||||
// Block #20: transfer 1000 GAS to priv1.
|
||||
txMoveGas, err = testchain.NewTransferFromOwner(bc, gasHash, priv1ScriptHash, int64(fixedn.Fixed8FromInt64(1000)),
|
||||
getNextNonce(), validUntilBlock)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock(txMoveGas)))
|
||||
checkTxHalt(t, bc, txMoveGas.Hash()) // block #20
|
||||
|
||||
// Block #21: transfer 0.05 NFSO from priv1 back to priv0.
|
||||
script.Reset()
|
||||
emit.AppCall(script.BinWriter, nfsHash, "transfer", callflag.All, priv1ScriptHash, priv0.GetScriptHash(), 5, tokenID, nil)
|
||||
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
||||
require.NoError(t, script.Err)
|
||||
txNFS1to0 := transaction.New(script.Bytes(), 1*native.GASFactor)
|
||||
txNFS1to0.Nonce = getNextNonce()
|
||||
txNFS1to0.ValidUntilBlock = validUntilBlock
|
||||
txNFS1to0.Signers = []transaction.Signer{{Account: priv1ScriptHash, Scopes: transaction.CalledByEntry}}
|
||||
require.NoError(t, addNetworkFee(bc, txNFS1to0, acc0))
|
||||
require.NoError(t, acc1.SignTx(testchain.Network(), txNFS1to0))
|
||||
b = bc.newBlock(txNFS1to0)
|
||||
require.NoError(t, bc.AddBlock(b)) // block #21
|
||||
checkTxHalt(t, bc, txNFS1to0.Hash())
|
||||
|
||||
// Compile contract to test `invokescript` RPC call
|
||||
invokePath := filepath.Join(prefix, "invoke", "invokescript_contract.go")
|
||||
invokeCfg := filepath.Join(prefix, "invoke", "invoke.yml")
|
||||
_, _ = newDeployTx(t, bc, priv0ScriptHash, invokePath, "ContractForInvokescriptTest", &invokeCfg)
|
||||
}
|
||||
|
||||
func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
|
||||
return newNEP17TransferWithAssert(sc, from, to, amount, true, additionalArgs...)
|
||||
}
|
||||
|
||||
func newNEP17TransferWithAssert(sc, from, to util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, sc, "transfer", callflag.All, from, to, amount, additionalArgs)
|
||||
if needAssert {
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
}
|
||||
if w.Err != nil {
|
||||
panic(fmt.Errorf("failed to create NEP-17 transfer transaction: %w", w.Err))
|
||||
}
|
||||
|
||||
script := w.Bytes()
|
||||
return transaction.New(script, 11000000)
|
||||
}
|
||||
|
||||
func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string, cfgName *string) (*transaction.Transaction, util.Uint160) {
|
||||
tx, h, avm, err := testchain.NewDeployTx(bc, name, sender, nil, cfgName)
|
||||
require.NoError(t, err)
|
||||
t.Logf("contract (%s): \n\tHash: %s\n\tAVM: %s", name, h.StringLE(), base64.StdEncoding.EncodeToString(avm))
|
||||
return tx, h
|
||||
}
|
||||
|
||||
func addSigners(sender util.Uint160, txs ...*transaction.Transaction) {
|
||||
for _, tx := range txs {
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: sender,
|
||||
Scopes: transaction.Global,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.Account) error {
|
||||
size := io.GetVarSize(tx)
|
||||
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), sender.Contract.Script)
|
||||
tx.NetworkFee += netFee
|
||||
size += sizeDelta
|
||||
for _, cosigner := range tx.Signers {
|
||||
contract := bc.GetContractState(cosigner.Account)
|
||||
if contract != nil {
|
||||
netFee, sizeDelta = fee.Calculate(bc.GetBaseExecFee(), contract.NEF.Script)
|
||||
tx.NetworkFee += netFee
|
||||
size += sizeDelta
|
||||
}
|
||||
}
|
||||
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Signer can be either bool or *wallet.Account.
|
||||
// In the first case `true` means sign by committee, `false` means sign by validators.
|
||||
func prepareContractMethodInvokeGeneric(chain *Blockchain, sysfee int64,
|
||||
hash util.Uint160, method string, signer interface{}, args ...interface{}) (*transaction.Transaction, error) {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
|
||||
if w.Err != nil {
|
||||
return nil, w.Err
|
||||
}
|
||||
script := w.Bytes()
|
||||
tx := transaction.New(script, 0)
|
||||
tx.ValidUntilBlock = chain.blockHeight + 1
|
||||
var err error
|
||||
switch s := signer.(type) {
|
||||
case bool:
|
||||
if s {
|
||||
addSigners(testchain.CommitteeScriptHash(), tx)
|
||||
setTxSystemFee(chain, sysfee, tx)
|
||||
err = testchain.SignTxCommittee(chain, tx)
|
||||
} else {
|
||||
addSigners(neoOwner, tx)
|
||||
setTxSystemFee(chain, sysfee, tx)
|
||||
err = testchain.SignTx(chain, tx)
|
||||
}
|
||||
case *wallet.Account:
|
||||
signTxWithAccounts(chain, sysfee, tx, s)
|
||||
case []*wallet.Account:
|
||||
signTxWithAccounts(chain, sysfee, tx, s...)
|
||||
default:
|
||||
panic("invalid signer")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func setTxSystemFee(bc *Blockchain, sysFee int64, tx *transaction.Transaction) {
|
||||
if sysFee >= 0 {
|
||||
tx.SystemFee = sysFee
|
||||
return
|
||||
}
|
||||
|
||||
lastBlock := bc.topBlock.Load().(*block.Block)
|
||||
b := &block.Block{
|
||||
Header: block.Header{
|
||||
Index: lastBlock.Index + 1,
|
||||
Timestamp: lastBlock.Timestamp + 1000,
|
||||
},
|
||||
Transactions: []*transaction.Transaction{tx},
|
||||
}
|
||||
|
||||
ttx := *tx // prevent setting 'hash' field
|
||||
ic := bc.GetTestVM(trigger.Application, &ttx, b)
|
||||
defer ic.Finalize()
|
||||
|
||||
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
||||
_ = ic.VM.Run()
|
||||
tx.SystemFee = ic.VM.GasConsumed()
|
||||
}
|
||||
|
||||
func signTxWithAccounts(chain *Blockchain, sysFee int64, tx *transaction.Transaction, accs ...*wallet.Account) {
|
||||
scope := transaction.CalledByEntry
|
||||
for _, acc := range accs {
|
||||
accH, _ := address.StringToUint160(acc.Address)
|
||||
tx.Signers = append(tx.Signers, transaction.Signer{
|
||||
Account: accH,
|
||||
Scopes: scope,
|
||||
})
|
||||
scope = transaction.Global
|
||||
}
|
||||
setTxSystemFee(chain, sysFee, tx)
|
||||
size := io.GetVarSize(tx)
|
||||
for _, acc := range accs {
|
||||
if acc.Contract.Deployed {
|
||||
// don't need precise calculation for tests
|
||||
tx.NetworkFee += 1000_0000
|
||||
continue
|
||||
}
|
||||
netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc.Contract.Script)
|
||||
size += sizeDelta
|
||||
tx.NetworkFee += netFee
|
||||
}
|
||||
tx.NetworkFee += int64(size) * chain.FeePerByte()
|
||||
|
||||
for _, acc := range accs {
|
||||
if err := acc.SignTx(testchain.Network(), tx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
|
||||
b := chain.newBlock(txs...)
|
||||
err := chain.AddBlock(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aers := make([]*state.AppExecResult, len(txs))
|
||||
for i, tx := range txs {
|
||||
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aers[i] = &res[0]
|
||||
}
|
||||
return aers, nil
|
||||
}
|
||||
|
||||
func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
||||
return invokeContractMethodGeneric(chain, sysfee, hash, method, false, args...)
|
||||
}
|
||||
|
||||
func invokeContractMethodGeneric(chain *Blockchain, sysfee int64, hash util.Uint160, method string,
|
||||
signer interface{}, args ...interface{}) (*state.AppExecResult, error) {
|
||||
tx, err := prepareContractMethodInvokeGeneric(chain, sysfee, hash,
|
||||
method, signer, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aers, err := persistBlock(chain, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aers[0], nil
|
||||
}
|
||||
|
||||
func transferTokenFromMultisigAccountCheckOK(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) {
|
||||
transferTx := transferTokenFromMultisigAccount(t, chain, to, tokenHash, amount, additionalArgs...)
|
||||
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, res[0].VMState)
|
||||
require.Equal(t, 0, len(res[0].Stack))
|
||||
}
|
||||
|
||||
func transferTokenFromMultisigAccount(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
|
||||
return transferTokenFromMultisigAccountWithAssert(t, chain, to, tokenHash, amount, true, additionalArgs...)
|
||||
}
|
||||
|
||||
func transferTokenFromMultisigAccountWithAssert(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
|
||||
transferTx := newNEP17TransferWithAssert(tokenHash, testchain.MultisigScriptHash(), to, amount, needAssert, additionalArgs...)
|
||||
transferTx.SystemFee = 100000000
|
||||
transferTx.ValidUntilBlock = chain.BlockHeight() + 1
|
||||
addSigners(neoOwner, transferTx)
|
||||
require.NoError(t, testchain.SignTx(chain, transferTx))
|
||||
b := chain.newBlock(transferTx)
|
||||
require.NoError(t, chain.AddBlock(b))
|
||||
return transferTx
|
||||
}
|
||||
|
||||
func checkResult(t *testing.T, result *state.AppExecResult, expected stackitem.Item) {
|
||||
require.Equal(t, vm.HaltState, result.VMState, result.FaultException)
|
||||
require.Equal(t, 1, len(result.Stack))
|
||||
require.Equal(t, expected, result.Stack[0])
|
||||
}
|
||||
|
||||
func checkTxHalt(t testing.TB, bc *Blockchain, h util.Uint256) {
|
||||
aer, err := bc.GetAppExecResults(h, trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(aer))
|
||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
||||
}
|
||||
|
||||
func checkFAULTState(t *testing.T, result *state.AppExecResult) {
|
||||
require.Equal(t, vm.FaultState, result.VMState)
|
||||
}
|
||||
|
||||
func checkBalanceOf(t *testing.T, chain *Blockchain, addr util.Uint160, expected int) {
|
||||
balance := chain.GetUtilityTokenBalance(addr)
|
||||
require.Equal(t, int64(expected), balance.Int64())
|
||||
}
|
||||
|
||||
type NotaryFeerStub struct {
|
||||
bc blockchainer.Blockchainer
|
||||
}
|
||||
|
||||
func (f NotaryFeerStub) FeePerByte() int64 { return f.bc.FeePerByte() }
|
||||
func (f NotaryFeerStub) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
|
||||
return f.bc.GetNotaryBalance(acc)
|
||||
}
|
||||
func (f NotaryFeerStub) BlockHeight() uint32 { return f.bc.BlockHeight() }
|
||||
func (f NotaryFeerStub) P2PSigExtensionsEnabled() bool { return f.bc.P2PSigExtensionsEnabled() }
|
||||
func NewNotaryFeerStub(bc blockchainer.Blockchainer) NotaryFeerStub {
|
||||
return NotaryFeerStub{
|
||||
bc: bc,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
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"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
||||
|
@ -29,7 +25,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
|
@ -39,10 +34,11 @@ 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"
|
||||
)
|
||||
|
||||
var pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
|
||||
|
||||
// Tests are taken from
|
||||
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
|
||||
func TestRuntimeGetRandomCompatibility(t *testing.T) {
|
||||
|
@ -72,29 +68,6 @@ func TestRuntimeGetRandomCompatibility(t *testing.T) {
|
|||
require.Equal(t, "217172703763162599519098299724476526911", ic.VM.Estack().Pop().BigInt().String())
|
||||
}
|
||||
|
||||
func TestRuntimeGetRandomDifferentTransactions(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
b, _ := bc.GetBlock(bc.GetHeaderHash(0))
|
||||
|
||||
tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
ic1 := bc.newInteropContext(trigger.Application, bc.dao.GetWrapped(), b, tx1)
|
||||
ic1.VM = vm.New()
|
||||
ic1.VM.LoadScript(tx1.Script)
|
||||
|
||||
tx2 := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
|
||||
ic2 := bc.newInteropContext(trigger.Application, bc.dao.GetWrapped(), b, tx2)
|
||||
ic2.VM = vm.New()
|
||||
ic2.VM.LoadScript(tx2.Script)
|
||||
|
||||
require.NoError(t, runtime.GetRandom(ic1))
|
||||
require.NoError(t, runtime.GetRandom(ic2))
|
||||
require.NotEqual(t, ic1.VM.Estack().Pop().BigInt(), ic2.VM.Estack().Pop().BigInt())
|
||||
|
||||
require.NoError(t, runtime.GetRandom(ic1))
|
||||
require.NoError(t, runtime.GetRandom(ic2))
|
||||
require.NotEqual(t, ic1.VM.Estack().Pop().BigInt(), ic2.VM.Estack().Pop().BigInt())
|
||||
}
|
||||
|
||||
func getSharpTestTx(sender util.Uint160) *transaction.Transaction {
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
|
||||
tx.Nonce = 0
|
||||
|
@ -116,81 +89,6 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
|
|||
return b
|
||||
}
|
||||
|
||||
func TestContractCreateAccount(t *testing.T) {
|
||||
v, ic, _ := createVM(t)
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pub := priv.PublicKey()
|
||||
v.Estack().PushVal(pub.Bytes())
|
||||
require.NoError(t, contractCreateStandardAccount(ic))
|
||||
|
||||
value := v.Estack().Pop().Bytes()
|
||||
u, err := util.Uint160DecodeBytesBE(value)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pub.GetScriptHash(), u)
|
||||
})
|
||||
t.Run("InvalidKey", func(t *testing.T) {
|
||||
v.Estack().PushVal([]byte{1, 2, 3})
|
||||
require.Error(t, contractCreateStandardAccount(ic))
|
||||
})
|
||||
}
|
||||
|
||||
func TestContractCreateMultisigAccount(t *testing.T) {
|
||||
v, ic, _ := createVM(t)
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
m, n := 3, 5
|
||||
pubs := make(keys.PublicKeys, n)
|
||||
arr := make([]stackitem.Item, n)
|
||||
for i := range pubs {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pubs[i] = pk.PublicKey()
|
||||
arr[i] = stackitem.Make(pubs[i].Bytes())
|
||||
}
|
||||
v.Estack().PushVal(stackitem.Make(arr))
|
||||
v.Estack().PushVal(m)
|
||||
require.NoError(t, contractCreateMultisigAccount(ic))
|
||||
|
||||
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Bytes()
|
||||
u, err := util.Uint160DecodeBytesBE(value)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hash.Hash160(expected), u)
|
||||
})
|
||||
t.Run("InvalidKey", func(t *testing.T) {
|
||||
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make([]byte{1, 2, 3})}))
|
||||
v.Estack().PushVal(1)
|
||||
require.Error(t, contractCreateMultisigAccount(ic))
|
||||
})
|
||||
t.Run("Invalid m", func(t *testing.T) {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
|
||||
v.Estack().PushVal(2)
|
||||
require.Error(t, contractCreateMultisigAccount(ic))
|
||||
})
|
||||
t.Run("m overflows int64", func(t *testing.T) {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
|
||||
m := big.NewInt(math.MaxInt64)
|
||||
m.Add(m, big.NewInt(1))
|
||||
v.Estack().PushVal(stackitem.NewBigInteger(m))
|
||||
require.Error(t, contractCreateMultisigAccount(ic))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRuntimeGasLeft(t *testing.T) {
|
||||
v, ic, _ := createVM(t)
|
||||
|
||||
v.GasLimit = 100
|
||||
v.AddGas(58)
|
||||
require.NoError(t, runtime.GasLeft(ic))
|
||||
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestRuntimeGetNotifications(t *testing.T) {
|
||||
v, ic, _ := createVM(t)
|
||||
|
||||
|
@ -235,7 +133,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 +559,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 +580,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))
|
||||
|
||||
|
@ -1496,80 +968,3 @@ 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
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT0", neoOwner.BytesBE())
|
||||
require.NoError(t, err)
|
||||
realBalance, _ := bc.GetGoverningTokenBalance(neoOwner)
|
||||
checkResult(t, aer, stackitem.Make(realBalance.Int64()+1))
|
||||
})
|
||||
t.Run("invalid param count", func(t *testing.T) {
|
||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT2")
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, aer)
|
||||
})
|
||||
t.Run("invalid contract", func(t *testing.T) {
|
||||
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT1")
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, aer)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRuntimeGetNetwork(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork)
|
||||
require.NoError(t, w.Err)
|
||||
|
||||
tx := transaction.New(w.Bytes(), 10_000)
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
addSigners(neoOwner, tx)
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
|
||||
|
||||
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
checkResult(t, &aer[0], stackitem.Make(uint32(bc.config.Magic)))
|
||||
}
|
||||
|
||||
func TestRuntimeBurnGas(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
const sysFee = 2_000000
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", int64(1))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, aer.VMState)
|
||||
|
||||
t.Run("gas limit exceeded", func(t *testing.T) {
|
||||
aer, err = invokeContractMethod(bc, aer.GasConsumed, cs.Hash, "burnGas", int64(2))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.FaultState, aer.VMState)
|
||||
})
|
||||
})
|
||||
t.Run("too big integer", func(t *testing.T) {
|
||||
gas := big.NewInt(math.MaxInt64)
|
||||
gas.Add(gas, big.NewInt(1))
|
||||
|
||||
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", gas)
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, aer)
|
||||
})
|
||||
t.Run("zero GAS", func(t *testing.T) {
|
||||
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", int64(0))
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, aer)
|
||||
})
|
||||
}
|
245
pkg/core/interop_system_neotest_test.go
Normal file
245
pkg/core/interop_system_neotest_test.go
Normal file
|
@ -0,0 +1,245 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"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/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSystemRuntimeGetRandom_DifferentTransactions(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetRandom)
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
|
||||
tx1 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
|
||||
tx2 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
|
||||
e.AddNewBlock(t, tx1, tx2)
|
||||
e.CheckHalt(t, tx1.Hash())
|
||||
e.CheckHalt(t, tx2.Hash())
|
||||
|
||||
res1 := e.GetTxExecResult(t, tx1.Hash())
|
||||
res2 := e.GetTxExecResult(t, tx2.Hash())
|
||||
|
||||
r1, err := res1.Stack[0].TryInteger()
|
||||
require.NoError(t, err)
|
||||
r2, err := res2.Stack[0].TryInteger()
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, r1, r2)
|
||||
}
|
||||
|
||||
func TestSystemContractCreateStandardAccount(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
w := io.NewBufBinWriter()
|
||||
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pub := priv.PublicKey()
|
||||
|
||||
emit.Bytes(w.BinWriter, pub.Bytes())
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
|
||||
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
|
||||
res := e.GetTxExecResult(t, tx.Hash())
|
||||
value := res.Stack[0].Value().([]byte)
|
||||
u, err := util.Uint160DecodeBytesBE(value)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pub.GetScriptHash(), u)
|
||||
})
|
||||
t.Run("InvalidKey", func(t *testing.T) {
|
||||
w.Reset()
|
||||
emit.Bytes(w.BinWriter, []byte{1, 2, 3})
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
|
||||
require.NoError(t, w.Err)
|
||||
script := w.Bytes()
|
||||
|
||||
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckFault(t, tx.Hash(), "invalid prefix 1")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSystemContractCreateMultisigAccount(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
w := io.NewBufBinWriter()
|
||||
|
||||
createScript := func(t *testing.T, pubs []interface{}, m int) []byte {
|
||||
w.Reset()
|
||||
emit.Array(w.BinWriter, pubs...)
|
||||
emit.Int(w.BinWriter, int64(m))
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
|
||||
require.NoError(t, w.Err)
|
||||
return w.Bytes()
|
||||
}
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
m, n := 3, 5
|
||||
pubs := make(keys.PublicKeys, n)
|
||||
arr := make([]interface{}, n)
|
||||
for i := range pubs {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pubs[i] = pk.PublicKey()
|
||||
arr[i] = pubs[i].Bytes()
|
||||
}
|
||||
script := createScript(t, arr, m)
|
||||
|
||||
txH := e.InvokeScript(t, script, []neotest.Signer{acc})
|
||||
e.CheckHalt(t, txH)
|
||||
res := e.GetTxExecResult(t, txH)
|
||||
value := res.Stack[0].Value().([]byte)
|
||||
u, err := util.Uint160DecodeBytesBE(value)
|
||||
require.NoError(t, err)
|
||||
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hash.Hash160(expected), u)
|
||||
})
|
||||
t.Run("InvalidKey", func(t *testing.T) {
|
||||
script := createScript(t, []interface{}{[]byte{1, 2, 3}}, 1)
|
||||
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "invalid prefix 1")
|
||||
})
|
||||
t.Run("Invalid m", func(t *testing.T) {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
script := createScript(t, []interface{}{pk.PublicKey().Bytes()}, 2)
|
||||
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "length of the signatures (2) is higher then the number of public keys")
|
||||
})
|
||||
t.Run("m overflows int32", func(t *testing.T) {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
m := big.NewInt(math.MaxInt32)
|
||||
m.Add(m, big.NewInt(1))
|
||||
w.Reset()
|
||||
emit.Array(w.BinWriter, pk.Bytes())
|
||||
emit.BigInt(w.BinWriter, m)
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
|
||||
require.NoError(t, w.Err)
|
||||
e.InvokeScriptCheckFAULT(t, w.Bytes(), []neotest.Signer{acc}, "m must be positive and fit int32")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSystemRuntimeGasLeft(t *testing.T) {
|
||||
const runtimeGasLeftPrice = 1 << 4
|
||||
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
w := io.NewBufBinWriter()
|
||||
|
||||
gasLimit := 1100
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
|
||||
require.NoError(t, w.Err)
|
||||
tx := transaction.New(w.Bytes(), int64(gasLimit))
|
||||
tx.Nonce = neotest.Nonce()
|
||||
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
||||
e.SignTx(t, tx, int64(gasLimit), acc)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
res := e.GetTxExecResult(t, tx.Hash())
|
||||
l1 := res.Stack[0].Value().(*big.Int)
|
||||
l2 := res.Stack[1].Value().(*big.Int)
|
||||
|
||||
require.Equal(t, int64(gasLimit-runtimeGasLeftPrice*interop.DefaultBaseExecFee), l1.Int64())
|
||||
require.Equal(t, int64(gasLimit-2*runtimeGasLeftPrice*interop.DefaultBaseExecFee), l2.Int64())
|
||||
}
|
||||
|
||||
func TestLoadToken(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||
|
||||
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash())
|
||||
rawManifest, err := json.Marshal(cs.Manifest)
|
||||
require.NoError(t, err)
|
||||
rawNef, err := cs.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
cInvoker := e.ValidatorInvoker(cs.Hash)
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
realBalance, _ := bc.GetGoverningTokenBalance(acc.ScriptHash())
|
||||
cInvoker.Invoke(t, stackitem.NewBigInteger(big.NewInt(realBalance.Int64()+1)), "callT0", acc.ScriptHash())
|
||||
})
|
||||
t.Run("invalid param count", func(t *testing.T) {
|
||||
cInvoker.InvokeFail(t, "method not found: callT2/1", "callT2", acc.ScriptHash())
|
||||
})
|
||||
t.Run("invalid contract", func(t *testing.T) {
|
||||
cInvoker.InvokeFail(t, "token contract 0000000000000000000000000000000000000000 not found: key not found", "callT1")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSystemRuntimeGetNetwork(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
w := io.NewBufBinWriter()
|
||||
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork)
|
||||
require.NoError(t, w.Err)
|
||||
e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(bc.GetConfig().Magic))))
|
||||
}
|
||||
|
||||
func TestSystemRuntimeBurnGas(t *testing.T) {
|
||||
bc, acc := chain.NewSingle(t)
|
||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||
|
||||
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash())
|
||||
rawManifest, err := json.Marshal(cs.Manifest)
|
||||
require.NoError(t, err)
|
||||
rawNef, err := cs.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
cInvoker := e.ValidatorInvoker(cs.Hash)
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
h := cInvoker.Invoke(t, stackitem.Null{}, "burnGas", int64(1))
|
||||
res := e.GetTxExecResult(t, h)
|
||||
|
||||
t.Run("gas limit exceeded", func(t *testing.T) {
|
||||
tx := e.NewUnsignedTx(t, cs.Hash, "burnGas", int64(2))
|
||||
e.SignTx(t, tx, res.GasConsumed, acc)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckFault(t, tx.Hash(), "GAS limit exceeded")
|
||||
})
|
||||
})
|
||||
t.Run("too big integer", func(t *testing.T) {
|
||||
gas := big.NewInt(math.MaxInt64)
|
||||
gas.Add(gas, big.NewInt(1))
|
||||
|
||||
cInvoker.InvokeFail(t, "invalid GAS value", "burnGas", gas)
|
||||
})
|
||||
t.Run("zero GAS", func(t *testing.T) {
|
||||
cInvoker.InvokeFail(t, "GAS must be positive", "burnGas", int64(0))
|
||||
})
|
||||
}
|
|
@ -17,14 +17,15 @@ func Call(ic *interop.Context) error {
|
|||
return fmt.Errorf("native contract of version %d is not active", version)
|
||||
}
|
||||
var c interop.Contract
|
||||
curr := ic.VM.GetCurrentScriptHash()
|
||||
for _, ctr := range ic.Natives {
|
||||
if ctr.Metadata().Hash == ic.VM.GetCurrentScriptHash() {
|
||||
if ctr.Metadata().Hash == curr {
|
||||
c = ctr
|
||||
break
|
||||
}
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("native contract %d not found", version)
|
||||
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
||||
}
|
||||
history := c.Metadata().UpdateHistory
|
||||
if len(history) == 0 {
|
||||
|
|
|
@ -200,7 +200,7 @@ func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stac
|
|||
|
||||
gas := ic.Chain.GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes))
|
||||
if isDeploy {
|
||||
fee := m.GetMinimumDeploymentFee(ic.DAO)
|
||||
fee := m.minimumDeploymentFee(ic.DAO)
|
||||
if fee > gas {
|
||||
gas = fee
|
||||
}
|
||||
|
@ -400,11 +400,11 @@ func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error {
|
|||
}
|
||||
|
||||
func (m *Management) getMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
return stackitem.NewBigInteger(big.NewInt(m.GetMinimumDeploymentFee(ic.DAO)))
|
||||
return stackitem.NewBigInteger(big.NewInt(m.minimumDeploymentFee(ic.DAO)))
|
||||
}
|
||||
|
||||
// GetMinimumDeploymentFee returns the minimum required fee for contract deploy.
|
||||
func (m *Management) GetMinimumDeploymentFee(dao *dao.Simple) int64 {
|
||||
// minimumDeploymentFee returns the minimum required fee for contract deploy.
|
||||
func (m *Management) minimumDeploymentFee(dao *dao.Simple) int64 {
|
||||
return getIntWithKey(m.ID, dao, keyMinimumDeploymentFee)
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok
|
|||
|
||||
func checkNodeRoles(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, index uint32, res keys.PublicKeys) {
|
||||
if ok {
|
||||
designateInvoker.InvokeAndCheck(t, func(t *testing.T, stack []stackitem.Item) {
|
||||
designateInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||
require.Equal(t, 1, len(stack))
|
||||
arr := stack[0].Value().([]stackitem.Item)
|
||||
require.Equal(t, len(res), len(arr))
|
||||
|
|
|
@ -82,7 +82,7 @@ func TestLedger_GetTransactionFromBlock(t *testing.T) {
|
|||
ledgerInvoker.Invoke(t, e.Chain.BlockHeight(), "currentIndex") // Adds a block.
|
||||
b := e.GetBlockByIndex(t, int(e.Chain.BlockHeight()))
|
||||
|
||||
check := func(t *testing.T, stack []stackitem.Item) {
|
||||
check := func(t testing.TB, stack []stackitem.Item) {
|
||||
require.Equal(t, 1, len(stack))
|
||||
actual, ok := stack[0].Value().([]stackitem.Item)
|
||||
require.True(t, ok)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -86,9 +86,7 @@ func newOracle() *Oracle {
|
|||
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)}
|
||||
defer o.UpdateHash()
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, o.Hash, "finish", callflag.All)
|
||||
o.oracleScript = w.Bytes()
|
||||
o.oracleScript = CreateOracleResponseScript(o.Hash)
|
||||
|
||||
desc := newDescriptor("request", smartcontract.VoidType,
|
||||
manifest.NewParameter("url", smartcontract.StringType),
|
||||
|
@ -526,3 +524,14 @@ func (o *Oracle) updateCache(d *dao.Simple) error {
|
|||
orc.AddRequests(reqs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOracleResponseScript returns script that is used to create native Oracle
|
||||
// response.
|
||||
func CreateOracleResponseScript(nativeOracleHash util.Uint160) []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, nativeOracleHash, "finish", callflag.All)
|
||||
if w.Err != nil {
|
||||
panic(fmt.Errorf("failed to create Oracle response script: %w", w.Err))
|
||||
}
|
||||
return w.Bytes()
|
||||
}
|
||||
|
|
|
@ -1,323 +1,164 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testNative struct {
|
||||
meta interop.ContractMD
|
||||
blocks chan uint32
|
||||
}
|
||||
|
||||
func (tn *testNative) Initialize(_ *interop.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tn *testNative) Metadata() *interop.ContractMD {
|
||||
return &tn.meta
|
||||
}
|
||||
|
||||
func (tn *testNative) OnPersist(ic *interop.Context) error {
|
||||
select {
|
||||
case tn.blocks <- ic.Block.Index:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("can't send index")
|
||||
}
|
||||
}
|
||||
|
||||
func (tn *testNative) PostPersist(ic *interop.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ interop.Contract = (*testNative)(nil)
|
||||
|
||||
// registerNative registers native contract in the blockchain.
|
||||
func (bc *Blockchain) registerNative(c interop.Contract) {
|
||||
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
|
||||
bc.config.NativeUpdateHistories[c.Metadata().Name] = c.Metadata().UpdateHistory
|
||||
}
|
||||
|
||||
const (
|
||||
testSumCPUFee = 1 << 15 // same as contract.Call
|
||||
testSumStorageFee = 200
|
||||
)
|
||||
|
||||
func newTestNative() *testNative {
|
||||
cMD := interop.NewContractMD("Test.Native.Sum", 0)
|
||||
cMD.UpdateHistory = []uint32{0}
|
||||
tn := &testNative{
|
||||
meta: *cMD,
|
||||
blocks: make(chan uint32, 1),
|
||||
}
|
||||
defer tn.meta.UpdateHash()
|
||||
|
||||
desc := &manifest.Method{
|
||||
Name: "sum",
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("addend1", smartcontract.IntegerType),
|
||||
manifest.NewParameter("addend2", smartcontract.IntegerType),
|
||||
},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
}
|
||||
md := &interop.MethodAndPrice{
|
||||
Func: tn.sum,
|
||||
CPUFee: testSumCPUFee,
|
||||
StorageFee: testSumStorageFee,
|
||||
RequiredFlags: callflag.NoneFlag,
|
||||
}
|
||||
tn.meta.AddMethod(md, desc)
|
||||
|
||||
desc = &manifest.Method{
|
||||
Name: "callOtherContractNoReturn",
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("method", smartcontract.StringType),
|
||||
manifest.NewParameter("arg", smartcontract.ArrayType),
|
||||
},
|
||||
ReturnType: smartcontract.VoidType,
|
||||
Safe: true,
|
||||
}
|
||||
md = &interop.MethodAndPrice{
|
||||
Func: tn.callOtherContractNoReturn,
|
||||
CPUFee: testSumCPUFee,
|
||||
RequiredFlags: callflag.NoneFlag}
|
||||
tn.meta.AddMethod(md, desc)
|
||||
|
||||
desc = &manifest.Method{
|
||||
Name: "callOtherContractWithReturn",
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("method", smartcontract.StringType),
|
||||
manifest.NewParameter("arg", smartcontract.ArrayType),
|
||||
},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
}
|
||||
md = &interop.MethodAndPrice{
|
||||
Func: tn.callOtherContractWithReturn,
|
||||
CPUFee: testSumCPUFee,
|
||||
RequiredFlags: callflag.NoneFlag}
|
||||
tn.meta.AddMethod(md, desc)
|
||||
|
||||
return tn
|
||||
}
|
||||
|
||||
func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
s1, err := args[0].TryInteger()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s2, err := args[1].TryInteger()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return stackitem.NewBigInteger(s1.Add(s1, s2))
|
||||
}
|
||||
|
||||
func toUint160(item stackitem.Item) util.Uint160 {
|
||||
bs, err := item.TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
u, err := util.Uint160DecodeBytesBE(bs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, hasReturn bool) {
|
||||
cs, err := ic.GetContract(toUint160(args[0]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bs, err := args[1].TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = contract.CallFromNative(ic, tn.meta.Hash, cs, string(bs), args[2].Value().([]stackitem.Item), hasReturn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tn *testNative) callOtherContractNoReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
tn.call(ic, args, false)
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
func (tn *testNative) callOtherContractWithReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
tn.call(ic, args, true)
|
||||
bi := ic.VM.Estack().Pop().BigInt()
|
||||
return stackitem.Make(bi.Add(bi, big.NewInt(1)))
|
||||
}
|
||||
|
||||
func TestNativeContract_Invoke(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
const (
|
||||
transferCPUFee = 1 << 17
|
||||
transferStorageFee = 50
|
||||
systemContractCallPrice = 1 << 15
|
||||
)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||
|
||||
tn := newTestNative()
|
||||
chain.registerNative(tn)
|
||||
baseExecFee := bc.GetBaseExecFee()
|
||||
price := fee.Opcode(baseExecFee, opcode.SYSCALL, // System.Contract.Call
|
||||
opcode.PUSHDATA1, // contract hash (20 byte)
|
||||
opcode.PUSHDATA1, // method
|
||||
opcode.PUSH15, // call flags
|
||||
// `transfer` args:
|
||||
opcode.PUSHDATA1, // from
|
||||
opcode.PUSHDATA1, // to
|
||||
opcode.PUSH1, // amount
|
||||
opcode.PUSHNULL, // data
|
||||
// end args
|
||||
opcode.PUSH4, // amount of args
|
||||
opcode.PACK, // pack args
|
||||
)
|
||||
price += systemContractCallPrice*baseExecFee + // System.Contract.Call price
|
||||
transferCPUFee*baseExecFee + // `transfer` itself
|
||||
transferStorageFee*bc.GetStoragePrice() // `transfer` storage price
|
||||
|
||||
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
ID: 1,
|
||||
NEF: tn.meta.NEF,
|
||||
Hash: tn.meta.Hash,
|
||||
Manifest: tn.meta.Manifest,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
||||
e.SignTx(t, tx, -1, validator)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
|
||||
|
||||
// System.Contract.Call + "sum" itself + opcodes for pushing arguments.
|
||||
price := int64(testSumCPUFee * chain.GetBaseExecFee() * 2)
|
||||
price += testSumStorageFee * chain.GetStoragePrice()
|
||||
price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8)
|
||||
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL, opcode.PUSHDATA1, opcode.PUSHINT8)
|
||||
price += fee.Opcode(chain.GetBaseExecFee(), opcode.PACK)
|
||||
res, err := invokeContractMethod(chain, price, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||
require.NoError(t, err)
|
||||
checkResult(t, res, stackitem.Make(42))
|
||||
_, err = chain.persist(false)
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case index := <-tn.blocks:
|
||||
require.Equal(t, chain.blockHeight, index)
|
||||
default:
|
||||
require.Fail(t, "onPersist wasn't called")
|
||||
}
|
||||
|
||||
// Enough for Call and other opcodes, but not enough for "sum" call.
|
||||
res, err = invokeContractMethod(chain, price-1, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||
require.NoError(t, err)
|
||||
checkFAULTState(t, res)
|
||||
// Enough for Call and other opcodes, but not enough for "transfer" call.
|
||||
tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
||||
e.SignTx(t, tx, price-1, validator)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckFault(t, tx.Hash(), "gas limit exceeded")
|
||||
}
|
||||
|
||||
func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
|
||||
tn := newTestNative()
|
||||
chain.registerNative(tn)
|
||||
|
||||
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
ID: 1,
|
||||
NEF: tn.meta.NEF,
|
||||
Manifest: tn.meta.Manifest,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
||||
ic := chain.newInteropContext(trigger.Application, d, nil, nil)
|
||||
|
||||
sumOffset := 0
|
||||
for _, md := range tn.Metadata().Methods {
|
||||
if md.MD.Name == "sum" {
|
||||
sumOffset = md.MD.Offset
|
||||
break
|
||||
}
|
||||
}
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib))
|
||||
require.NotNil(t, clState)
|
||||
md := clState.Manifest.ABI.GetMethod("ripemd160", 1)
|
||||
require.NotNil(t, md)
|
||||
|
||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
||||
ic := bc.GetTestVM(trigger.Application, nil, nil)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, util.Uint160{1, 2, 3}, callflag.All)
|
||||
v.Estack().PushVal(14)
|
||||
v.Estack().PushVal(28)
|
||||
v.Context().Jump(sumOffset)
|
||||
fakeH := util.Uint160{1, 2, 3}
|
||||
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
|
||||
input := []byte{1, 2, 3, 4}
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
// it's prohibited to call natives directly
|
||||
require.Error(t, v.Run())
|
||||
// Bad current script hash
|
||||
err := v.Run()
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
|
||||
})
|
||||
|
||||
t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) {
|
||||
tn.Metadata().UpdateHistory = []uint32{chain.blockHeight + 1}
|
||||
bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
|
||||
c.NativeUpdateHistories = map[string][]uint32{
|
||||
nativenames.Policy: {0},
|
||||
nativenames.Neo: {0},
|
||||
nativenames.Gas: {0},
|
||||
nativenames.Designation: {0},
|
||||
nativenames.StdLib: {0},
|
||||
nativenames.Management: {0},
|
||||
nativenames.Oracle: {0},
|
||||
nativenames.Ledger: {0},
|
||||
nativenames.CryptoLib: {1},
|
||||
}
|
||||
})
|
||||
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
|
||||
|
||||
ic := bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
|
||||
v.Estack().PushVal(14)
|
||||
v.Estack().PushVal(28)
|
||||
v.Context().Jump(sumOffset)
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
input := []byte{1, 2, 3, 4}
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
// it's prohibited to call natives before NativeUpdateHistory[0] height
|
||||
require.Error(t, v.Run())
|
||||
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
||||
err := v.Run()
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
||||
|
||||
// set the value back to 0
|
||||
tn.Metadata().UpdateHistory = []uint32{0}
|
||||
// Add new block => CryptoLib should be active now.
|
||||
eBad.AddNewBlock(t)
|
||||
ic = bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
v = ic.SpawnVM()
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
require.NoError(t, v.Run())
|
||||
value := v.Estack().Pop().Bytes()
|
||||
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
ic := bc.GetTestVM(trigger.Application, nil, nil)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
|
||||
v.Estack().PushVal(14)
|
||||
v.Estack().PushVal(28)
|
||||
v.Context().Jump(sumOffset)
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
|
||||
input := []byte{1, 2, 3, 4}
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
require.NoError(t, v.Run())
|
||||
|
||||
value := v.Estack().Pop().BigInt()
|
||||
require.Equal(t, int64(42), value.Int64())
|
||||
value := v.Estack().Pop().Bytes()
|
||||
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||
gasInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
||||
|
||||
tn := newTestNative()
|
||||
chain.registerNative(tn)
|
||||
|
||||
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||
ContractBase: state.ContractBase{
|
||||
ID: 1,
|
||||
Hash: tn.meta.Hash,
|
||||
NEF: tn.meta.NEF,
|
||||
Manifest: tn.meta.Manifest,
|
||||
},
|
||||
})
|
||||
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash())
|
||||
cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
||||
manifB, err := json.Marshal(cs.Manifest)
|
||||
require.NoError(t, err)
|
||||
nefB, err := cs.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
si, err := cs.ToStackItem()
|
||||
require.NoError(t, err)
|
||||
managementInvoker.Invoke(t, si, "deploy", nefB, manifB)
|
||||
|
||||
var drainTN = func(t *testing.T) {
|
||||
select {
|
||||
case <-tn.blocks:
|
||||
default:
|
||||
require.Fail(t, "testNative didn't send us block")
|
||||
}
|
||||
}
|
||||
|
||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, cs))
|
||||
|
||||
baseFee := chain.GetBaseExecFee()
|
||||
t.Run("non-native, no return", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(chain, testSumCPUFee*baseFee*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{})
|
||||
require.NoError(t, err)
|
||||
drainTN(t)
|
||||
require.Equal(t, vm.HaltState, res.VMState, res.FaultException)
|
||||
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
|
||||
})
|
||||
t.Run("non-native, with return", func(t *testing.T) {
|
||||
res, err := invokeContractMethod(chain, testSumCPUFee*baseFee*4+10000, tn.Metadata().Hash,
|
||||
"callOtherContractWithReturn", cs.Hash, "ret7", []interface{}{})
|
||||
require.NoError(t, err)
|
||||
drainTN(t)
|
||||
checkResult(t, res, stackitem.Make(8))
|
||||
// `onNEP17Payment` will be invoked on test contract from GAS contract.
|
||||
gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,69 +10,11 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (bc *Blockchain) setNodesByRole(t *testing.T, ok bool, r noderoles.Role, nodes keys.PublicKeys) {
|
||||
w := io.NewBufBinWriter()
|
||||
for _, pub := range nodes {
|
||||
emit.Bytes(w.BinWriter, pub.Bytes())
|
||||
}
|
||||
emit.Int(w.BinWriter, int64(len(nodes)))
|
||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||
emit.Int(w.BinWriter, int64(r))
|
||||
emit.Int(w.BinWriter, 2)
|
||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||
emit.AppCallNoArgs(w.BinWriter, bc.contracts.Designate.Hash, "designateAsRole", callflag.All)
|
||||
require.NoError(t, w.Err)
|
||||
tx := transaction.New(w.Bytes(), 0)
|
||||
tx.NetworkFee = 10_000_000
|
||||
tx.SystemFee = 10_000_000
|
||||
tx.ValidUntilBlock = 100
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: testchain.MultisigScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
{
|
||||
Account: testchain.CommitteeScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
}
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
tx.Scripts = append(tx.Scripts, transaction.Witness{
|
||||
InvocationScript: testchain.SignCommittee(tx),
|
||||
VerificationScript: testchain.CommitteeVerificationScript(),
|
||||
})
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
|
||||
|
||||
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(aer))
|
||||
if ok {
|
||||
require.Equal(t, vm.HaltState, aer[0].VMState)
|
||||
require.Equal(t, 1, len(aer[0].Events))
|
||||
|
||||
ev := aer[0].Events[0]
|
||||
require.Equal(t, bc.contracts.Designate.Hash, ev.ScriptHash)
|
||||
require.Equal(t, native.DesignationEventName, ev.Name)
|
||||
require.Equal(t, []stackitem.Item{
|
||||
stackitem.Make(int64(r)),
|
||||
stackitem.Make(bc.BlockHeight()),
|
||||
}, ev.Item.Value().([]stackitem.Item))
|
||||
} else {
|
||||
require.Equal(t, vm.FaultState, aer[0].VMState)
|
||||
require.Equal(t, 0, len(aer[0].Events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
|
||||
|
|
|
@ -1,39 +1,33 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type memoryStore struct {
|
||||
*storage.MemoryStore
|
||||
}
|
||||
|
||||
func (memoryStore) Close() error { return nil }
|
||||
|
||||
func TestMinimumDeploymentFee(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
|
||||
t.Run("get, internal method", func(t *testing.T) {
|
||||
n := chain.contracts.Management.GetMinimumDeploymentFee(chain.dao)
|
||||
require.Equal(t, 10_00000000, int(n))
|
||||
})
|
||||
}
|
||||
|
||||
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||
t.Run("empty chain", func(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash}, chain.contracts.Management.GetNEP17Contracts())
|
||||
bc, validators, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
|
||||
require.ElementsMatch(t, []util.Uint160{e.NativeHash(t, nativenames.Neo),
|
||||
e.NativeHash(t, nativenames.Gas)}, bc.GetNEP17Contracts())
|
||||
})
|
||||
|
||||
t.Run("test chain", func(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
initBasicChain(t, chain)
|
||||
rublesHash, err := chain.GetContractScriptHash(1)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash, rublesHash}, chain.contracts.Management.GetNEP17Contracts())
|
||||
t.Run("basic chain", func(t *testing.T) {
|
||||
bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
|
||||
c.P2PSigExtensions = true // `initBasicChain` requires Notary enabled
|
||||
})
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
initBasicChain(t, e)
|
||||
|
||||
require.ElementsMatch(t, []util.Uint160{e.NativeHash(t, nativenames.Neo),
|
||||
e.NativeHash(t, nativenames.Gas), e.ContractHash(t, 1)}, bc.GetNEP17Contracts())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -40,96 +37,88 @@ func BenchmarkNEO_GetGASPerVote(t *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
|
||||
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
|
||||
|
||||
neo := bc.contracts.NEO
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, tx)
|
||||
ic.SpawnVM()
|
||||
ic.Block = bc.newBlock(tx)
|
||||
|
||||
advanceChain := func(t *testing.B, count int) {
|
||||
for i := 0; i < count; i++ {
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
ic.Block.Index++
|
||||
}
|
||||
func newLevelDBForTesting(t testing.TB) storage.Store {
|
||||
dbPath := t.TempDir()
|
||||
dbOptions := storage.LevelDBOptions{
|
||||
DataDirectoryPath: dbPath,
|
||||
}
|
||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||
require.Nil(t, err, "NewLevelDBStore error")
|
||||
return newLevelStore
|
||||
}
|
||||
|
||||
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
||||
d := t.TempDir()
|
||||
dbPath := filepath.Join(d, "test_bolt_db")
|
||||
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
|
||||
require.NoError(t, err)
|
||||
return boltDBStore
|
||||
}
|
||||
|
||||
func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
|
||||
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
|
||||
cfg := bc.GetConfig()
|
||||
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
neoHash := e.NativeHash(t, nativenames.Neo)
|
||||
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||
neoSuperInvoker := e.NewInvoker(neoHash, validators, committee)
|
||||
neoValidatorsInvoker := e.ValidatorInvoker(neoHash)
|
||||
gasValidatorsInvoker := e.ValidatorInvoker(gasHash)
|
||||
|
||||
// Vote for new committee.
|
||||
sz := testchain.CommitteeSize()
|
||||
accs := make([]*wallet.Account, sz)
|
||||
sz := len(cfg.StandbyCommittee)
|
||||
voters := make([]*wallet.Account, sz)
|
||||
candidates := make(keys.PublicKeys, sz)
|
||||
txs := make([]*transaction.Transaction, 0, len(accs))
|
||||
txs := make([]*transaction.Transaction, 0, len(voters)*3)
|
||||
for i := 0; i < sz; i++ {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
candidates[i] = priv.PublicKey()
|
||||
accs[i], err = wallet.NewAccount()
|
||||
voters[i], err = wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[i]))
|
||||
registerTx := neoSuperInvoker.PrepareInvoke(t, "registerCandidate", candidates[i].Bytes())
|
||||
txs = append(txs, registerTx)
|
||||
|
||||
to := accs[i].Contract.ScriptHash()
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All,
|
||||
neoOwner.BytesBE(), to.BytesBE(),
|
||||
big.NewInt(int64(sz-i)*1000000).Int64(), nil)
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
emit.AppCall(w.BinWriter, bc.contracts.GAS.Hash, "transfer", callflag.All,
|
||||
neoOwner.BytesBE(), to.BytesBE(),
|
||||
int64(1_000_000_000), nil)
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
require.NoError(t, w.Err)
|
||||
tx := transaction.New(w.Bytes(), 1000_000_000)
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
setSigner(tx, testchain.MultisigScriptHash())
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
txs = append(txs, tx)
|
||||
to := voters[i].Contract.ScriptHash()
|
||||
transferNeoTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, big.NewInt(int64(sz-i)*1000000).Int64(), nil)
|
||||
txs = append(txs, transferNeoTx)
|
||||
|
||||
transferGasTx := gasValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, int64(1_000_000_000), nil)
|
||||
txs = append(txs, transferGasTx)
|
||||
}
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock(txs...)))
|
||||
e.AddNewBlock(t, txs...)
|
||||
for _, tx := range txs {
|
||||
checkTxHalt(t, bc, tx.Hash())
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
}
|
||||
voteTxs := make([]*transaction.Transaction, 0, sz)
|
||||
for i := 0; i < sz; i++ {
|
||||
priv := accs[i].PrivateKey()
|
||||
priv := voters[i].PrivateKey()
|
||||
h := priv.GetScriptHash()
|
||||
setSigner(tx, h)
|
||||
ic.VM.Load(priv.PublicKey().GetVerificationScript())
|
||||
require.NoError(t, neo.VoteInternal(ic, h, candidates[i]))
|
||||
voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
|
||||
voteTxs = append(voteTxs, voteTx)
|
||||
}
|
||||
e.AddNewBlock(t, voteTxs...)
|
||||
for _, tx := range voteTxs {
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
}
|
||||
_, err := ic.DAO.Persist()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Collect set of nRewardRecords reward records for each voter.
|
||||
advanceChain(t, nRewardRecords*testchain.CommitteeSize())
|
||||
e.GenerateNewBlocks(t, len(cfg.StandbyCommittee))
|
||||
|
||||
// Transfer some more NEO to first voter to update his balance height.
|
||||
to := accs[0].Contract.ScriptHash()
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All,
|
||||
neoOwner.BytesBE(), to.BytesBE(), int64(1), nil)
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
require.NoError(t, w.Err)
|
||||
tx = transaction.New(w.Bytes(), 1000_000_000)
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
setSigner(tx, testchain.MultisigScriptHash())
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
|
||||
|
||||
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(aer))
|
||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
||||
to := voters[0].Contract.ScriptHash()
|
||||
neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), to, int64(1), nil)
|
||||
|
||||
// Advance chain one more time to avoid same start/end rewarding bounds.
|
||||
advanceChain(t, rewardDistance)
|
||||
e.GenerateNewBlocks(t, rewardDistance)
|
||||
end := bc.BlockHeight()
|
||||
|
||||
t.ResetTimer()
|
||||
t.ReportAllocs()
|
||||
t.StartTimer()
|
||||
for i := 0; i < t.N; i++ {
|
||||
_, err := neo.CalculateBonus(ic.DAO, to, end)
|
||||
_, err := bc.CalculateClaimable(to, end)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.StopTimer()
|
||||
|
|
|
@ -1,54 +1,67 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"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/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func transferFundsToCommittee(t *testing.T, chain *Blockchain) {
|
||||
transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(),
|
||||
chain.contracts.GAS.Hash, 1000_00000000)
|
||||
}
|
||||
|
||||
func TestFeePerByte(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
func TestPolicy_FeePerByte(t *testing.T) {
|
||||
bc, _, _ := chain.NewMulti(t)
|
||||
|
||||
t.Run("get, internal method", func(t *testing.T) {
|
||||
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
|
||||
n := bc.FeePerByte()
|
||||
require.Equal(t, 1000, int(n))
|
||||
})
|
||||
}
|
||||
|
||||
func TestExecFeeFactor(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
func TestPolicy_ExecFeeFactor(t *testing.T) {
|
||||
bc, _, _ := chain.NewMulti(t)
|
||||
|
||||
t.Run("get, internal method", func(t *testing.T) {
|
||||
n := chain.contracts.Policy.GetExecFeeFactorInternal(chain.dao)
|
||||
n := bc.GetBaseExecFee()
|
||||
require.EqualValues(t, interop.DefaultBaseExecFee, n)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStoragePrice(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
func TestPolicy_StoragePrice(t *testing.T) {
|
||||
bc, validators, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
|
||||
t.Run("get, internal method", func(t *testing.T) {
|
||||
n := chain.contracts.Policy.GetStoragePriceInternal(chain.dao)
|
||||
e.AddNewBlock(t) // avoid default value got from Blockchain.
|
||||
|
||||
n := bc.GetStoragePrice()
|
||||
require.Equal(t, int64(native.DefaultStoragePrice), n)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBlockedAccounts(t *testing.T) {
|
||||
chain := newTestChain(t)
|
||||
transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(),
|
||||
chain.contracts.GAS.Hash, 100_00000000)
|
||||
func TestPolicy_BlockedAccounts(t *testing.T) {
|
||||
bc, validators, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
policyHash := e.NativeHash(t, nativenames.Policy)
|
||||
|
||||
policySuperInvoker := e.NewInvoker(policyHash, validators, committee)
|
||||
unlucky := e.NewAccount(t, 5_0000_0000)
|
||||
policyUnluckyInvoker := e.NewInvoker(policyHash, unlucky)
|
||||
|
||||
// Block unlucky account.
|
||||
policySuperInvoker.Invoke(t, true, "blockAccount", unlucky.ScriptHash())
|
||||
|
||||
// Transaction from blocked account shouldn't be accepted.
|
||||
t.Run("isBlocked, internal method", func(t *testing.T) {
|
||||
isBlocked := chain.contracts.Policy.IsBlockedInternal(chain.dao, random.Uint160())
|
||||
require.Equal(t, false, isBlocked)
|
||||
tx := policyUnluckyInvoker.PrepareInvoke(t, "getStoragePrice")
|
||||
b := e.NewUnsignedBlock(t, tx)
|
||||
e.SignBlock(b)
|
||||
expectedErr := fmt.Sprintf("transaction %s failed to verify: not allowed by policy: account %s is blocked", tx.Hash().StringLE(), unlucky.ScriptHash().StringLE())
|
||||
err := e.Chain.AddBlock(b)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, expectedErr, err.Error())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -10,29 +11,32 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"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"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||
"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/network"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/notary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
var notaryModulePath = filepath.Join("..", "services", "notary")
|
||||
|
||||
func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
|
||||
func getTestNotary(t *testing.T, bc *core.Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
|
||||
mainCfg := config.P2PNotary{
|
||||
Enabled: true,
|
||||
UnlockWallet: config.Wallet{
|
||||
|
@ -46,7 +50,7 @@ func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx f
|
|||
Log: zaptest.NewLogger(t),
|
||||
}
|
||||
mp := mempool.New(10, 1, true)
|
||||
ntr, err := notary.NewNotary(cfg, testchain.Network(), mp, onTx)
|
||||
ntr, err := notary.NewNotary(cfg, netmode.UnitTestNet, mp, onTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
w, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, walletPath))
|
||||
|
@ -68,7 +72,14 @@ func dupNotaryRequest(t *testing.T, p *payload.P2PNotaryRequest) *payload.P2PNot
|
|||
}
|
||||
|
||||
func TestNotary(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
|
||||
c.P2PSigExtensions = true
|
||||
})
|
||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||
notaryHash := e.NativeHash(t, nativenames.Notary)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validators, committee)
|
||||
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
||||
|
||||
var (
|
||||
nonce uint32
|
||||
nvbDiffFallback uint32 = 20
|
||||
|
@ -145,8 +156,9 @@ func TestNotary(t *testing.T) {
|
|||
mp1.StopSubscriptions()
|
||||
})
|
||||
|
||||
notaryNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
||||
bc.setNodesByRole(t, true, noderoles.P2PNotary, notaryNodes)
|
||||
notaryNodes := []interface{}{acc1.PrivateKey().PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()}
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.P2PNotary), notaryNodes)
|
||||
|
||||
type requester struct {
|
||||
accounts []*wallet.Account
|
||||
|
@ -193,7 +205,7 @@ func TestNotary(t *testing.T) {
|
|||
VerificationScript: []byte{},
|
||||
},
|
||||
}
|
||||
err = requester.SignTx(testchain.Network(), fallback)
|
||||
err = requester.SignTx(netmode.UnitTestNet, fallback)
|
||||
require.NoError(t, err)
|
||||
return fallback
|
||||
}
|
||||
|
@ -251,7 +263,7 @@ func TestNotary(t *testing.T) {
|
|||
for j := range main.Scripts {
|
||||
main.Scripts[j].VerificationScript = verificationScripts[j]
|
||||
if i == j {
|
||||
main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(testchain.Network()), main)...)
|
||||
main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), main)...)
|
||||
}
|
||||
}
|
||||
main.Scripts = append(main.Scripts, transaction.Witness{}) // empty Notary witness
|
||||
|
@ -296,12 +308,11 @@ func TestNotary(t *testing.T) {
|
|||
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
|
||||
|
||||
for i := 0; i < len(completedTx.Scripts)-1; i++ {
|
||||
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx)
|
||||
_, err := bc.verifyHashAgainstScript(completedTx.Signers[i].Account, &completedTx.Scripts[i], interopCtx, -1)
|
||||
_, err := bc.VerifyWitness(completedTx.Signers[i].Account, completedTx, &completedTx.Scripts[i], -1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(testchain.Network()), requests[0].MainTransaction)...),
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), requests[0].MainTransaction)...),
|
||||
VerificationScript: []byte{},
|
||||
}, completedTx.Scripts[len(completedTx.Scripts)-1])
|
||||
} else {
|
||||
|
@ -316,15 +327,14 @@ func TestNotary(t *testing.T) {
|
|||
require.Equal(t, 2, len(completedTx.Signers))
|
||||
require.Equal(t, 2, len(completedTx.Scripts))
|
||||
require.Equal(t, transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(testchain.Network()), req.FallbackTransaction)...),
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), req.FallbackTransaction)...),
|
||||
VerificationScript: []byte{},
|
||||
}, completedTx.Scripts[0])
|
||||
|
||||
// check that tx size was updated
|
||||
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
|
||||
|
||||
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx)
|
||||
_, err := bc.verifyHashAgainstScript(completedTx.Signers[1].Account, &completedTx.Scripts[1], interopCtx, -1)
|
||||
_, err := bc.VerifyWitness(completedTx.Signers[1].Account, completedTx, &completedTx.Scripts[1], -1)
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash())
|
||||
|
@ -483,7 +493,8 @@ func TestNotary(t *testing.T) {
|
|||
checkFallbackTxs(t, r, false)
|
||||
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, r, 1, false)
|
||||
checkFallbackTxs(t, r, false)
|
||||
// set account back for the next tests
|
||||
|
@ -494,11 +505,11 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters := checkCompleteStandardRequest(t, 3, false)
|
||||
// check PostPersist with finalisation error
|
||||
setFinalizeWithError(true)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
// check PostPersist without finalisation error
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), true)
|
||||
|
||||
// PostPersist: complete main transaction, multisignature account
|
||||
|
@ -507,12 +518,12 @@ func TestNotary(t *testing.T) {
|
|||
checkFallbackTxs(t, requests, false)
|
||||
// check PostPersist with finalisation error
|
||||
setFinalizeWithError(true)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// check PostPersist without finalisation error
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), true)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
|
||||
|
@ -521,15 +532,15 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteStandardRequest(t, 3, false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
// check PostPersist for valid fallbacks with finalisation error
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// check PostPersist for valid fallbacks without finalisation error
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, true)
|
||||
|
||||
|
@ -540,15 +551,15 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
// check PostPersist for valid fallbacks with finalisation error
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// check PostPersist for valid fallbacks without finalisation error
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests[:nSigs], true)
|
||||
// the rest of fallbacks should also be applied even if the main tx was already constructed by the moment they were sent
|
||||
|
@ -559,14 +570,14 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteStandardRequest(t, 5, false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
// some of fallbacks should fail finalisation
|
||||
unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]}
|
||||
lucky := requests[1:4]
|
||||
setChoosy(true)
|
||||
// check PostPersist for lucky fallbacks
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, lucky, true)
|
||||
checkFallbackTxs(t, unluckies, false)
|
||||
|
@ -574,7 +585,7 @@ func TestNotary(t *testing.T) {
|
|||
setChoosy(false)
|
||||
setFinalizeWithError(false)
|
||||
// check PostPersist for unlucky fallbacks
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, lucky, true)
|
||||
checkFallbackTxs(t, unluckies, true)
|
||||
|
@ -585,19 +596,19 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteStandardRequest(t, 5, false, 1, 2, 3, 4, 5)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// generate blocks to reach the most earlier fallback's NVB
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
// check PostPersist for valid fallbacks without finalisation error
|
||||
// Add block before allowing tx to finalize to exclude race condition when
|
||||
// main transaction is finalized between `finalizeWithError` restore and adding new block.
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
mtx.RLock()
|
||||
start := len(completedTxes)
|
||||
mtx.RUnlock()
|
||||
setFinalizeWithError(false)
|
||||
for i := range requests {
|
||||
if i != 0 {
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
mtx.RLock()
|
||||
|
@ -615,13 +626,13 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid and remove one fallback
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
||||
ntr1.OnRequestRemoval(requests[3])
|
||||
// non of the fallbacks should be completed
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// set account back for the next tests
|
||||
|
@ -633,13 +644,13 @@ func TestNotary(t *testing.T) {
|
|||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid and remove one fallback
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
unlucky := requests[3]
|
||||
ntr1.OnRequestRemoval(unlucky)
|
||||
// rest of the fallbacks should be completed
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests[:3], true)
|
||||
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
||||
|
@ -648,20 +659,20 @@ func TestNotary(t *testing.T) {
|
|||
setFinalizeWithError(true)
|
||||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||
// remove all fallbacks
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
for i := range requests {
|
||||
ntr1.OnRequestRemoval(requests[i])
|
||||
}
|
||||
// then the whole request should be removed, i.e. there are no completed transactions
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
|
||||
// OnRequestRemoval: signature request, remove unexisting fallback
|
||||
ntr1.OnRequestRemoval(requests[0])
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
|
||||
|
@ -673,13 +684,13 @@ func TestNotary(t *testing.T) {
|
|||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
// make fallbacks valid and remove the last fallback
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
unlucky = requests[nSigs-1]
|
||||
ntr1.OnRequestRemoval(unlucky)
|
||||
// then (m-1) out of n fallbacks should be completed
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests[:nSigs-1], true)
|
||||
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
||||
|
@ -690,20 +701,20 @@ func TestNotary(t *testing.T) {
|
|||
setFinalizeWithError(true)
|
||||
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
||||
// make fallbacks valid and then remove all of them
|
||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
||||
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||
require.NoError(t, err)
|
||||
for i := range requests {
|
||||
ntr1.OnRequestRemoval(requests[i])
|
||||
}
|
||||
// then the whole request should be removed, i.e. there are no completed transactions
|
||||
setFinalizeWithError(false)
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
|
||||
// // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this
|
||||
ntr1.OnRequestRemoval(requests[0])
|
||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
checkMainTx(t, requesters, requests, len(requests), false)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
|
||||
|
@ -712,11 +723,11 @@ func TestNotary(t *testing.T) {
|
|||
requester1, _ := wallet.NewAccount()
|
||||
requester2, _ := wallet.NewAccount()
|
||||
amount := int64(100_0000_0000)
|
||||
feer := NewNotaryFeerStub(bc)
|
||||
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50))
|
||||
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(amount))
|
||||
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50))
|
||||
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(2*amount))
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
|
||||
e.CheckGASBalance(t, notaryHash, big.NewInt(amount))
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
|
||||
e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount))
|
||||
|
||||
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool
|
||||
requests = createMixedRequest([]requester{
|
||||
{
|
||||
|
@ -728,6 +739,7 @@ func TestNotary(t *testing.T) {
|
|||
typ: notary.Signature,
|
||||
},
|
||||
})
|
||||
feer := network.NewNotaryFeer(bc)
|
||||
require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0]))
|
||||
require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1]))
|
||||
require.Eventually(t, func() bool {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
gio "io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -15,168 +15,41 @@ 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"
|
||||
"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/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/interop/native/roles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"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/util/slice"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
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")
|
||||
)
|
||||
var oracleModulePath = filepath.Join("..", "services", "oracle")
|
||||
|
||||
// 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, oracleValidatorInvoker *neotest.ContractInvoker,
|
||||
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
|
||||
var filtItem interface{}
|
||||
if filter != nil {
|
||||
filtItem = *filter
|
||||
}
|
||||
res, err := invokeContractMethod(bc, gas+50_000_000+5_000_000, h, "requestURL",
|
||||
url, filtItem, cb, userData, gas)
|
||||
require.NoError(t, err)
|
||||
return res.Container
|
||||
return oracleValidatorInvoker.Invoke(t, stackitem.Null{}, "requestURL", url, filtItem, cb, userData, gas)
|
||||
}
|
||||
|
||||
func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string, returnOracleRedirectionErrOn func(address string) bool) oracle.Config {
|
||||
func getOracleConfig(t *testing.T, bc *core.Blockchain, w, pass string, returnOracleRedirectionErrOn func(address string) bool) oracle.Config {
|
||||
return oracle.Config{
|
||||
Log: zaptest.NewLogger(t),
|
||||
Network: netmode.UnitTestNet,
|
||||
|
@ -193,7 +66,7 @@ func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string, returnOracleR
|
|||
}
|
||||
}
|
||||
|
||||
func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) (
|
||||
func getTestOracle(t *testing.T, bc *core.Blockchain, walletPath, pass string) (
|
||||
*wallet.Account,
|
||||
*oracle.Oracle,
|
||||
map[uint64]*responseWithSig,
|
||||
|
@ -217,7 +90,19 @@ func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) (
|
|||
// Compatibility test from C# code.
|
||||
// https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61
|
||||
func TestCreateResponseTx(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||
|
||||
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
|
||||
rawManifest, err := json.Marshal(cs.Manifest)
|
||||
require.NoError(t, err)
|
||||
rawNef, err := cs.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
cInvoker := e.ValidatorInvoker(cs.Hash)
|
||||
|
||||
require.Equal(t, int64(30), bc.GetBaseExecFee())
|
||||
require.Equal(t, int64(1000), bc.FeePerByte())
|
||||
|
@ -236,10 +121,10 @@ func TestCreateResponseTx(t *testing.T) {
|
|||
Code: transaction.Success,
|
||||
Result: []byte{0},
|
||||
}
|
||||
require.NoError(t, bc.contracts.Oracle.PutRequestInternal(1, req, bc.dao))
|
||||
cInvoker.Invoke(t, stackitem.Null{}, "requestURL", req.URL, *req.Filter, req.CallbackMethod, req.UserData, int64(req.GasForResponse))
|
||||
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
||||
bc.SetOracle(orc)
|
||||
tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
|
||||
tx, err = orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 166, tx.Size())
|
||||
assert.Equal(t, int64(2198650), tx.NetworkFee)
|
||||
|
@ -247,7 +132,7 @@ func TestCreateResponseTx(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOracle_InvalidWallet(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
bc, _, _ := chain.NewMulti(t)
|
||||
|
||||
_, err := oracle.NewOracle(getOracleConfig(t, bc, "./testdata/oracle1.json", "invalid", nil))
|
||||
require.Error(t, err)
|
||||
|
@ -257,45 +142,65 @@ func TestOracle_InvalidWallet(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOracle(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
nativeOracleH := e.NativeHash(t, nativenames.Oracle)
|
||||
nativeOracleID := e.NativeID(t, nativenames.Oracle)
|
||||
|
||||
oracleCtr := bc.contracts.Oracle
|
||||
acc1, orc1, m1, ch1 := getTestOracle(t, bc, "./testdata/oracle1.json", "one")
|
||||
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
||||
// Must be set in native contract for tx verification.
|
||||
bc.setNodesByRole(t, true, noderoles.Oracle, oracleNodes)
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.Oracle), []interface{}{oracleNodes[0].Bytes(), oracleNodes[1].Bytes()})
|
||||
orc1.UpdateOracleNodes(oracleNodes.Copy())
|
||||
orc2.UpdateOracleNodes(oracleNodes.Copy())
|
||||
|
||||
orcNative := bc.contracts.Oracle
|
||||
md, ok := orcNative.GetMethod(manifest.MethodVerify, -1)
|
||||
require.True(t, ok)
|
||||
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
||||
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
||||
nativeOracleState := bc.GetContractState(nativeOracleH)
|
||||
require.NotNil(t, nativeOracleState)
|
||||
md := nativeOracleState.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
|
||||
require.NotNil(t, md)
|
||||
oracleRespScript := native.CreateOracleResponseScript(nativeOracleH)
|
||||
orc1.UpdateNativeContract(nativeOracleState.NEF.Script, slice.Copy(oracleRespScript), nativeOracleH, md.Offset)
|
||||
orc2.UpdateNativeContract(nativeOracleState.NEF.Script, slice.Copy(oracleRespScript), nativeOracleH, md.Offset)
|
||||
|
||||
cs := getOracleContractState(t, util.Uint160{}, 42)
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
|
||||
rawManifest, err := json.Marshal(cs.Manifest)
|
||||
require.NoError(t, err)
|
||||
rawNef, err := cs.NEF.Bytes()
|
||||
require.NoError(t, err)
|
||||
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
cInvoker := e.ValidatorInvoker(cs.Hash)
|
||||
|
||||
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)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.timeout", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.notfound", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.forbidden", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://private.url", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.big", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.timeout", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.notfound", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.forbidden", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://private.url", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.big", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
|
||||
|
||||
flt := "$.Values[1]"
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.filter", &flt, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.filterinv", &flt, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.filter", &flt, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.filterinv", &flt, "handle", []byte{}, 10_000_000)
|
||||
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.invalidcontent", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cInvoker, "https://get.invalidcontent", nil, "handle", []byte{}, 10_000_000)
|
||||
|
||||
checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest {
|
||||
req, err := oracleCtr.GetRequestInternal(bc.dao, id)
|
||||
require.NoError(t, err)
|
||||
// Use a hack to get request from Oracle contract, because we can't use GetRequestInternal directly.
|
||||
requestKey := make([]byte, 9)
|
||||
requestKey[0] = 7 // prefixRequest from native Oracle contract
|
||||
binary.BigEndian.PutUint64(requestKey[1:], id)
|
||||
si := bc.GetStorageItem(nativeOracleID, requestKey)
|
||||
require.NotNil(t, si)
|
||||
req := new(state.OracleRequest)
|
||||
require.NoError(t, stackitem.DeserializeConvertible(si, req))
|
||||
|
||||
reqs := map[uint64]*state.OracleRequest{id: req}
|
||||
orc1.ProcessRequestsInternal(reqs)
|
||||
|
@ -328,7 +233,7 @@ func TestOracle(t *testing.T) {
|
|||
actualHash := cp.Hash()
|
||||
require.Equal(t, actualHash, cachedHash, "transaction hash was changed during ")
|
||||
|
||||
require.NoError(t, bc.verifyAndPoolTx(tx, bc.GetMemPool(), bc))
|
||||
require.NoError(t, bc.PoolTx(tx))
|
||||
}
|
||||
|
||||
t.Run("NormalRequest", func(t *testing.T) {
|
||||
|
@ -436,21 +341,34 @@ func TestOracle(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOracleFull(t *testing.T) {
|
||||
bc := initTestChain(t, nil, nil)
|
||||
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
|
||||
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||
mp := bc.GetMemPool()
|
||||
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||
bc.SetOracle(orc)
|
||||
|
||||
cs := getOracleContractState(t, util.Uint160{}, 42)
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
go bc.Run()
|
||||
orc.Start()
|
||||
t.Cleanup(orc.Shutdown)
|
||||
t.Cleanup(func() {
|
||||
orc.Shutdown()
|
||||
bc.Close()
|
||||
})
|
||||
|
||||
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
||||
putOracleRequest(t, cs.Hash, bc, "https://get.1234", new(string), "handle", []byte{}, 10_000_000)
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
|
||||
|
||||
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
|
||||
e.DeployContract(t, &neotest.Contract{
|
||||
Hash: cs.Hash,
|
||||
NEF: &cs.NEF,
|
||||
Manifest: &cs.Manifest,
|
||||
}, nil)
|
||||
cInvoker := e.ValidatorInvoker(cs.Hash)
|
||||
|
||||
putOracleRequest(t, cInvoker, "https://get.1234", new(string), "handle", []byte{}, 10_000_000)
|
||||
|
||||
require.Eventually(t, func() bool { return mp.Count() == 1 },
|
||||
time.Second*3, time.Millisecond*200)
|
||||
|
@ -461,17 +379,20 @@ func TestOracleFull(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNotYetRunningOracle(t *testing.T) {
|
||||
bc := initTestChain(t, nil, nil)
|
||||
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
|
||||
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||
mp := bc.GetMemPool()
|
||||
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||
bc.SetOracle(orc)
|
||||
|
||||
cs := getOracleContractState(t, util.Uint160{}, 42)
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
go bc.Run()
|
||||
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
||||
t.Cleanup(bc.Close)
|
||||
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
|
||||
|
||||
var req state.OracleRequest
|
||||
var reqs = make(map[uint64]*state.OracleRequest)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
@ -10,18 +11,25 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"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"
|
||||
"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"
|
||||
corestate "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||
"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/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"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/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/atomic"
|
||||
|
@ -70,95 +78,113 @@ func newMajorityMultisigWithGAS(t *testing.T, n int) (util.Uint160, keys.PublicK
|
|||
}
|
||||
|
||||
func TestStateRoot(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
||||
|
||||
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
||||
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
|
||||
validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.StateValidator), validatorNodes)
|
||||
updateIndex := bc.BlockHeight()
|
||||
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
|
||||
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
||||
cfg := createStateRootConfig(w.Path(), "pass")
|
||||
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, nil)
|
||||
srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
|
||||
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, nil)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
||||
r, err := bc.stateRoot.GetStateRoot(bc.BlockHeight())
|
||||
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||
r, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, r.Root, bc.stateRoot.CurrentLocalStateRoot())
|
||||
require.Equal(t, r.Root, bc.GetStateModule().CurrentLocalStateRoot())
|
||||
|
||||
t.Run("invalid message", func(t *testing.T) {
|
||||
require.Error(t, srv.OnPayload(&payload.Extensible{Data: []byte{42}}))
|
||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
||||
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||
})
|
||||
t.Run("drop zero index", func(t *testing.T) {
|
||||
r, err := bc.stateRoot.GetStateRoot(0)
|
||||
r, err := bc.GetStateModule().GetStateRoot(0)
|
||||
require.NoError(t, err)
|
||||
data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
||||
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||
})
|
||||
t.Run("invalid height", func(t *testing.T) {
|
||||
r, err := bc.stateRoot.GetStateRoot(1)
|
||||
r, err := bc.GetStateModule().GetStateRoot(1)
|
||||
require.NoError(t, err)
|
||||
r.Index = 10
|
||||
data := testSignStateRoot(t, r, pubs, accs...)
|
||||
require.Error(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
||||
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||
})
|
||||
t.Run("invalid signer", func(t *testing.T) {
|
||||
accInv, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
|
||||
require.NoError(t, accInv.ConvertMultisig(1, pubs))
|
||||
transferTokenFromMultisigAccount(t, bc, accInv.Contract.ScriptHash(), bc.contracts.GAS.Hash, 1_0000_0000)
|
||||
r, err := bc.stateRoot.GetStateRoot(1)
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), accInv.Contract.ScriptHash(), 1_0000_0000, nil)
|
||||
r, err := bc.GetStateModule().GetStateRoot(1)
|
||||
require.NoError(t, err)
|
||||
data := testSignStateRoot(t, r, pubs, accInv)
|
||||
err = srv.OnPayload(&payload.Extensible{Data: data})
|
||||
require.True(t, errors.Is(err, ErrWitnessHashMismatch), "got: %v", err)
|
||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
||||
require.True(t, errors.Is(err, core.ErrWitnessHashMismatch), "got: %v", err)
|
||||
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||
})
|
||||
|
||||
r, err = bc.stateRoot.GetStateRoot(updateIndex + 1)
|
||||
r, err = bc.GetStateModule().GetStateRoot(updateIndex + 1)
|
||||
require.NoError(t, err)
|
||||
data := testSignStateRoot(t, r, pubs, accs...)
|
||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
||||
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight())
|
||||
require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
|
||||
|
||||
r, err = bc.stateRoot.GetStateRoot(updateIndex + 1)
|
||||
r, err = bc.GetStateModule().GetStateRoot(updateIndex + 1)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, len(r.Witness))
|
||||
require.Equal(t, h, r.Witness[0].ScriptHash())
|
||||
}
|
||||
|
||||
type memoryStore struct {
|
||||
*storage.MemoryStore
|
||||
}
|
||||
|
||||
func (memoryStore) Close() error { return nil }
|
||||
|
||||
func TestStateRootInitNonZeroHeight(t *testing.T) {
|
||||
st := memoryStore{storage.NewMemoryStore()}
|
||||
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
||||
|
||||
var root util.Uint256
|
||||
t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup
|
||||
bc := newTestChainWithCustomCfgAndStore(t, st, nil)
|
||||
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
|
||||
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
|
||||
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
||||
|
||||
validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.StateValidator), validatorNodes)
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
|
||||
|
||||
_, err := persistBlock(bc)
|
||||
require.NoError(t, err)
|
||||
tmpDir := t.TempDir()
|
||||
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
||||
cfg := createStateRootConfig(w.Path(), "pass")
|
||||
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, nil)
|
||||
srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
|
||||
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, nil)
|
||||
require.NoError(t, err)
|
||||
r, err := bc.stateRoot.GetStateRoot(2)
|
||||
r, err := bc.GetStateModule().GetStateRoot(2)
|
||||
require.NoError(t, err)
|
||||
data := testSignStateRoot(t, r, pubs, accs...)
|
||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
||||
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight())
|
||||
root = bc.stateRoot.CurrentLocalStateRoot()
|
||||
require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
|
||||
root = bc.GetStateModule().CurrentLocalStateRoot()
|
||||
})
|
||||
|
||||
bc2 := newTestChainWithCustomCfgAndStore(t, st, nil)
|
||||
bc2, _, _ := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
|
||||
srv := bc2.GetStateModule()
|
||||
require.EqualValues(t, 2, srv.CurrentValidatedHeight())
|
||||
require.Equal(t, root, srv.CurrentLocalStateRoot())
|
||||
|
@ -186,7 +212,22 @@ func createStateRootConfig(walletPath, password string) config.StateRoot {
|
|||
|
||||
func TestStateRootFull(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
bc := newTestChain(t)
|
||||
bc, validator, committee := chain.NewMulti(t)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
|
||||
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
|
||||
|
||||
getDesignatedByRole := func(t *testing.T, h uint32) keys.PublicKeys {
|
||||
res, err := designationSuperInvoker.TestInvoke(t, "getDesignatedByRole", int64(noderoles.StateValidator), h)
|
||||
require.NoError(t, err)
|
||||
nodes := res.Pop().Value().([]stackitem.Item)
|
||||
pubs := make(keys.PublicKeys, len(nodes))
|
||||
for i, node := range nodes {
|
||||
pubs[i], err = keys.NewPublicKeyFromBytes(node.Value().([]byte), elliptic.P256())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return pubs
|
||||
}
|
||||
|
||||
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
||||
w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two")
|
||||
|
@ -194,7 +235,8 @@ func TestStateRootFull(t *testing.T) {
|
|||
|
||||
var lastValidated atomic.Value
|
||||
var lastHeight atomic.Uint32
|
||||
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, func(ep *payload.Extensible) {
|
||||
srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
|
||||
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, func(ep *payload.Extensible) {
|
||||
lastHeight.Store(ep.ValidBlockStart)
|
||||
lastValidated.Store(ep)
|
||||
})
|
||||
|
@ -202,16 +244,17 @@ func TestStateRootFull(t *testing.T) {
|
|||
srv.Start()
|
||||
t.Cleanup(srv.Shutdown)
|
||||
|
||||
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
|
||||
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
|
||||
validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
|
||||
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||
int64(roles.StateValidator), validatorNodes)
|
||||
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
|
||||
require.Eventually(t, func() bool { return lastHeight.Load() == 2 }, time.Second, time.Millisecond)
|
||||
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1)
|
||||
_, err = persistBlock(bc)
|
||||
require.NoError(t, err)
|
||||
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1, getDesignatedByRole)
|
||||
e.AddNewBlock(t)
|
||||
require.Eventually(t, func() bool { return lastHeight.Load() == 3 }, time.Second, time.Millisecond)
|
||||
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 3, 1)
|
||||
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 3, 1, getDesignatedByRole)
|
||||
|
||||
r, err := bc.stateRoot.GetStateRoot(2)
|
||||
r, err := bc.GetStateModule().GetStateRoot(2)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
||||
require.NotNil(t, lastValidated.Load().(*payload.Extensible))
|
||||
|
@ -220,7 +263,7 @@ func TestStateRootFull(t *testing.T) {
|
|||
require.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg))
|
||||
require.NotEqual(t, stateroot.RootT, msg.Type) // not a sender for this root
|
||||
|
||||
r, err = bc.stateRoot.GetStateRoot(3)
|
||||
r, err = bc.GetStateModule().GetStateRoot(3)
|
||||
require.NoError(t, err)
|
||||
require.Error(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
||||
require.NoError(t, srv.AddSignature(3, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
||||
|
@ -235,8 +278,8 @@ func TestStateRootFull(t *testing.T) {
|
|||
require.Equal(t, r.Root, actual.Root)
|
||||
}
|
||||
|
||||
func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible,
|
||||
height uint32, valIndex byte) {
|
||||
func checkVoteBroadcasted(t *testing.T, bc *core.Blockchain, p *payload.Extensible,
|
||||
height uint32, valIndex byte, getDesignatedByRole func(t *testing.T, h uint32) keys.PublicKeys) {
|
||||
require.NotNil(t, p)
|
||||
m := new(stateroot.Message)
|
||||
require.NoError(t, testserdes.DecodeBinary(p.Data, m))
|
||||
|
@ -249,8 +292,7 @@ func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible,
|
|||
require.Equal(t, height, vote.Height)
|
||||
require.Equal(t, int32(valIndex), vote.ValidatorIndex)
|
||||
|
||||
pubs, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.StateValidator, bc.BlockHeight())
|
||||
require.NoError(t, err)
|
||||
pubs := getDesignatedByRole(t, bc.BlockHeight())
|
||||
require.True(t, len(pubs) > int(valIndex))
|
||||
require.True(t, pubs[valIndex].VerifyHashable(vote.Signature, uint32(netmode.UnitTestNet), r))
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package core
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -18,40 +19,41 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
stateSyncInterval = 2
|
||||
maxTraceable uint32 = 3
|
||||
)
|
||||
spoutCfg := func(c *config.Config) {
|
||||
c.ProtocolConfiguration.StateRootInHeader = true
|
||||
c.ProtocolConfiguration.P2PStateExchangeExtensions = true
|
||||
c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval
|
||||
c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable
|
||||
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||
c.StateRootInHeader = true
|
||||
c.P2PStateExchangeExtensions = true
|
||||
c.StateSyncInterval = stateSyncInterval
|
||||
c.MaxTraceableBlocks = maxTraceable
|
||||
}
|
||||
bcSpout := newTestChainWithCustomCfg(t, spoutCfg)
|
||||
bcSpout, validators, committee := chain.NewMultiWithCustomConfig(t, spoutCfg)
|
||||
e := neotest.NewExecutor(t, bcSpout, validators, committee)
|
||||
for i := 0; i <= 2*stateSyncInterval+int(maxTraceable)+1; i++ {
|
||||
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
}
|
||||
|
||||
boltCfg := func(c *config.Config) {
|
||||
boltCfg := func(c *config.ProtocolConfiguration) {
|
||||
spoutCfg(c)
|
||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = true
|
||||
c.KeepOnlyLatestState = true
|
||||
c.RemoveUntraceableBlocks = true
|
||||
}
|
||||
t.Run("error: module disabled by config", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
|
||||
boltCfg(c)
|
||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = false
|
||||
c.RemoveUntraceableBlocks = false
|
||||
})
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.Error(t, module.Init(bcSpout.BlockHeight())) // module inactive (non-archival node)
|
||||
})
|
||||
|
||||
t.Run("inactive: spout chain is too low to start state sync process", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(uint32(2*stateSyncInterval-1)))
|
||||
require.False(t, module.IsActive())
|
||||
})
|
||||
|
||||
t.Run("inactive: bolt chain height is close enough to spout chain height", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
for i := 1; i < int(bcSpout.BlockHeight())-stateSyncInterval; i++ {
|
||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||
require.NoError(t, err)
|
||||
|
@ -63,15 +65,16 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("error: bolt chain is too low to start state sync process", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
require.NoError(t, bcBolt.AddBlock(bcBolt.newBlock()))
|
||||
bcBolt, validatorsBolt, committeeBolt := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
eBolt := neotest.NewExecutor(t, bcBolt, validatorsBolt, committeeBolt)
|
||||
eBolt.AddNewBlock(t)
|
||||
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.Error(t, module.Init(uint32(3*stateSyncInterval)))
|
||||
})
|
||||
|
||||
t.Run("initialized: no previous state sync point", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||
|
@ -82,7 +85,7 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("error: outdated state sync point in the storage", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||
|
||||
|
@ -91,7 +94,7 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("initialized: valid previous state sync point in the storage", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||
|
||||
|
@ -104,7 +107,8 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("initialization from headers/blocks/mpt synced stages", func(t *testing.T) {
|
||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
||||
bcBolt, validatorsBolt, committeeBolt := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||
eBolt := neotest.NewExecutor(t, bcBolt, validatorsBolt, committeeBolt)
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||
|
||||
|
@ -263,7 +267,7 @@ func TestStateSyncModule_Init(t *testing.T) {
|
|||
|
||||
// add one more block to the restored chain and start new module: the module should recognise state sync is completed
|
||||
// and regular blocks processing was started
|
||||
require.NoError(t, bcBolt.AddBlock(bcBolt.newBlock()))
|
||||
eBolt.AddNewBlock(t)
|
||||
module = bcBolt.GetStateSyncModule()
|
||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||
require.False(t, module.IsActive())
|
||||
|
@ -282,27 +286,31 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
|||
maxTraceable uint32 = 6
|
||||
stateSyncPoint = 20
|
||||
)
|
||||
spoutCfg := func(c *config.Config) {
|
||||
c.ProtocolConfiguration.StateRootInHeader = true
|
||||
c.ProtocolConfiguration.P2PStateExchangeExtensions = true
|
||||
c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval
|
||||
c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable
|
||||
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||
c.StateRootInHeader = true
|
||||
c.P2PStateExchangeExtensions = true
|
||||
c.StateSyncInterval = stateSyncInterval
|
||||
c.MaxTraceableBlocks = maxTraceable
|
||||
c.P2PSigExtensions = true // `initBasicChain` assumes Notary is enabled.
|
||||
}
|
||||
bcSpout := newTestChainWithCustomCfg(t, spoutCfg)
|
||||
initBasicChain(t, bcSpout)
|
||||
bcSpoutStore := storage.NewMemoryStore()
|
||||
bcSpout, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, spoutCfg, bcSpoutStore, false)
|
||||
go bcSpout.Run() // Will close it manually at the end.
|
||||
e := neotest.NewExecutor(t, bcSpout, validators, committee)
|
||||
initBasicChain(t, e)
|
||||
|
||||
// make spout chain higher that latest state sync point (add several blocks up to stateSyncPoint+2)
|
||||
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
|
||||
e.AddNewBlock(t)
|
||||
require.Equal(t, stateSyncPoint+2, int(bcSpout.BlockHeight()))
|
||||
|
||||
boltCfg := func(c *config.Config) {
|
||||
boltCfg := func(c *config.ProtocolConfiguration) {
|
||||
spoutCfg(c)
|
||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = true
|
||||
c.KeepOnlyLatestState = true
|
||||
c.RemoveUntraceableBlocks = true
|
||||
}
|
||||
bcBoltStore := memoryStore{storage.NewMemoryStore()}
|
||||
bcBolt := initTestChain(t, bcBoltStore, boltCfg)
|
||||
go bcBolt.Run()
|
||||
bcBoltStore := storage.NewMemoryStore()
|
||||
bcBolt, _, _ := chain.NewMultiWithCustomConfigAndStore(t, boltCfg, bcBoltStore, false)
|
||||
go bcBolt.Run() // Will close it manually at the end.
|
||||
module := bcBolt.GetStateSyncModule()
|
||||
|
||||
t.Run("error: add headers before initialisation", func(t *testing.T) {
|
||||
|
@ -421,9 +429,9 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
|||
require.Equal(t, bcSpout.BlockHeight(), bcBolt.BlockHeight())
|
||||
|
||||
// compare storage states
|
||||
fetchStorage := func(bc *Blockchain) []storage.KeyValue {
|
||||
fetchStorage := func(ps storage.Store, storagePrefix byte) []storage.KeyValue {
|
||||
var kv []storage.KeyValue
|
||||
bc.dao.Store.Seek(storage.SeekRange{Prefix: []byte{byte(bc.dao.Version.StoragePrefix)}}, func(k, v []byte) bool {
|
||||
ps.Seek(storage.SeekRange{Prefix: []byte{storagePrefix}}, func(k, v []byte) bool {
|
||||
key := slice.Copy(k)
|
||||
value := slice.Copy(v)
|
||||
if key[0] == byte(storage.STTempStorage) {
|
||||
|
@ -437,25 +445,19 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
|||
})
|
||||
return kv
|
||||
}
|
||||
expected := fetchStorage(bcSpout)
|
||||
actual := fetchStorage(bcBolt)
|
||||
// Both blockchains are running, so we need to wait until recent changes will be persisted
|
||||
// to the underlying backend store. Close blockchains to ensure persist was completed.
|
||||
bcSpout.Close()
|
||||
bcBolt.Close()
|
||||
expected := fetchStorage(bcSpoutStore, byte(storage.STStorage))
|
||||
actual := fetchStorage(bcBoltStore, byte(storage.STTempStorage))
|
||||
require.ElementsMatch(t, expected, actual)
|
||||
|
||||
// no temp items should be left
|
||||
require.Eventually(t, func() bool {
|
||||
var haveItems bool
|
||||
bcBolt.dao.Store.Seek(storage.SeekRange{Prefix: []byte{byte(storage.STStorage)}}, func(_, _ []byte) bool {
|
||||
haveItems = true
|
||||
return false
|
||||
})
|
||||
return !haveItems
|
||||
}, time.Second*5, time.Millisecond*100)
|
||||
bcBolt.Close()
|
||||
|
||||
// Check restoring with new prefix.
|
||||
bcBolt = initTestChain(t, bcBoltStore, boltCfg)
|
||||
go bcBolt.Run()
|
||||
defer bcBolt.Close()
|
||||
require.Equal(t, storage.STTempStorage, bcBolt.dao.Version.StoragePrefix)
|
||||
require.Equal(t, storage.STTempStorage, bcBolt.persistent.Version.StoragePrefix)
|
||||
var haveItems bool
|
||||
bcBoltStore.Seek(storage.SeekRange{Prefix: []byte{byte(storage.STStorage)}}, func(_, _ []byte) bool {
|
||||
haveItems = true
|
||||
return false
|
||||
})
|
||||
require.False(t, haveItems)
|
||||
}
|
||||
|
|
|
@ -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 +0,0 @@
|
|||
{"name":"TestAux","abi":{"methods":[],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0x00ecaa2f079b65e3b31572e4c2c160a1abd02997","methods":["add","drop","add3","invalidReturn","justReturn","getValue"]}],"supportedstandards":[],"trusts":[],"extra":null}
|
|
@ -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`.
|
|
@ -36,7 +36,7 @@ type Executor struct {
|
|||
}
|
||||
|
||||
// NewExecutor creates new executor instance from provided blockchain and committee.
|
||||
func NewExecutor(t *testing.T, bc blockchainer.Blockchainer, validator, committee Signer) *Executor {
|
||||
func NewExecutor(t testing.TB, bc blockchainer.Blockchainer, validator, committee Signer) *Executor {
|
||||
checkMultiSigner(t, validator)
|
||||
checkMultiSigner(t, committee)
|
||||
|
||||
|
@ -50,21 +50,28 @@ func NewExecutor(t *testing.T, bc blockchainer.Blockchainer, validator, committe
|
|||
}
|
||||
|
||||
// TopBlock returns block with the highest index.
|
||||
func (e *Executor) TopBlock(t *testing.T) *block.Block {
|
||||
func (e *Executor) TopBlock(t testing.TB) *block.Block {
|
||||
b, err := e.Chain.GetBlock(e.Chain.GetHeaderHash(int(e.Chain.BlockHeight())))
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}
|
||||
|
||||
// NativeHash returns native contract hash by name.
|
||||
func (e *Executor) NativeHash(t *testing.T, name string) util.Uint160 {
|
||||
func (e *Executor) NativeHash(t testing.TB, name string) util.Uint160 {
|
||||
h, err := e.Chain.GetNativeContractScriptHash(name)
|
||||
require.NoError(t, err)
|
||||
return h
|
||||
}
|
||||
|
||||
// ContractHash returns contract hash by ID.
|
||||
func (e *Executor) ContractHash(t testing.TB, id int32) util.Uint160 {
|
||||
h, err := e.Chain.GetContractScriptHash(id)
|
||||
require.NoError(t, err)
|
||||
return h
|
||||
}
|
||||
|
||||
// NativeID returns native contract ID by name.
|
||||
func (e *Executor) NativeID(t *testing.T, name string) int32 {
|
||||
func (e *Executor) NativeID(t testing.TB, name string) int32 {
|
||||
h := e.NativeHash(t, name)
|
||||
cs := e.Chain.GetContractState(h)
|
||||
require.NotNil(t, cs)
|
||||
|
@ -72,7 +79,7 @@ func (e *Executor) NativeID(t *testing.T, name string) int32 {
|
|||
}
|
||||
|
||||
// NewUnsignedTx creates new unsigned transaction which invokes method of contract with hash.
|
||||
func (e *Executor) NewUnsignedTx(t *testing.T, hash util.Uint160, method string, args ...interface{}) *transaction.Transaction {
|
||||
func (e *Executor) NewUnsignedTx(t testing.TB, hash util.Uint160, method string, args ...interface{}) *transaction.Transaction {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
|
||||
require.NoError(t, w.Err)
|
||||
|
@ -86,14 +93,14 @@ func (e *Executor) NewUnsignedTx(t *testing.T, hash util.Uint160, method string,
|
|||
|
||||
// NewTx creates new transaction which invokes contract method.
|
||||
// Transaction is signed with signer.
|
||||
func (e *Executor) NewTx(t *testing.T, signers []Signer,
|
||||
func (e *Executor) NewTx(t testing.TB, signers []Signer,
|
||||
hash util.Uint160, method string, args ...interface{}) *transaction.Transaction {
|
||||
tx := e.NewUnsignedTx(t, hash, method, args...)
|
||||
return e.SignTx(t, tx, -1, signers...)
|
||||
}
|
||||
|
||||
// SignTx signs a transaction using provided signers.
|
||||
func (e *Executor) SignTx(t *testing.T, tx *transaction.Transaction, sysFee int64, signers ...Signer) *transaction.Transaction {
|
||||
func (e *Executor) SignTx(t testing.TB, tx *transaction.Transaction, sysFee int64, signers ...Signer) *transaction.Transaction {
|
||||
for _, acc := range signers {
|
||||
tx.Signers = append(tx.Signers, transaction.Signer{
|
||||
Account: acc.ScriptHash(),
|
||||
|
@ -111,7 +118,7 @@ func (e *Executor) SignTx(t *testing.T, tx *transaction.Transaction, sysFee int6
|
|||
|
||||
// NewAccount returns new signer holding 100.0 GAS (or given amount is specified).
|
||||
// This method advances the chain by one block with a transfer transaction.
|
||||
func (e *Executor) NewAccount(t *testing.T, expectedGASBalance ...int64) Signer {
|
||||
func (e *Executor) NewAccount(t testing.TB, expectedGASBalance ...int64) Signer {
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -131,8 +138,16 @@ func (e *Executor) NewAccount(t *testing.T, expectedGASBalance ...int64) Signer
|
|||
// precalculated contract hash matches the actual one.
|
||||
// data is an optional argument to `_deploy`.
|
||||
// Returns hash of the deploy transaction.
|
||||
func (e *Executor) DeployContract(t *testing.T, c *Contract, data interface{}) util.Uint256 {
|
||||
tx := e.NewDeployTx(t, e.Chain, c, data)
|
||||
func (e *Executor) DeployContract(t testing.TB, c *Contract, data interface{}) util.Uint256 {
|
||||
return e.DeployContractBy(t, e.Validator, c, data)
|
||||
}
|
||||
|
||||
// DeployContractBy compiles and deploys contract to bc using provided signer.
|
||||
// It also checks that precalculated contract hash matches the actual one.
|
||||
// data is an optional argument to `_deploy`.
|
||||
// Returns hash of the deploy transaction.
|
||||
func (e *Executor) DeployContractBy(t testing.TB, signer Signer, c *Contract, data interface{}) util.Uint256 {
|
||||
tx := NewDeployTxBy(t, e.Chain, signer, c, data)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
|
||||
|
@ -148,9 +163,9 @@ func (e *Executor) DeployContract(t *testing.T, c *Contract, data interface{}) u
|
|||
return tx.Hash()
|
||||
}
|
||||
|
||||
// DeployContractCheckFAULT compiles and deploys contract to bc. It checks that deploy
|
||||
// transaction FAULTed with the specified error.
|
||||
func (e *Executor) DeployContractCheckFAULT(t *testing.T, c *Contract, data interface{}, errMessage string) {
|
||||
// DeployContractCheckFAULT compiles and deploys contract to bc using validator
|
||||
// account. It checks that deploy transaction FAULTed with the specified error.
|
||||
func (e *Executor) DeployContractCheckFAULT(t testing.TB, c *Contract, data interface{}, errMessage string) {
|
||||
tx := e.NewDeployTx(t, e.Chain, c, data)
|
||||
e.AddNewBlock(t, tx)
|
||||
e.CheckFault(t, tx.Hash(), errMessage)
|
||||
|
@ -158,31 +173,41 @@ func (e *Executor) DeployContractCheckFAULT(t *testing.T, c *Contract, data inte
|
|||
|
||||
// InvokeScript adds transaction with the specified script to the chain and
|
||||
// returns its hash. It does no faults check.
|
||||
func (e *Executor) InvokeScript(t *testing.T, script []byte, signers []Signer) util.Uint256 {
|
||||
tx := transaction.New(script, 0)
|
||||
tx.Nonce = Nonce()
|
||||
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
||||
e.SignTx(t, tx, -1, signers...)
|
||||
func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) util.Uint256 {
|
||||
tx := e.PrepareInvocation(t, script, signers)
|
||||
e.AddNewBlock(t, tx)
|
||||
return tx.Hash()
|
||||
}
|
||||
|
||||
// PrepareInvocation creates transaction with the specified script and signs it
|
||||
// by the provided signer.
|
||||
func (e *Executor) PrepareInvocation(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) *transaction.Transaction {
|
||||
tx := transaction.New(script, 0)
|
||||
tx.Nonce = Nonce()
|
||||
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
||||
if len(validUntilBlock) != 0 {
|
||||
tx.ValidUntilBlock = validUntilBlock[0]
|
||||
}
|
||||
e.SignTx(t, tx, -1, signers...)
|
||||
return tx
|
||||
}
|
||||
|
||||
// InvokeScriptCheckHALT adds transaction with the specified script to the chain
|
||||
// and checks it's HALTed with the specified items on stack.
|
||||
func (e *Executor) InvokeScriptCheckHALT(t *testing.T, script []byte, signers []Signer, stack ...stackitem.Item) {
|
||||
func (e *Executor) InvokeScriptCheckHALT(t testing.TB, script []byte, signers []Signer, stack ...stackitem.Item) {
|
||||
hash := e.InvokeScript(t, script, signers)
|
||||
e.CheckHalt(t, hash, stack...)
|
||||
}
|
||||
|
||||
// InvokeScriptCheckFAULT adds transaction with the specified script to the
|
||||
// chain and checks it's FAULTed with the specified error.
|
||||
func (e *Executor) InvokeScriptCheckFAULT(t *testing.T, script []byte, signers []Signer, errMessage string) {
|
||||
func (e *Executor) InvokeScriptCheckFAULT(t testing.TB, script []byte, signers []Signer, errMessage string) {
|
||||
hash := e.InvokeScript(t, script, signers)
|
||||
e.CheckFault(t, hash, errMessage)
|
||||
}
|
||||
|
||||
// CheckHalt checks that transaction persisted with HALT state.
|
||||
func (e *Executor) CheckHalt(t *testing.T, h util.Uint256, stack ...stackitem.Item) *state.AppExecResult {
|
||||
func (e *Executor) CheckHalt(t testing.TB, h util.Uint256, stack ...stackitem.Item) *state.AppExecResult {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
||||
|
@ -194,7 +219,7 @@ func (e *Executor) CheckHalt(t *testing.T, h util.Uint256, stack ...stackitem.It
|
|||
|
||||
// CheckFault checks that transaction persisted with FAULT state.
|
||||
// Raised exception is also checked to contain s as a substring.
|
||||
func (e *Executor) CheckFault(t *testing.T, h util.Uint256, s string) {
|
||||
func (e *Executor) CheckFault(t testing.TB, h util.Uint256, s string) {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, vm.FaultState, aer[0].VMState)
|
||||
|
@ -204,7 +229,7 @@ func (e *Executor) CheckFault(t *testing.T, h util.Uint256, s string) {
|
|||
|
||||
// CheckTxNotificationEvent checks that specified event was emitted at the specified position
|
||||
// during transaction script execution. Negative index corresponds to backwards enumeration.
|
||||
func (e *Executor) CheckTxNotificationEvent(t *testing.T, h util.Uint256, index int, expected state.NotificationEvent) {
|
||||
func (e *Executor) CheckTxNotificationEvent(t testing.TB, h util.Uint256, index int, expected state.NotificationEvent) {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
require.NoError(t, err)
|
||||
l := len(aer[0].Events)
|
||||
|
@ -216,13 +241,24 @@ func (e *Executor) CheckTxNotificationEvent(t *testing.T, h util.Uint256, index
|
|||
}
|
||||
|
||||
// CheckGASBalance ensures that provided account owns specified amount of GAS.
|
||||
func (e *Executor) CheckGASBalance(t *testing.T, acc util.Uint160, expected *big.Int) {
|
||||
func (e *Executor) CheckGASBalance(t testing.TB, acc util.Uint160, expected *big.Int) {
|
||||
actual := e.Chain.GetUtilityTokenBalance(acc)
|
||||
require.Equal(t, expected, actual, fmt.Errorf("invalid GAS balance: expected %s, got %s", expected.String(), actual.String()))
|
||||
}
|
||||
|
||||
// EnsureGASBalance ensures that provided account owns amount of GAS that satisfies provided condition.
|
||||
func (e *Executor) EnsureGASBalance(t testing.TB, acc util.Uint160, isOk func(balance *big.Int) bool) {
|
||||
actual := e.Chain.GetUtilityTokenBalance(acc)
|
||||
require.True(t, isOk(actual), fmt.Errorf("invalid GAS balance: got %s, condition is not satisfied", actual.String()))
|
||||
}
|
||||
|
||||
// NewDeployTx returns new deployment tx for contract signed by committee.
|
||||
func (e *Executor) NewDeployTx(t *testing.T, bc blockchainer.Blockchainer, c *Contract, data interface{}) *transaction.Transaction {
|
||||
func (e *Executor) NewDeployTx(t testing.TB, bc blockchainer.Blockchainer, c *Contract, data interface{}) *transaction.Transaction {
|
||||
return NewDeployTxBy(t, bc, e.Validator, c, data)
|
||||
}
|
||||
|
||||
// NewDeployTxBy returns new deployment tx for contract signed by the specified signer.
|
||||
func NewDeployTxBy(t testing.TB, bc blockchainer.Blockchainer, signer Signer, c *Contract, data interface{}) *transaction.Transaction {
|
||||
rawManifest, err := json.Marshal(c.Manifest)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -237,11 +273,11 @@ func (e *Executor) NewDeployTx(t *testing.T, bc blockchainer.Blockchainer, c *Co
|
|||
tx.Nonce = Nonce()
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: e.Validator.ScriptHash(),
|
||||
Account: signer.ScriptHash(),
|
||||
Scopes: transaction.Global,
|
||||
}}
|
||||
addNetworkFee(bc, tx, e.Validator)
|
||||
require.NoError(t, e.Validator.SignTx(netmode.UnitTestNet, tx))
|
||||
addNetworkFee(bc, tx, signer)
|
||||
require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx))
|
||||
return tx
|
||||
}
|
||||
|
||||
|
@ -266,7 +302,7 @@ func addNetworkFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, si
|
|||
}
|
||||
|
||||
// NewUnsignedBlock creates new unsigned block from txs.
|
||||
func (e *Executor) NewUnsignedBlock(t *testing.T, txs ...*transaction.Transaction) *block.Block {
|
||||
func (e *Executor) NewUnsignedBlock(t testing.TB, txs ...*transaction.Transaction) *block.Block {
|
||||
lastBlock := e.TopBlock(t)
|
||||
b := &block.Block{
|
||||
Header: block.Header{
|
||||
|
@ -289,7 +325,7 @@ func (e *Executor) NewUnsignedBlock(t *testing.T, txs ...*transaction.Transactio
|
|||
}
|
||||
|
||||
// AddNewBlock creates a new block from provided transactions and adds it on bc.
|
||||
func (e *Executor) AddNewBlock(t *testing.T, txs ...*transaction.Transaction) *block.Block {
|
||||
func (e *Executor) AddNewBlock(t testing.TB, txs ...*transaction.Transaction) *block.Block {
|
||||
b := e.NewUnsignedBlock(t, txs...)
|
||||
e.SignBlock(b)
|
||||
require.NoError(t, e.Chain.AddBlock(b))
|
||||
|
@ -297,10 +333,12 @@ func (e *Executor) AddNewBlock(t *testing.T, txs ...*transaction.Transaction) *b
|
|||
}
|
||||
|
||||
// GenerateNewBlocks adds specified number of empty blocks to the chain.
|
||||
func (e *Executor) GenerateNewBlocks(t *testing.T, count int) {
|
||||
func (e *Executor) GenerateNewBlocks(t testing.TB, count int) []*block.Block {
|
||||
blocks := make([]*block.Block, count)
|
||||
for i := 0; i < count; i++ {
|
||||
e.AddNewBlock(t)
|
||||
blocks[i] = e.AddNewBlock(t)
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
// SignBlock add validators signature to b.
|
||||
|
@ -311,7 +349,7 @@ func (e *Executor) SignBlock(b *block.Block) *block.Block {
|
|||
}
|
||||
|
||||
// AddBlockCheckHalt is a convenient wrapper over AddBlock and CheckHalt.
|
||||
func (e *Executor) AddBlockCheckHalt(t *testing.T, txs ...*transaction.Transaction) *block.Block {
|
||||
func (e *Executor) AddBlockCheckHalt(t testing.TB, txs ...*transaction.Transaction) *block.Block {
|
||||
b := e.AddNewBlock(t, txs...)
|
||||
for _, tx := range txs {
|
||||
e.CheckHalt(t, tx.Hash())
|
||||
|
@ -344,14 +382,14 @@ func TestInvoke(bc blockchainer.Blockchainer, tx *transaction.Transaction) (*vm.
|
|||
}
|
||||
|
||||
// GetTransaction returns transaction and its height by the specified hash.
|
||||
func (e *Executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Transaction, uint32) {
|
||||
func (e *Executor) GetTransaction(t testing.TB, h util.Uint256) (*transaction.Transaction, uint32) {
|
||||
tx, height, err := e.Chain.GetTransaction(h)
|
||||
require.NoError(t, err)
|
||||
return tx, height
|
||||
}
|
||||
|
||||
// GetBlockByIndex returns block by the specified index.
|
||||
func (e *Executor) GetBlockByIndex(t *testing.T, idx int) *block.Block {
|
||||
func (e *Executor) GetBlockByIndex(t testing.TB, idx int) *block.Block {
|
||||
h := e.Chain.GetHeaderHash(idx)
|
||||
require.NotEmpty(t, h)
|
||||
b, err := e.Chain.GetBlock(h)
|
||||
|
@ -360,7 +398,7 @@ func (e *Executor) GetBlockByIndex(t *testing.T, idx int) *block.Block {
|
|||
}
|
||||
|
||||
// GetTxExecResult returns application execution results for the specified transaction.
|
||||
func (e *Executor) GetTxExecResult(t *testing.T, h util.Uint256) *state.AppExecResult {
|
||||
func (e *Executor) GetTxExecResult(t testing.TB, h util.Uint256) *state.AppExecResult {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(aer))
|
||||
|
|
|
@ -121,15 +121,14 @@ func init() {
|
|||
// this package. MemoryStore is used as the backend storage, so all of the chain
|
||||
// contents is always in RAM. The Signer returned is validator (and committee at
|
||||
// the same time).
|
||||
func NewSingle(t *testing.T) (*core.Blockchain, neotest.Signer) {
|
||||
func NewSingle(t testing.TB) (*core.Blockchain, neotest.Signer) {
|
||||
return NewSingleWithCustomConfig(t, nil)
|
||||
}
|
||||
|
||||
// NewSingleWithCustomConfig is similar to NewSingle, but allows to override the
|
||||
// default configuration.
|
||||
func NewSingleWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
|
||||
st := storage.NewMemoryStore()
|
||||
return NewSingleWithCustomConfigAndStore(t, f, st, true)
|
||||
func NewSingleWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
|
||||
return NewSingleWithCustomConfigAndStore(t, f, nil, true)
|
||||
}
|
||||
|
||||
// NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but
|
||||
|
@ -137,7 +136,7 @@ func NewSingleWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguratio
|
|||
// Run method is called on the Blockchain instance, if not then it's caller's
|
||||
// responsibility to do that before using the chain and its caller's responsibility
|
||||
// also to properly Close the chain when done.
|
||||
func NewSingleWithCustomConfigAndStore(t *testing.T, f func(cfg *config.ProtocolConfiguration), st storage.Store, run bool) (*core.Blockchain, neotest.Signer) {
|
||||
func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.ProtocolConfiguration), st storage.Store, run bool) (*core.Blockchain, neotest.Signer) {
|
||||
protoCfg := config.ProtocolConfiguration{
|
||||
Magic: netmode.UnitTestNet,
|
||||
MaxTraceableBlocks: MaxTraceableBlocks,
|
||||
|
@ -150,6 +149,9 @@ func NewSingleWithCustomConfigAndStore(t *testing.T, f func(cfg *config.Protocol
|
|||
if f != nil {
|
||||
f(&protoCfg)
|
||||
}
|
||||
if st == nil {
|
||||
st = storage.NewMemoryStore()
|
||||
}
|
||||
log := zaptest.NewLogger(t)
|
||||
bc, err := core.NewBlockchain(st, protoCfg, log)
|
||||
require.NoError(t, err)
|
||||
|
@ -163,13 +165,34 @@ func NewSingleWithCustomConfigAndStore(t *testing.T, f func(cfg *config.Protocol
|
|||
// NewMulti creates new blockchain instance with four validators and six
|
||||
// committee members, otherwise not differring much from NewSingle. The
|
||||
// second value returned contains validators Signer, the third -- committee one.
|
||||
func NewMulti(t *testing.T) (*core.Blockchain, neotest.Signer, neotest.Signer) {
|
||||
func NewMulti(t testing.TB) (*core.Blockchain, neotest.Signer, neotest.Signer) {
|
||||
return NewMultiWithCustomConfig(t, nil)
|
||||
}
|
||||
|
||||
// NewMultiWithCustomConfig is similar to NewMulti except it allows to override the
|
||||
// default configuration.
|
||||
func NewMultiWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer, neotest.Signer) {
|
||||
func NewMultiWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer, neotest.Signer) {
|
||||
return NewMultiWithCustomConfigAndStore(t, f, nil, true)
|
||||
}
|
||||
|
||||
// NewMultiWithCustomConfigAndStore is similar to NewMultiWithCustomConfig, but
|
||||
// also allows to override backend Store being used. The last parameter controls if
|
||||
// Run method is called on the Blockchain instance, if not then it's caller's
|
||||
// responsibility to do that before using the chain and its caller's responsibility
|
||||
// also to properly Close the chain when done.
|
||||
func NewMultiWithCustomConfigAndStore(t testing.TB, f func(*config.ProtocolConfiguration), st storage.Store, run bool) (*core.Blockchain, neotest.Signer, neotest.Signer) {
|
||||
bc, validator, committee, err := NewMultiWithCustomConfigAndStoreNoCheck(t, f, st)
|
||||
require.NoError(t, err)
|
||||
if run {
|
||||
go bc.Run()
|
||||
t.Cleanup(bc.Close)
|
||||
}
|
||||
return bc, validator, committee
|
||||
}
|
||||
|
||||
// NewMultiWithCustomConfigAndStoreNoCheck is similar to NewMultiWithCustomConfig,
|
||||
// but do not perform Blockchain run and do not check Blockchain constructor error.
|
||||
func NewMultiWithCustomConfigAndStoreNoCheck(t testing.TB, f func(*config.ProtocolConfiguration), st storage.Store) (*core.Blockchain, neotest.Signer, neotest.Signer, error) {
|
||||
protoCfg := config.ProtocolConfiguration{
|
||||
Magic: netmode.UnitTestNet,
|
||||
MaxTraceableBlocks: MaxTraceableBlocks,
|
||||
|
@ -182,12 +205,11 @@ func NewMultiWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration
|
|||
if f != nil {
|
||||
f(&protoCfg)
|
||||
}
|
||||
if st == nil {
|
||||
st = storage.NewMemoryStore()
|
||||
}
|
||||
|
||||
st := storage.NewMemoryStore()
|
||||
log := zaptest.NewLogger(t)
|
||||
bc, err := core.NewBlockchain(st, protoCfg, log)
|
||||
require.NoError(t, err)
|
||||
go bc.Run()
|
||||
t.Cleanup(bc.Close)
|
||||
return bc, neotest.NewMultiSigner(multiValidatorAcc...), neotest.NewMultiSigner(multiCommitteeAcc...)
|
||||
return bc, neotest.NewMultiSigner(multiValidatorAcc...), neotest.NewMultiSigner(multiCommitteeAcc...), err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,15 @@ type ContractInvoker struct {
|
|||
Signers []Signer
|
||||
}
|
||||
|
||||
// NewInvoker creates new ContractInvoker for contract with hash h and specified signers.
|
||||
func (e *Executor) NewInvoker(h util.Uint160, signers ...Signer) *ContractInvoker {
|
||||
return &ContractInvoker{
|
||||
Executor: e,
|
||||
Hash: h,
|
||||
Signers: signers,
|
||||
}
|
||||
}
|
||||
|
||||
// CommitteeInvoker creates new ContractInvoker for contract with hash h and committee multisignature signer.
|
||||
func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
|
||||
return &ContractInvoker{
|
||||
|
@ -38,7 +47,7 @@ func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker {
|
|||
}
|
||||
|
||||
// TestInvoke creates test VM and invokes method with args.
|
||||
func (c *ContractInvoker) TestInvoke(t *testing.T, method string, args ...interface{}) (*vm.Stack, error) {
|
||||
func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...interface{}) (*vm.Stack, error) {
|
||||
tx := c.PrepareInvokeNoSign(t, method, args...)
|
||||
b := c.NewUnsignedBlock(t, tx)
|
||||
ic := c.Chain.GetTestVM(trigger.Application, tx, b)
|
||||
|
@ -57,18 +66,18 @@ func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker {
|
|||
}
|
||||
|
||||
// PrepareInvoke creates new invocation transaction.
|
||||
func (c *ContractInvoker) PrepareInvoke(t *testing.T, method string, args ...interface{}) *transaction.Transaction {
|
||||
func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...interface{}) *transaction.Transaction {
|
||||
return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...)
|
||||
}
|
||||
|
||||
// PrepareInvokeNoSign creates new unsigned invocation transaction.
|
||||
func (c *ContractInvoker) PrepareInvokeNoSign(t *testing.T, method string, args ...interface{}) *transaction.Transaction {
|
||||
func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...interface{}) *transaction.Transaction {
|
||||
return c.Executor.NewUnsignedTx(t, c.Hash, method, args...)
|
||||
}
|
||||
|
||||
// Invoke invokes method with args, persists transaction and checks the result.
|
||||
// Returns transaction hash.
|
||||
func (c *ContractInvoker) Invoke(t *testing.T, result interface{}, method string, args ...interface{}) util.Uint256 {
|
||||
func (c *ContractInvoker) Invoke(t testing.TB, result interface{}, method string, args ...interface{}) util.Uint256 {
|
||||
tx := c.PrepareInvoke(t, method, args...)
|
||||
c.AddNewBlock(t, tx)
|
||||
c.CheckHalt(t, tx.Hash(), stackitem.Make(result))
|
||||
|
@ -77,7 +86,7 @@ func (c *ContractInvoker) Invoke(t *testing.T, result interface{}, method string
|
|||
|
||||
// InvokeAndCheck invokes method with args, persists transaction and checks the result
|
||||
// using provided function. Returns transaction hash.
|
||||
func (c *ContractInvoker) InvokeAndCheck(t *testing.T, checkResult func(t *testing.T, stack []stackitem.Item), method string, args ...interface{}) util.Uint256 {
|
||||
func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...interface{}) util.Uint256 {
|
||||
tx := c.PrepareInvoke(t, method, args...)
|
||||
c.AddNewBlock(t, tx)
|
||||
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
|
@ -90,7 +99,7 @@ func (c *ContractInvoker) InvokeAndCheck(t *testing.T, checkResult func(t *testi
|
|||
}
|
||||
|
||||
// InvokeWithFeeFail is like InvokeFail but sets custom system fee for the transaction.
|
||||
func (c *ContractInvoker) InvokeWithFeeFail(t *testing.T, message string, sysFee int64, method string, args ...interface{}) util.Uint256 {
|
||||
func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...interface{}) util.Uint256 {
|
||||
tx := c.PrepareInvokeNoSign(t, method, args...)
|
||||
c.Executor.SignTx(t, tx, sysFee, c.Signers...)
|
||||
c.AddNewBlock(t, tx)
|
||||
|
@ -100,7 +109,7 @@ func (c *ContractInvoker) InvokeWithFeeFail(t *testing.T, message string, sysFee
|
|||
|
||||
// InvokeFail invokes method with args, persists transaction and checks the error message.
|
||||
// Returns transaction hash.
|
||||
func (c *ContractInvoker) InvokeFail(t *testing.T, message string, method string, args ...interface{}) {
|
||||
func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...interface{}) {
|
||||
tx := c.PrepareInvoke(t, method, args...)
|
||||
c.AddNewBlock(t, tx)
|
||||
c.CheckFault(t, tx.Hash(), message)
|
||||
|
|
|
@ -25,7 +25,7 @@ type Contract struct {
|
|||
var contracts = make(map[string]*Contract)
|
||||
|
||||
// CompileSource compiles contract from reader and returns it's NEF, manifest and hash.
|
||||
func CompileSource(t *testing.T, sender util.Uint160, src io.Reader, opts *compiler.Options) *Contract {
|
||||
func CompileSource(t testing.TB, sender util.Uint160, src io.Reader, opts *compiler.Options) *Contract {
|
||||
// nef.NewFile() cares about version a lot.
|
||||
config.Version = "neotest"
|
||||
|
||||
|
@ -43,7 +43,7 @@ func CompileSource(t *testing.T, sender util.Uint160, src io.Reader, opts *compi
|
|||
}
|
||||
|
||||
// CompileFile compiles contract from file and returns it's NEF, manifest and hash.
|
||||
func CompileFile(t *testing.T, sender util.Uint160, srcPath string, configPath string) *Contract {
|
||||
func CompileFile(t testing.TB, sender util.Uint160, srcPath string, configPath string) *Contract {
|
||||
if c, ok := contracts[srcPath]; ok {
|
||||
return c
|
||||
}
|
||||
|
@ -66,6 +66,8 @@ func CompileFile(t *testing.T, sender util.Uint160, srcPath string, configPath s
|
|||
o.Permissions[i] = manifest.Permission(conf.Permissions[i])
|
||||
}
|
||||
o.SafeMethods = conf.SafeMethods
|
||||
o.Overloads = conf.Overloads
|
||||
o.SourceURL = conf.SourceURL
|
||||
m, err := compiler.CreateManifest(di, o)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ func (m multiSigner) Single(n int) SingleSigner {
|
|||
return NewSingleSigner(wallet.NewAccountFromPrivateKey(m.accounts[n].PrivateKey()))
|
||||
}
|
||||
|
||||
func checkMultiSigner(t *testing.T, s Signer) {
|
||||
func checkMultiSigner(t testing.TB, s Signer) {
|
||||
ms, ok := s.(multiSigner)
|
||||
require.True(t, ok, "expected to be a multi-signer")
|
||||
|
||||
|
|
|
@ -61,17 +61,17 @@ type rpcTestCase struct {
|
|||
check func(t *testing.T, e *executor, result interface{})
|
||||
}
|
||||
|
||||
const genesisBlockHash = "a4ae00f6ac7496cac14e709fbf8b8ecb4c9831d8a6ee396056af9350fcf22671"
|
||||
const testContractHash = "1ab08f5508edafa6f28e3db3227442a9e70aac52"
|
||||
const deploymentTxHash = "017c9edb217477aeb3e0c35462361209fdb7bf104dc8e285e2385af8713926b4"
|
||||
const genesisBlockHash = "f42e2ae74bbea6aa1789fdc4efa35ad55b04335442637c091eafb5b0e779dae7"
|
||||
const testContractHash = "2db7d679c538ace5f00495c9e9d8ea95f1e0f5a5"
|
||||
const deploymentTxHash = "496bccb5cb0a008ef9b7a32c459e508ef24fbb0830f82bac9162afa4ca804839"
|
||||
|
||||
const (
|
||||
verifyContractHash = "7deef31e5c616e157cdf02a5446f36d0a4eead52"
|
||||
verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c"
|
||||
verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
|
||||
verifyWithArgsContractHash = "6df009754ce475a6a5730c9e488f80e8e47bc1f1"
|
||||
nnsContractHash = "1a7530a4c6cfdd40ffed40775aa5453febab24c0"
|
||||
verifyWithArgsContractHash = "0dce75f52adb1a4c5c6eaa6a34eb26db2e5b3781"
|
||||
nnsContractHash = "ee92563903e4efd53565784080b2dbdc5c37e21f"
|
||||
nnsToken1ID = "6e656f2e636f6d"
|
||||
nfsoContractHash = "aaf8913c501e25c42877e79f04cb7c2c1ab47e57"
|
||||
nfsoContractHash = "5f9ebd6b001b54c7bc70f96e0412fcf415dfe09f"
|
||||
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
|
||||
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
||||
)
|
||||
|
@ -279,7 +279,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
result: func(e *executor) interface{} {
|
||||
return &map[string]interface{}{
|
||||
"name": "neo.com",
|
||||
"expiration": "HrL+G4YB",
|
||||
"expiration": "lhbLRl0B",
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -882,7 +882,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
name: "positive, with notifications",
|
||||
params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
|
||||
result: func(e *executor) interface{} {
|
||||
script := []byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b, 0x13, 0xc0, 0x1f, 0x0c, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x0c, 0x14, 0xc0, 0x24, 0xab, 0xeb, 0x3f, 0x45, 0xa5, 0x5a, 0x77, 0x40, 0xed, 0xff, 0x40, 0xdd, 0xcf, 0xc6, 0xa4, 0x30, 0x75, 0x1a, 0x41, 0x62, 0x7d, 0x5b, 0x52}
|
||||
script := []byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14, 0x1f, 0xe2, 0x37, 0x5c, 0xdc, 0xdb, 0xb2, 0x80, 0x40, 0x78, 0x65, 0x35, 0xd5, 0xef, 0xe4, 0x3, 0x39, 0x56, 0x92, 0xee, 0x41, 0x62, 0x7d, 0x5b, 0x52}
|
||||
return &result.Invoke{
|
||||
State: "HALT",
|
||||
GasConsumed: 32167260,
|
||||
|
@ -915,7 +915,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
chg := []storage.Operation{{
|
||||
State: "Changed",
|
||||
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
|
||||
Value: []byte{0xe8, 0x80, 0x64, 0xcb, 0x53, 0x79, 0x12},
|
||||
Value: []byte{0x1e, 0xb, 0xca, 0xeb, 0x53, 0x79, 0x12},
|
||||
}, {
|
||||
State: "Added",
|
||||
Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb},
|
||||
|
@ -927,7 +927,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
}, {
|
||||
State: "Changed",
|
||||
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
|
||||
Value: []byte{0x41, 0x01, 0x21, 0x05, 0x9e, 0x0b, 0x0b, 0x18, 0x0b},
|
||||
Value: []byte{0x41, 0x01, 0x21, 0x05, 0xf6, 0x99, 0x28, 0x2d, 0xb},
|
||||
}}
|
||||
// Can be returned in any order.
|
||||
assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
|
||||
|
@ -937,7 +937,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
name: "positive, verbose",
|
||||
params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
|
||||
result: func(e *executor) interface{} {
|
||||
script := []byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14, 0xc0, 0x24, 0xab, 0xeb, 0x3f, 0x45, 0xa5, 0x5a, 0x77, 0x40, 0xed, 0xff, 0x40, 0xdd, 0xcf, 0xc6, 0xa4, 0x30, 0x75, 0x1a, 0x41, 0x62, 0x7d, 0x5b, 0x52}
|
||||
script := []byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14, 0x1f, 0xe2, 0x37, 0x5c, 0xdc, 0xdb, 0xb2, 0x80, 0x40, 0x78, 0x65, 0x35, 0xd5, 0xef, 0xe4, 0x3, 0x39, 0x56, 0x92, 0xee, 0x41, 0x62, 0x7d, 0x5b, 0x52}
|
||||
stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib)
|
||||
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
|
||||
return &result.Invoke{
|
||||
|
@ -1205,12 +1205,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"sendrawtransaction": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["ADQSAADA2KcAAAAAABDiEgAAAAAAgBYAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsgEAYBDAAwDodkgXAAAADBQRJlu0FyUAQb4E6PokDjj1fB5WmwwU7p6iLCfjS9AUj8QQjgj3To9QSLIUwB8MCHRyYW5zZmVyDBT1Y+pAvCg9TQ4FxI6jBbPyoHNA70FifVtSOQFCDEBRp0p08GFA2rYC/Xrol8DIhXEMfVMbUJEYer1RqZSatmTjUJE9fnZtDGkQEX/zQ7yOhbnIPAZIrllUTuUBskhUKAwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CQVbnsyc="]`,
|
||||
params: `["ABsAAACWP5gAAAAAAEDaEgAAAAAAFgAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAOv87rSn7OV7Y/wuVE58QaSz0o0wv37hWY08RZFP2kYYgSPvemZiT69wf6QeAUTABJ1JosxgIUory9vXv0kkpXSgMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`,
|
||||
result: func(e *executor) interface{} { return &result.RelayResult{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.RelayResult)
|
||||
require.True(t, ok)
|
||||
expectedHash := "8ea251d812fbbdecaebfc164fb6afbd78b7db94f7dacb69421cd5d4e364522d2"
|
||||
expectedHash := "acc3e13102c211068d06ff64034d6f7e2d4db00c1703d0dec8afa73560664fe1"
|
||||
assert.Equal(t, expectedHash, res.Hash.StringLE())
|
||||
},
|
||||
},
|
||||
|
@ -1945,12 +1945,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoError(t, json.Unmarshal(res, actual))
|
||||
checkNep17TransfersAux(t, e, actual, sent, rcvd)
|
||||
}
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{14, 15, 16, 17}, []int{3, 4}) })
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{17, 18, 19, 20}, []int{3, 4}) })
|
||||
t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{11, 12}, []int{2}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{14}, []int{3}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{13, 14}, []int{3}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{15, 16}, []int{4}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{14, 15}, []int{2}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{17}, []int{3}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{16, 17}, []int{3}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{18, 19}, []int{4}) })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2086,7 +2086,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
|||
},
|
||||
{
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Amount: "46748035310",
|
||||
Amount: "47102293830",
|
||||
LastUpdated: 19,
|
||||
}},
|
||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||
|
@ -2198,7 +2198,7 @@ func checkNep11TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
}
|
||||
|
||||
func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) {
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8})
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8})
|
||||
}
|
||||
|
||||
func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) {
|
||||
|
@ -2224,8 +2224,11 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
|
||||
blockPutNewTestValue, err := e.chain.GetBlock(e.chain.GetHeaderHash(16)) // invoke `put` method of `test_contract.go` with `testkey`, `newtestvalue` args
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockPutNewTestValue.Transactions))
|
||||
require.Equal(t, 4, len(blockPutNewTestValue.Transactions))
|
||||
txPutNewTestValue := blockPutNewTestValue.Transactions[0]
|
||||
txPutValue1 := blockPutNewTestValue.Transactions[1] // invoke `put` method of `test_contract.go` with `aa`, `v1` args
|
||||
txPutValue2 := blockPutNewTestValue.Transactions[2] // invoke `put` method of `test_contract.go` with `aa10`, `v2` args
|
||||
txPutValue3 := blockPutNewTestValue.Transactions[3] // invoke `put` method of `test_contract.go` with `aa50`, `v3` args
|
||||
|
||||
blockSetRecord, err := e.chain.GetBlock(e.chain.GetHeaderHash(15)) // add type A record to `neo.com` domain via NNS
|
||||
require.NoError(t, err)
|
||||
|
@ -2336,6 +2339,30 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
Index: 17,
|
||||
TxHash: blockDeploy5.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockPutNewTestValue.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: "", // burn
|
||||
Amount: big.NewInt(txPutValue3.SystemFee + txPutValue3.NetworkFee).String(),
|
||||
Index: 16,
|
||||
TxHash: blockPutNewTestValue.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockPutNewTestValue.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: "", // burn
|
||||
Amount: big.NewInt(txPutValue2.SystemFee + txPutValue2.NetworkFee).String(),
|
||||
Index: 16,
|
||||
TxHash: blockPutNewTestValue.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockPutNewTestValue.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: "", // burn
|
||||
Amount: big.NewInt(txPutValue1.SystemFee + txPutValue1.NetworkFee).String(),
|
||||
Index: 16,
|
||||
TxHash: blockPutNewTestValue.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockPutNewTestValue.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
2
pkg/rpc/server/testdata/verify/verification_contract.yml
vendored
Normal file
2
pkg/rpc/server/testdata/verify/verification_contract.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
name: "Verify"
|
||||
sourceurl: https://github.com/nspcc-dev/neo-go/
|
2
pkg/rpc/server/testdata/verify_args/verification_with_args_contract.yml
vendored
Normal file
2
pkg/rpc/server/testdata/verify_args/verification_with_args_contract.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
name: "Verify with args"
|
||||
sourceurl: https://github.com/nspcc-dev/neo-go/
|
Loading…
Reference in a new issue