forked from TrueCloudLab/neoneo-go
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:
|
gomod:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys: [deps-]
|
keys: [deps-v2-]
|
||||||
- run:
|
- run:
|
||||||
name: Download go module dependencies
|
name: Download go module dependencies
|
||||||
command: go mod download
|
command: go mod download
|
||||||
- save_cache:
|
- 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]
|
paths: [/home/circleci/go/pkg/mod]
|
||||||
|
|
||||||
jobs:
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkVerifyWitness(t *testing.B) {
|
func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
|
||||||
bc := newTestChain(t)
|
bc, acc := chain.NewSingle(t)
|
||||||
acc, err := wallet.NewAccount()
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
require.NoError(t, err)
|
tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
|
||||||
|
|
||||||
tx := bc.newTestTx(acc.Contract.ScriptHash(), []byte{byte(opcode.PUSH1)})
|
|
||||||
require.NoError(t, acc.SignTx(netmode.UnitTestNet, tx))
|
|
||||||
|
|
||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
for n := 0; n < t.N; n++ {
|
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) {
|
func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBlock, nBlocksToTake int) {
|
||||||
var (
|
var (
|
||||||
nonce uint32 = 1
|
|
||||||
chainHeight = 2_100 // constant chain height to be able to compare paging results
|
chainHeight = 2_100 // constant chain height to be able to compare paging results
|
||||||
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
|
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
|
||||||
state.TokenTransferBatchSize/32 // shift
|
state.TokenTransferBatchSize/32 // shift
|
||||||
)
|
)
|
||||||
|
|
||||||
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
|
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
|
||||||
gasHash := bc.contracts.GAS.Hash
|
|
||||||
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||||
|
|
||||||
acc := random.Uint160()
|
acc := random.Uint160()
|
||||||
|
from := e.Validator.ScriptHash()
|
||||||
|
|
||||||
for j := 0; j < chainHeight; j++ {
|
for j := 0; j < chainHeight; j++ {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := 0; i < transfersPerBlock; i++ {
|
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)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
require.NoError(t, w.Err)
|
|
||||||
}
|
}
|
||||||
|
require.NoError(t, w.Err)
|
||||||
script := w.Bytes()
|
script := w.Bytes()
|
||||||
tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
|
tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
|
||||||
|
tx.NetworkFee = 1_0000_000
|
||||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
tx.Nonce = nonce
|
tx.Nonce = neotest.Nonce()
|
||||||
nonce++
|
tx.Signers = []transaction.Signer{{Account: from, Scopes: transaction.CalledByEntry}}
|
||||||
tx.Signers = []transaction.Signer{{
|
require.NoError(t, validators.SignTx(netmode.UnitTestNet, tx))
|
||||||
Account: testchain.MultisigScriptHash(),
|
e.AddNewBlock(t, tx)
|
||||||
Scopes: transaction.CalledByEntry,
|
e.CheckHalt(t, tx.Hash())
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newestB, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()) - startFromBlock + 1))
|
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)
|
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.
|
// GetStoragePrice returns current storage price.
|
||||||
func (bc *Blockchain) GetStoragePrice() int64 {
|
func (bc *Blockchain) GetStoragePrice() int64 {
|
||||||
if bc.BlockHeight() == 0 {
|
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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"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/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"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/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/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"
|
||||||
"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/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"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zaptest"
|
"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
|
// newTestChain should be called before newBlock invocation to properly setup
|
||||||
// global state.
|
// global state.
|
||||||
func newTestChain(t testing.TB) *Blockchain {
|
func newTestChain(t testing.TB) *Blockchain {
|
||||||
|
@ -67,38 +33,6 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c
|
||||||
return chain
|
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 {
|
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
||||||
chain, err := initTestChainNoCheck(t, st, f)
|
chain, err := initTestChainNoCheck(t, st, f)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -192,646 +126,3 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
|
||||||
}
|
}
|
||||||
return blocks, nil
|
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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"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/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"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/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/iterator"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
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/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
@ -39,10 +34,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
|
||||||
|
|
||||||
// Tests are taken from
|
// Tests are taken from
|
||||||
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
|
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
|
||||||
func TestRuntimeGetRandomCompatibility(t *testing.T) {
|
func TestRuntimeGetRandomCompatibility(t *testing.T) {
|
||||||
|
@ -72,29 +68,6 @@ func TestRuntimeGetRandomCompatibility(t *testing.T) {
|
||||||
require.Equal(t, "217172703763162599519098299724476526911", ic.VM.Estack().Pop().BigInt().String())
|
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 {
|
func getSharpTestTx(sender util.Uint160) *transaction.Transaction {
|
||||||
tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
|
tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
|
||||||
tx.Nonce = 0
|
tx.Nonce = 0
|
||||||
|
@ -116,81 +89,6 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
|
||||||
return b
|
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) {
|
func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
v, ic, _ := createVM(t)
|
v, ic, _ := createVM(t)
|
||||||
|
|
||||||
|
@ -235,7 +133,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||||
v, ic, bc := createVM(t)
|
v, ic, bc := createVM(t)
|
||||||
|
|
||||||
cs, _ := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
|
|
||||||
ic.Invocations[hash.Hash160([]byte{2})] = 42
|
ic.Invocations[hash.Hash160([]byte{2})] = 42
|
||||||
|
@ -661,432 +559,6 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C
|
||||||
return v, contractState, context, chain
|
return v, contractState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
helper1ContractNEFPath = filepath.Join("test_data", "management_helper", "management_helper1.nef")
|
|
||||||
helper1ContractManifestPath = filepath.Join("test_data", "management_helper", "management_helper1.manifest.json")
|
|
||||||
helper2ContractNEFPath = filepath.Join("test_data", "management_helper", "management_helper2.nef")
|
|
||||||
helper2ContractManifestPath = filepath.Join("test_data", "management_helper", "management_helper2.manifest.json")
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestGenerateManagementHelperContracts generates 2 helper contracts second of which is
|
|
||||||
// allowed to call the first. It uses test chain to define Management and StdLib
|
|
||||||
// native hashes and saves generated NEF and manifest to ../test_data/management_contract folder.
|
|
||||||
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
|
|
||||||
func TestGenerateManagementHelperContracts(t *testing.T) {
|
|
||||||
const saveState = false
|
|
||||||
|
|
||||||
bc := newTestChain(t)
|
|
||||||
mgmtHash := bc.contracts.Management.Hash
|
|
||||||
stdHash := bc.contracts.Std.Hash
|
|
||||||
neoHash := bc.contracts.NEO.Hash
|
|
||||||
singleChainValidator := testchain.PrivateKey(testchain.IDToOrder(0))
|
|
||||||
acc := wallet.NewAccountFromPrivateKey(singleChainValidator)
|
|
||||||
require.NoError(t, acc.ConvertMultisig(1, keys.PublicKeys{singleChainValidator.PublicKey()}))
|
|
||||||
singleChainValidatorHash := acc.Contract.ScriptHash()
|
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
|
||||||
addOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.RET)
|
|
||||||
addMultiOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.ADD, opcode.ADD, opcode.RET)
|
|
||||||
ret7Off := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PUSH7, opcode.RET)
|
|
||||||
dropOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP, opcode.RET)
|
|
||||||
initOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.INITSSLOT, 1, opcode.PUSH3, opcode.STSFLD0, opcode.RET)
|
|
||||||
add3Off := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.ADD, opcode.RET)
|
|
||||||
invalidRetOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PUSH1, opcode.PUSH2, opcode.RET)
|
|
||||||
justRetOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
verifyOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB,
|
|
||||||
opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET)
|
|
||||||
deployOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+1+1+39+3)
|
|
||||||
emit.String(w.BinWriter, "create") // 8 bytes
|
|
||||||
emit.Int(w.BinWriter, 2) // 1 byte
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
|
|
||||||
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+1+1+39+3, opcode.RET)
|
|
||||||
emit.String(w.BinWriter, "update") // 8 bytes
|
|
||||||
emit.Int(w.BinWriter, 2) // 1 byte
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
|
|
||||||
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET)
|
|
||||||
putValOff := w.Len()
|
|
||||||
emit.String(w.BinWriter, "initial")
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
getValOff := w.Len()
|
|
||||||
emit.String(w.BinWriter, "initial")
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGet)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
delValOff := w.Len()
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageDelete)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
onNEP17PaymentOff := w.Len()
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
|
|
||||||
emit.Int(w.BinWriter, 4)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.String(w.BinWriter, "LastPayment")
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
onNEP11PaymentOff := w.Len()
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
|
|
||||||
emit.Int(w.BinWriter, 5)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.String(w.BinWriter, "LostPayment")
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
update3Off := w.Len()
|
|
||||||
emit.Int(w.BinWriter, 3)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.JMP, 2+1)
|
|
||||||
updateOff := w.Len()
|
|
||||||
emit.Int(w.BinWriter, 2)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, mgmtHash, "update", callflag.All)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
destroyOff := w.Len()
|
|
||||||
emit.AppCall(w.BinWriter, mgmtHash, "destroy", callflag.All)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
invalidStackOff := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND) // recursive array
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetReadOnlyContext) // interop item
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
callT0Off := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.PUSH1, opcode.ADD, opcode.RET)
|
|
||||||
callT1Off := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.CALLT, 1, 0, opcode.RET)
|
|
||||||
callT2Off := w.Len()
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.RET)
|
|
||||||
burnGasOff := w.Len()
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeBurnGas)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
invocCounterOff := w.Len()
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetInvocationCounter)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
|
|
||||||
script := w.Bytes()
|
|
||||||
m := manifest.NewManifest("TestMain")
|
|
||||||
m.ABI.Methods = []manifest.Method{
|
|
||||||
{
|
|
||||||
Name: "add",
|
|
||||||
Offset: addOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("addend1", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("addend2", smartcontract.IntegerType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "add",
|
|
||||||
Offset: addMultiOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("addend1", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("addend2", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("addend3", smartcontract.IntegerType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "ret7",
|
|
||||||
Offset: ret7Off,
|
|
||||||
Parameters: []manifest.Parameter{},
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "drop",
|
|
||||||
Offset: dropOff,
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: manifest.MethodInit,
|
|
||||||
Offset: initOff,
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "add3",
|
|
||||||
Offset: add3Off,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("addend", smartcontract.IntegerType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "invalidReturn",
|
|
||||||
Offset: invalidRetOff,
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "justReturn",
|
|
||||||
Offset: justRetOff,
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: manifest.MethodVerify,
|
|
||||||
Offset: verifyOff,
|
|
||||||
ReturnType: smartcontract.BoolType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: manifest.MethodDeploy,
|
|
||||||
Offset: deployOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("data", smartcontract.AnyType),
|
|
||||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "getValue",
|
|
||||||
Offset: getValOff,
|
|
||||||
ReturnType: smartcontract.StringType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "putValue",
|
|
||||||
Offset: putValOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("value", smartcontract.StringType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "delValue",
|
|
||||||
Offset: delValOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("key", smartcontract.StringType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: manifest.MethodOnNEP11Payment,
|
|
||||||
Offset: onNEP11PaymentOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
|
|
||||||
manifest.NewParameter("data", smartcontract.AnyType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: manifest.MethodOnNEP17Payment,
|
|
||||||
Offset: onNEP17PaymentOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("data", smartcontract.AnyType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "update",
|
|
||||||
Offset: updateOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("nef", smartcontract.ByteArrayType),
|
|
||||||
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "update",
|
|
||||||
Offset: update3Off,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("nef", smartcontract.ByteArrayType),
|
|
||||||
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
|
|
||||||
manifest.NewParameter("data", smartcontract.AnyType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "destroy",
|
|
||||||
Offset: destroyOff,
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "invalidStack",
|
|
||||||
Offset: invalidStackOff,
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "callT0",
|
|
||||||
Offset: callT0Off,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("address", smartcontract.Hash160Type),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "callT1",
|
|
||||||
Offset: callT1Off,
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "callT2",
|
|
||||||
Offset: callT2Off,
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "burnGas",
|
|
||||||
Offset: burnGasOff,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "invocCounter",
|
|
||||||
Offset: invocCounterOff,
|
|
||||||
ReturnType: smartcontract.IntegerType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
m.Permissions = make([]manifest.Permission, 2)
|
|
||||||
m.Permissions[0].Contract.Type = manifest.PermissionHash
|
|
||||||
m.Permissions[0].Contract.Value = bc.contracts.NEO.Hash
|
|
||||||
m.Permissions[0].Methods.Add("balanceOf")
|
|
||||||
|
|
||||||
m.Permissions[1].Contract.Type = manifest.PermissionHash
|
|
||||||
m.Permissions[1].Contract.Value = util.Uint160{}
|
|
||||||
m.Permissions[1].Methods.Add("method")
|
|
||||||
|
|
||||||
// Generate NEF file.
|
|
||||||
ne, err := nef.NewFile(script)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
ne.Tokens = []nef.MethodToken{
|
|
||||||
{
|
|
||||||
Hash: neoHash,
|
|
||||||
Method: "balanceOf",
|
|
||||||
ParamCount: 1,
|
|
||||||
HasReturn: true,
|
|
||||||
CallFlag: callflag.ReadStates,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Hash: util.Uint160{},
|
|
||||||
Method: "method",
|
|
||||||
HasReturn: true,
|
|
||||||
CallFlag: callflag.ReadStates,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
ne.Checksum = ne.CalculateChecksum()
|
|
||||||
|
|
||||||
// Write first NEF file.
|
|
||||||
bytes, err := ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(helper1ContractNEFPath, bytes, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write first manifest file.
|
|
||||||
mData, err := json.Marshal(m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(helper1ContractManifestPath, mData, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create hash of the first contract assuming that sender is single-chain validator.
|
|
||||||
h := state.CreateContractHash(singleChainValidatorHash, ne.Checksum, m.Name)
|
|
||||||
|
|
||||||
currScript := []byte{byte(opcode.RET)}
|
|
||||||
m = manifest.NewManifest("TestAux")
|
|
||||||
perm := manifest.NewPermission(manifest.PermissionHash, h)
|
|
||||||
perm.Methods.Add("add")
|
|
||||||
perm.Methods.Add("drop")
|
|
||||||
perm.Methods.Add("add3")
|
|
||||||
perm.Methods.Add("invalidReturn")
|
|
||||||
perm.Methods.Add("justReturn")
|
|
||||||
perm.Methods.Add("getValue")
|
|
||||||
m.Permissions = append(m.Permissions, *perm)
|
|
||||||
ne, err = nef.NewFile(currScript)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write second NEF file.
|
|
||||||
bytes, err = ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(helper2ContractNEFPath, bytes, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write second manifest file.
|
|
||||||
mData, err = json.Marshal(m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(helper2ContractManifestPath, mData, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
require.False(t, saveState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
|
||||||
func getTestContractState(t *testing.T, id1, id2 int32, sender2 util.Uint160) (*state.Contract, *state.Contract) {
|
|
||||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
|
||||||
|
|
||||||
neBytes, err := os.ReadFile(helper1ContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
|
|
||||||
ne, err := nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err := os.ReadFile(helper1ContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest1: %w", errNotFound))
|
|
||||||
m := &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cs1 := &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
neBytes, err = os.ReadFile(helper2ContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
|
|
||||||
ne, err = nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err = os.ReadFile(helper2ContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest2: %w", errNotFound))
|
|
||||||
m = &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Retrieve hash of the first contract from the permissions of the second contract.
|
|
||||||
require.Equal(t, 1, len(m.Permissions))
|
|
||||||
require.Equal(t, manifest.PermissionHash, m.Permissions[0].Contract.Type)
|
|
||||||
cs1.Hash = m.Permissions[0].Contract.Hash()
|
|
||||||
|
|
||||||
cs2 := &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id2,
|
|
||||||
Hash: state.CreateContractHash(sender2, ne.Checksum, m.Name),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cs1, cs2
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
|
func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
|
||||||
ic.SpawnVM()
|
ic.SpawnVM()
|
||||||
ic.VM.LoadScriptWithFlags(script, callflag.AllowCall)
|
ic.VM.LoadScriptWithFlags(script, callflag.AllowCall)
|
||||||
|
@ -1108,7 +580,7 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui
|
||||||
func TestContractCall(t *testing.T) {
|
func TestContractCall(t *testing.T) {
|
||||||
_, ic, bc := createVM(t)
|
_, ic, bc := createVM(t)
|
||||||
|
|
||||||
cs, currCs := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
cs, currCs := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
||||||
|
|
||||||
|
@ -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)
|
return fmt.Errorf("native contract of version %d is not active", version)
|
||||||
}
|
}
|
||||||
var c interop.Contract
|
var c interop.Contract
|
||||||
|
curr := ic.VM.GetCurrentScriptHash()
|
||||||
for _, ctr := range ic.Natives {
|
for _, ctr := range ic.Natives {
|
||||||
if ctr.Metadata().Hash == ic.VM.GetCurrentScriptHash() {
|
if ctr.Metadata().Hash == curr {
|
||||||
c = ctr
|
c = ctr
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c == nil {
|
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
|
history := c.Metadata().UpdateHistory
|
||||||
if len(history) == 0 {
|
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))
|
gas := ic.Chain.GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes))
|
||||||
if isDeploy {
|
if isDeploy {
|
||||||
fee := m.GetMinimumDeploymentFee(ic.DAO)
|
fee := m.minimumDeploymentFee(ic.DAO)
|
||||||
if fee > gas {
|
if fee > gas {
|
||||||
gas = fee
|
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 {
|
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.
|
// minimumDeploymentFee returns the minimum required fee for contract deploy.
|
||||||
func (m *Management) GetMinimumDeploymentFee(dao *dao.Simple) int64 {
|
func (m *Management) minimumDeploymentFee(dao *dao.Simple) int64 {
|
||||||
return getIntWithKey(m.ID, dao, keyMinimumDeploymentFee)
|
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) {
|
func checkNodeRoles(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, index uint32, res keys.PublicKeys) {
|
||||||
if ok {
|
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))
|
require.Equal(t, 1, len(stack))
|
||||||
arr := stack[0].Value().([]stackitem.Item)
|
arr := stack[0].Value().([]stackitem.Item)
|
||||||
require.Equal(t, len(res), len(arr))
|
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.
|
ledgerInvoker.Invoke(t, e.Chain.BlockHeight(), "currentIndex") // Adds a block.
|
||||||
b := e.GetBlockByIndex(t, int(e.Chain.BlockHeight()))
|
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))
|
require.Equal(t, 1, len(stack))
|
||||||
actual, ok := stack[0].Value().([]stackitem.Item)
|
actual, ok := stack[0].Value().([]stackitem.Item)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
|
@ -3,17 +3,14 @@ package native_test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -23,20 +20,12 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
helper1ContractNEFPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper1.nef")
|
|
||||||
helper1ContractManifestPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper1.manifest.json")
|
|
||||||
helper2ContractNEFPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper2.nef")
|
|
||||||
helper2ContractManifestPath = filepath.Join("..", "..", "test_data", "management_helper", "management_helper2.manifest.json")
|
|
||||||
)
|
|
||||||
|
|
||||||
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Management)
|
return newNativeClient(t, nativenames.Management)
|
||||||
}
|
}
|
||||||
|
@ -45,62 +34,11 @@ func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
||||||
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
|
||||||
func getTestContractState(t *testing.T, id1, id2 int32, sender2 util.Uint160) (*state.Contract, *state.Contract) {
|
|
||||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
|
||||||
|
|
||||||
neBytes, err := os.ReadFile(helper1ContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef1: %w", errNotFound))
|
|
||||||
ne, err := nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err := os.ReadFile(helper1ContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest1: %w", errNotFound))
|
|
||||||
m := &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cs1 := &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
neBytes, err = os.ReadFile(helper2ContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef2: %w", errNotFound))
|
|
||||||
ne, err = nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err = os.ReadFile(helper2ContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest2: %w", errNotFound))
|
|
||||||
m = &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Retrieve hash of the first contract from the permissions of the second contract.
|
|
||||||
require.Equal(t, 1, len(m.Permissions))
|
|
||||||
require.Equal(t, manifest.PermissionHash, m.Permissions[0].Contract.Type)
|
|
||||||
cs1.Hash = m.Permissions[0].Contract.Hash()
|
|
||||||
|
|
||||||
cs2 := &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id2,
|
|
||||||
Hash: state.CreateContractHash(sender2, ne.Checksum, m.Name),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cs1, cs2
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManagement_ContractDeploy(t *testing.T) {
|
func TestManagement_ContractDeploy(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.Committee.ScriptHash())
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.Committee.ScriptHash())
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -301,7 +239,7 @@ func TestManagement_StartFromHeight(t *testing.T) {
|
||||||
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
|
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Management))
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -329,7 +267,7 @@ func TestManagement_DeployManifestOverflow(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
manif1, err := json.Marshal(cs1.Manifest)
|
manif1, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||||
|
@ -359,7 +297,7 @@ func TestManagement_ContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
cs1.ID = 1
|
cs1.ID = 1
|
||||||
cs1.Hash = state.CreateContractHash(c.CommitteeHash, cs1.NEF.Checksum, cs1.Manifest.Name)
|
cs1.Hash = state.CreateContractHash(c.CommitteeHash, cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||||
|
@ -400,7 +338,7 @@ func TestManagement_ContractUpdate(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
// Allow calling management contract.
|
// Allow calling management contract.
|
||||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
|
@ -535,7 +473,7 @@ func TestManagement_GetContract(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nefBytes, err := cs1.NEF.Bytes()
|
nefBytes, err := cs1.NEF.Bytes()
|
||||||
|
@ -560,7 +498,7 @@ func TestManagement_ContractDestroy(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
cs1, _ := getTestContractState(t, 1, 2, c.CommitteeHash)
|
cs1, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, c.CommitteeHash)
|
||||||
// Allow calling management contract.
|
// Allow calling management contract.
|
||||||
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
manifestBytes, err := json.Marshal(cs1.Manifest)
|
manifestBytes, err := json.Marshal(cs1.Manifest)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
@ -276,7 +277,7 @@ func TestNEO_TransferOnPayment(t *testing.T) {
|
||||||
e := neoValidatorsInvoker.Executor
|
e := neoValidatorsInvoker.Executor
|
||||||
managementValidatorsInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
managementValidatorsInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
|
||||||
|
|
||||||
cs, _ := getTestContractState(t, 1, 2, e.CommitteeHash)
|
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, e.CommitteeHash)
|
||||||
cs.Hash = state.CreateContractHash(e.Validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
cs.Hash = state.CreateContractHash(e.Validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
||||||
manifB, err := json.Marshal(cs.Manifest)
|
manifB, err := json.Marshal(cs.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -2,32 +2,28 @@ package native_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal", "contracts")
|
||||||
|
|
||||||
func newOracleClient(t *testing.T) *neotest.ContractInvoker {
|
func newOracleClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Oracle)
|
return newNativeClient(t, nativenames.Oracle)
|
||||||
}
|
}
|
||||||
|
@ -36,36 +32,6 @@ func TestGetSetPrice(t *testing.T) {
|
||||||
testGetSet(t, newOracleClient(t), "Price", native.DefaultOracleRequestPrice, 1, math.MaxInt64)
|
testGetSet(t, newOracleClient(t), "Price", native.DefaultOracleRequestPrice, 1, math.MaxInt64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOracleContractState reads pre-compiled oracle contract generated by
|
|
||||||
// TestGenerateOracleContract and returns its state.
|
|
||||||
func getOracleContractState(t *testing.T, sender util.Uint160, id int32) *state.Contract {
|
|
||||||
var (
|
|
||||||
oracleContractNEFPath = filepath.Join("..", "..", "test_data", "oracle_contract", "oracle.nef")
|
|
||||||
oracleContractManifestPath = filepath.Join("..", "..", "test_data", "oracle_contract", "oracle.manifest.json")
|
|
||||||
)
|
|
||||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
|
||||||
|
|
||||||
neBytes, err := os.ReadFile(oracleContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef: %w", errNotFound))
|
|
||||||
ne, err := nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err := os.ReadFile(oracleContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest: %w", errNotFound))
|
|
||||||
m := &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Hash: state.CreateContractHash(sender, ne.Checksum, m.Name),
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func putOracleRequest(t *testing.T, oracleInvoker *neotest.ContractInvoker,
|
func putOracleRequest(t *testing.T, oracleInvoker *neotest.ContractInvoker,
|
||||||
url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) {
|
url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) {
|
||||||
var filtItem interface{}
|
var filtItem interface{}
|
||||||
|
@ -86,7 +52,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
designationCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
|
designationCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
|
||||||
gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
|
gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
|
||||||
|
|
||||||
cs := getOracleContractState(t, e.Validator.ScriptHash(), 1)
|
cs := contracts.GetOracleContractState(t, pathToInternalContracts, e.Validator.ScriptHash(), 1)
|
||||||
nBytes, err := cs.NEF.Bytes()
|
nBytes, err := cs.NEF.Bytes()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mBytes, err := json.Marshal(cs.Manifest)
|
mBytes, err := json.Marshal(cs.Manifest)
|
||||||
|
|
|
@ -86,9 +86,7 @@ func newOracle() *Oracle {
|
||||||
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)}
|
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)}
|
||||||
defer o.UpdateHash()
|
defer o.UpdateHash()
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
o.oracleScript = CreateOracleResponseScript(o.Hash)
|
||||||
emit.AppCall(w.BinWriter, o.Hash, "finish", callflag.All)
|
|
||||||
o.oracleScript = w.Bytes()
|
|
||||||
|
|
||||||
desc := newDescriptor("request", smartcontract.VoidType,
|
desc := newDescriptor("request", smartcontract.VoidType,
|
||||||
manifest.NewParameter("url", smartcontract.StringType),
|
manifest.NewParameter("url", smartcontract.StringType),
|
||||||
|
@ -526,3 +524,14 @@ func (o *Oracle) updateCache(d *dao.Simple) error {
|
||||||
orc.AddRequests(reqs)
|
orc.AddRequests(reqs)
|
||||||
return nil
|
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 (
|
import (
|
||||||
"errors"
|
"encoding/json"
|
||||||
"math/big"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"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/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"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/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/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"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/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
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) {
|
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()
|
baseExecFee := bc.GetBaseExecFee()
|
||||||
chain.registerNative(tn)
|
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{
|
tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
||||||
ContractBase: state.ContractBase{
|
e.SignTx(t, tx, -1, validator)
|
||||||
ID: 1,
|
e.AddNewBlock(t, tx)
|
||||||
NEF: tn.meta.NEF,
|
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
|
||||||
Hash: tn.meta.Hash,
|
|
||||||
Manifest: tn.meta.Manifest,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// System.Contract.Call + "sum" itself + opcodes for pushing arguments.
|
// Enough for Call and other opcodes, but not enough for "transfer" call.
|
||||||
price := int64(testSumCPUFee * chain.GetBaseExecFee() * 2)
|
tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
|
||||||
price += testSumStorageFee * chain.GetStoragePrice()
|
e.SignTx(t, tx, price-1, validator)
|
||||||
price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8)
|
e.AddNewBlock(t, tx)
|
||||||
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL, opcode.PUSHDATA1, opcode.PUSHINT8)
|
e.CheckFault(t, tx.Hash(), "gas limit exceeded")
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNativeContract_InvokeInternal(t *testing.T) {
|
func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, validator, committee := chain.NewMulti(t)
|
||||||
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||||
tn := newTestNative()
|
clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib))
|
||||||
chain.registerNative(tn)
|
require.NotNil(t, clState)
|
||||||
|
md := clState.Manifest.ABI.GetMethod("ripemd160", 1)
|
||||||
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
require.NotNil(t, md)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
t.Run("fail, bad current script hash", func(t *testing.T) {
|
||||||
|
ic := bc.GetTestVM(trigger.Application, nil, nil)
|
||||||
v := ic.SpawnVM()
|
v := ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, util.Uint160{1, 2, 3}, callflag.All)
|
fakeH := util.Uint160{1, 2, 3}
|
||||||
v.Estack().PushVal(14)
|
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
|
||||||
v.Estack().PushVal(28)
|
input := []byte{1, 2, 3, 4}
|
||||||
v.Context().Jump(sumOffset)
|
v.Estack().PushVal(input)
|
||||||
|
v.Context().Jump(md.Offset)
|
||||||
|
|
||||||
// it's prohibited to call natives directly
|
// Bad current script hash
|
||||||
require.Error(t, v.Run())
|
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) {
|
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 := ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||||
v.Estack().PushVal(14)
|
input := []byte{1, 2, 3, 4}
|
||||||
v.Estack().PushVal(28)
|
v.Estack().PushVal(input)
|
||||||
v.Context().Jump(sumOffset)
|
v.Context().Jump(md.Offset)
|
||||||
|
|
||||||
// it's prohibited to call natives before NativeUpdateHistory[0] height
|
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
||||||
require.Error(t, v.Run())
|
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
|
// Add new block => CryptoLib should be active now.
|
||||||
tn.Metadata().UpdateHistory = []uint32{0}
|
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) {
|
t.Run("success", func(t *testing.T) {
|
||||||
|
ic := bc.GetTestVM(trigger.Application, nil, nil)
|
||||||
v := ic.SpawnVM()
|
v := ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
|
||||||
v.Estack().PushVal(14)
|
input := []byte{1, 2, 3, 4}
|
||||||
v.Estack().PushVal(28)
|
v.Estack().PushVal(input)
|
||||||
v.Context().Jump(sumOffset)
|
v.Context().Jump(md.Offset)
|
||||||
|
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
|
|
||||||
value := v.Estack().Pop().BigInt()
|
value := v.Estack().Pop().Bytes()
|
||||||
require.Equal(t, int64(42), value.Int64())
|
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
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()
|
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash())
|
||||||
chain.registerNative(tn)
|
cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
|
||||||
|
manifB, err := json.Marshal(cs.Manifest)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
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) {
|
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{}{})
|
// `onNEP17Payment` will be invoked on test contract from GAS contract.
|
||||||
require.NoError(t, err)
|
gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil)
|
||||||
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))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,69 +10,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"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/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||||
bc := newTestChain(t)
|
bc := newTestChain(t)
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,33 @@
|
||||||
package core
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"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/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
t.Run("empty chain", func(t *testing.T) {
|
t.Run("empty chain", func(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, validators, committee := chain.NewMulti(t)
|
||||||
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash}, chain.contracts.Management.GetNEP17Contracts())
|
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) {
|
t.Run("basic chain", func(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
|
||||||
initBasicChain(t, chain)
|
c.P2PSigExtensions = true // `initBasicChain` requires Notary enabled
|
||||||
rublesHash, err := chain.GetContractScriptHash(1)
|
})
|
||||||
require.NoError(t, err)
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash, rublesHash}, chain.contracts.Management.GetNEP17Contracts())
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"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/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
"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/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -40,96 +37,88 @@ func BenchmarkNEO_GetGASPerVote(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
|
||||||
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
|
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
|
||||||
|
cfg := bc.GetConfig()
|
||||||
|
|
||||||
neo := bc.contracts.NEO
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
neoHash := e.NativeHash(t, nativenames.Neo)
|
||||||
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, tx)
|
gasHash := e.NativeHash(t, nativenames.Gas)
|
||||||
ic.SpawnVM()
|
neoSuperInvoker := e.NewInvoker(neoHash, validators, committee)
|
||||||
ic.Block = bc.newBlock(tx)
|
neoValidatorsInvoker := e.ValidatorInvoker(neoHash)
|
||||||
|
gasValidatorsInvoker := e.ValidatorInvoker(gasHash)
|
||||||
advanceChain := func(t *testing.B, count int) {
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
|
||||||
ic.Block.Index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vote for new committee.
|
// Vote for new committee.
|
||||||
sz := testchain.CommitteeSize()
|
sz := len(cfg.StandbyCommittee)
|
||||||
accs := make([]*wallet.Account, sz)
|
voters := make([]*wallet.Account, sz)
|
||||||
candidates := make(keys.PublicKeys, 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++ {
|
for i := 0; i < sz; i++ {
|
||||||
priv, err := keys.NewPrivateKey()
|
priv, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
candidates[i] = priv.PublicKey()
|
candidates[i] = priv.PublicKey()
|
||||||
accs[i], err = wallet.NewAccount()
|
voters[i], err = wallet.NewAccount()
|
||||||
require.NoError(t, err)
|
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()
|
to := voters[i].Contract.ScriptHash()
|
||||||
w := io.NewBufBinWriter()
|
transferNeoTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, big.NewInt(int64(sz-i)*1000000).Int64(), nil)
|
||||||
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All,
|
txs = append(txs, transferNeoTx)
|
||||||
neoOwner.BytesBE(), to.BytesBE(),
|
|
||||||
big.NewInt(int64(sz-i)*1000000).Int64(), nil)
|
transferGasTx := gasValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, int64(1_000_000_000), nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
txs = append(txs, transferGasTx)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock(txs...)))
|
e.AddNewBlock(t, txs...)
|
||||||
for _, tx := range 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++ {
|
for i := 0; i < sz; i++ {
|
||||||
priv := accs[i].PrivateKey()
|
priv := voters[i].PrivateKey()
|
||||||
h := priv.GetScriptHash()
|
h := priv.GetScriptHash()
|
||||||
setSigner(tx, h)
|
voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
|
||||||
ic.VM.Load(priv.PublicKey().GetVerificationScript())
|
voteTxs = append(voteTxs, voteTx)
|
||||||
require.NoError(t, neo.VoteInternal(ic, h, candidates[i]))
|
}
|
||||||
|
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.
|
// 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.
|
// Transfer some more NEO to first voter to update his balance height.
|
||||||
to := accs[0].Contract.ScriptHash()
|
to := voters[0].Contract.ScriptHash()
|
||||||
w := io.NewBufBinWriter()
|
neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), to, int64(1), nil)
|
||||||
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)
|
|
||||||
|
|
||||||
// Advance chain one more time to avoid same start/end rewarding bounds.
|
// Advance chain one more time to avoid same start/end rewarding bounds.
|
||||||
advanceChain(t, rewardDistance)
|
e.GenerateNewBlocks(t, rewardDistance)
|
||||||
end := bc.BlockHeight()
|
end := bc.BlockHeight()
|
||||||
|
|
||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
t.StartTimer()
|
t.StartTimer()
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
_, err := neo.CalculateBonus(ic.DAO, to, end)
|
_, err := bc.CalculateClaimable(to, end)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
t.StopTimer()
|
t.StopTimer()
|
||||||
|
|
|
@ -1,54 +1,67 @@
|
||||||
package core
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"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/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func transferFundsToCommittee(t *testing.T, chain *Blockchain) {
|
func TestPolicy_FeePerByte(t *testing.T) {
|
||||||
transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(),
|
bc, _, _ := chain.NewMulti(t)
|
||||||
chain.contracts.GAS.Hash, 1000_00000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFeePerByte(t *testing.T) {
|
|
||||||
chain := newTestChain(t)
|
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.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))
|
require.Equal(t, 1000, int(n))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExecFeeFactor(t *testing.T) {
|
func TestPolicy_ExecFeeFactor(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, _, _ := chain.NewMulti(t)
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.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)
|
require.EqualValues(t, interop.DefaultBaseExecFee, n)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoragePrice(t *testing.T) {
|
func TestPolicy_StoragePrice(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, validators, committee := chain.NewMulti(t)
|
||||||
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.T) {
|
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)
|
require.Equal(t, int64(native.DefaultStoragePrice), n)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockedAccounts(t *testing.T) {
|
func TestPolicy_BlockedAccounts(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
bc, validators, committee := chain.NewMulti(t)
|
||||||
transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(),
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
chain.contracts.GAS.Hash, 100_00000000)
|
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) {
|
t.Run("isBlocked, internal method", func(t *testing.T) {
|
||||||
isBlocked := chain.contracts.Policy.IsBlockedInternal(chain.dao, random.Uint160())
|
tx := policyUnluckyInvoker.PrepareInvoke(t, "getStoragePrice")
|
||||||
require.Equal(t, false, isBlocked)
|
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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -10,29 +11,32 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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"
|
||||||
|
"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/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
"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/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"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/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/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/network/payload"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/services/notary"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"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/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap/zaptest"
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var notaryModulePath = filepath.Join("..", "services", "notary")
|
func getTestNotary(t *testing.T, bc *core.Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
|
||||||
|
|
||||||
func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
|
|
||||||
mainCfg := config.P2PNotary{
|
mainCfg := config.P2PNotary{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
UnlockWallet: config.Wallet{
|
UnlockWallet: config.Wallet{
|
||||||
|
@ -46,7 +50,7 @@ func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx f
|
||||||
Log: zaptest.NewLogger(t),
|
Log: zaptest.NewLogger(t),
|
||||||
}
|
}
|
||||||
mp := mempool.New(10, 1, true)
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
w, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, walletPath))
|
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) {
|
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 (
|
var (
|
||||||
nonce uint32
|
nonce uint32
|
||||||
nvbDiffFallback uint32 = 20
|
nvbDiffFallback uint32 = 20
|
||||||
|
@ -145,8 +156,9 @@ func TestNotary(t *testing.T) {
|
||||||
mp1.StopSubscriptions()
|
mp1.StopSubscriptions()
|
||||||
})
|
})
|
||||||
|
|
||||||
notaryNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
notaryNodes := []interface{}{acc1.PrivateKey().PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()}
|
||||||
bc.setNodesByRole(t, true, noderoles.P2PNotary, notaryNodes)
|
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||||
|
int64(roles.P2PNotary), notaryNodes)
|
||||||
|
|
||||||
type requester struct {
|
type requester struct {
|
||||||
accounts []*wallet.Account
|
accounts []*wallet.Account
|
||||||
|
@ -193,7 +205,7 @@ func TestNotary(t *testing.T) {
|
||||||
VerificationScript: []byte{},
|
VerificationScript: []byte{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = requester.SignTx(testchain.Network(), fallback)
|
err = requester.SignTx(netmode.UnitTestNet, fallback)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
|
@ -251,7 +263,7 @@ func TestNotary(t *testing.T) {
|
||||||
for j := range main.Scripts {
|
for j := range main.Scripts {
|
||||||
main.Scripts[j].VerificationScript = verificationScripts[j]
|
main.Scripts[j].VerificationScript = verificationScripts[j]
|
||||||
if i == 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
|
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())
|
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
|
||||||
|
|
||||||
for i := 0; i < len(completedTx.Scripts)-1; i++ {
|
for i := 0; i < len(completedTx.Scripts)-1; i++ {
|
||||||
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx)
|
_, err := bc.VerifyWitness(completedTx.Signers[i].Account, completedTx, &completedTx.Scripts[i], -1)
|
||||||
_, err := bc.verifyHashAgainstScript(completedTx.Signers[i].Account, &completedTx.Scripts[i], interopCtx, -1)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
require.Equal(t, transaction.Witness{
|
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{},
|
VerificationScript: []byte{},
|
||||||
}, completedTx.Scripts[len(completedTx.Scripts)-1])
|
}, completedTx.Scripts[len(completedTx.Scripts)-1])
|
||||||
} else {
|
} else {
|
||||||
|
@ -316,15 +327,14 @@ func TestNotary(t *testing.T) {
|
||||||
require.Equal(t, 2, len(completedTx.Signers))
|
require.Equal(t, 2, len(completedTx.Signers))
|
||||||
require.Equal(t, 2, len(completedTx.Scripts))
|
require.Equal(t, 2, len(completedTx.Scripts))
|
||||||
require.Equal(t, transaction.Witness{
|
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{},
|
VerificationScript: []byte{},
|
||||||
}, completedTx.Scripts[0])
|
}, completedTx.Scripts[0])
|
||||||
|
|
||||||
// check that tx size was updated
|
// check that tx size was updated
|
||||||
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
|
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
|
||||||
|
|
||||||
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx)
|
_, err := bc.VerifyWitness(completedTx.Signers[1].Account, completedTx, &completedTx.Scripts[1], -1)
|
||||||
_, err := bc.verifyHashAgainstScript(completedTx.Signers[1].Account, &completedTx.Scripts[1], interopCtx, -1)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
} else {
|
} else {
|
||||||
completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash())
|
completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash())
|
||||||
|
@ -483,7 +493,8 @@ func TestNotary(t *testing.T) {
|
||||||
checkFallbackTxs(t, r, false)
|
checkFallbackTxs(t, r, false)
|
||||||
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
|
||||||
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, r, 1, false)
|
checkMainTx(t, requesters, r, 1, false)
|
||||||
checkFallbackTxs(t, r, false)
|
checkFallbackTxs(t, r, false)
|
||||||
// set account back for the next tests
|
// set account back for the next tests
|
||||||
|
@ -494,11 +505,11 @@ func TestNotary(t *testing.T) {
|
||||||
requests, requesters := checkCompleteStandardRequest(t, 3, false)
|
requests, requesters := checkCompleteStandardRequest(t, 3, false)
|
||||||
// check PostPersist with finalisation error
|
// check PostPersist with finalisation error
|
||||||
setFinalizeWithError(true)
|
setFinalizeWithError(true)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
// check PostPersist without finalisation error
|
// check PostPersist without finalisation error
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), true)
|
checkMainTx(t, requesters, requests, len(requests), true)
|
||||||
|
|
||||||
// PostPersist: complete main transaction, multisignature account
|
// PostPersist: complete main transaction, multisignature account
|
||||||
|
@ -507,12 +518,12 @@ func TestNotary(t *testing.T) {
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// check PostPersist with finalisation error
|
// check PostPersist with finalisation error
|
||||||
setFinalizeWithError(true)
|
setFinalizeWithError(true)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// check PostPersist without finalisation error
|
// check PostPersist without finalisation error
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), true)
|
checkMainTx(t, requesters, requests, len(requests), true)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
|
|
||||||
|
@ -521,15 +532,15 @@ func TestNotary(t *testing.T) {
|
||||||
requests, requesters = checkCompleteStandardRequest(t, 3, false)
|
requests, requesters = checkCompleteStandardRequest(t, 3, false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid
|
// make fallbacks valid
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// check PostPersist for valid fallbacks with finalisation error
|
// 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)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// check PostPersist for valid fallbacks without finalisation error
|
// check PostPersist for valid fallbacks without finalisation error
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, true)
|
checkFallbackTxs(t, requests, true)
|
||||||
|
|
||||||
|
@ -540,15 +551,15 @@ func TestNotary(t *testing.T) {
|
||||||
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid
|
// make fallbacks valid
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// check PostPersist for valid fallbacks with finalisation error
|
// 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)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// check PostPersist for valid fallbacks without finalisation error
|
// check PostPersist for valid fallbacks without finalisation error
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests[:nSigs], true)
|
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
|
// 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)
|
requests, requesters = checkCompleteStandardRequest(t, 5, false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid
|
// make fallbacks valid
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// some of fallbacks should fail finalisation
|
// some of fallbacks should fail finalisation
|
||||||
unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]}
|
unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]}
|
||||||
lucky := requests[1:4]
|
lucky := requests[1:4]
|
||||||
setChoosy(true)
|
setChoosy(true)
|
||||||
// check PostPersist for lucky fallbacks
|
// check PostPersist for lucky fallbacks
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, lucky, true)
|
checkFallbackTxs(t, lucky, true)
|
||||||
checkFallbackTxs(t, unluckies, false)
|
checkFallbackTxs(t, unluckies, false)
|
||||||
|
@ -574,7 +585,7 @@ func TestNotary(t *testing.T) {
|
||||||
setChoosy(false)
|
setChoosy(false)
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
// check PostPersist for unlucky fallbacks
|
// check PostPersist for unlucky fallbacks
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, lucky, true)
|
checkFallbackTxs(t, lucky, true)
|
||||||
checkFallbackTxs(t, unluckies, 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)
|
requests, requesters = checkCompleteStandardRequest(t, 5, false, 1, 2, 3, 4, 5)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// generate blocks to reach the most earlier fallback's NVB
|
// generate blocks to reach the most earlier fallback's NVB
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// check PostPersist for valid fallbacks without finalisation error
|
// check PostPersist for valid fallbacks without finalisation error
|
||||||
// Add block before allowing tx to finalize to exclude race condition when
|
// Add block before allowing tx to finalize to exclude race condition when
|
||||||
// main transaction is finalized between `finalizeWithError` restore and adding new block.
|
// main transaction is finalized between `finalizeWithError` restore and adding new block.
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
mtx.RLock()
|
mtx.RLock()
|
||||||
start := len(completedTxes)
|
start := len(completedTxes)
|
||||||
mtx.RUnlock()
|
mtx.RUnlock()
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
for i := range requests {
|
for i := range requests {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
}
|
}
|
||||||
require.Eventually(t, func() bool {
|
require.Eventually(t, func() bool {
|
||||||
mtx.RLock()
|
mtx.RLock()
|
||||||
|
@ -615,13 +626,13 @@ func TestNotary(t *testing.T) {
|
||||||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid and remove one fallback
|
// make fallbacks valid and remove one fallback
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
|
||||||
ntr1.OnRequestRemoval(requests[3])
|
ntr1.OnRequestRemoval(requests[3])
|
||||||
// non of the fallbacks should be completed
|
// non of the fallbacks should be completed
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// set account back for the next tests
|
// set account back for the next tests
|
||||||
|
@ -633,13 +644,13 @@ func TestNotary(t *testing.T) {
|
||||||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid and remove one fallback
|
// make fallbacks valid and remove one fallback
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
unlucky := requests[3]
|
unlucky := requests[3]
|
||||||
ntr1.OnRequestRemoval(unlucky)
|
ntr1.OnRequestRemoval(unlucky)
|
||||||
// rest of the fallbacks should be completed
|
// rest of the fallbacks should be completed
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests[:3], true)
|
checkFallbackTxs(t, requests[:3], true)
|
||||||
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
||||||
|
@ -648,20 +659,20 @@ func TestNotary(t *testing.T) {
|
||||||
setFinalizeWithError(true)
|
setFinalizeWithError(true)
|
||||||
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
requests, requesters = checkCompleteStandardRequest(t, 4, false)
|
||||||
// remove all fallbacks
|
// remove all fallbacks
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for i := range requests {
|
for i := range requests {
|
||||||
ntr1.OnRequestRemoval(requests[i])
|
ntr1.OnRequestRemoval(requests[i])
|
||||||
}
|
}
|
||||||
// then the whole request should be removed, i.e. there are no completed transactions
|
// then the whole request should be removed, i.e. there are no completed transactions
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
|
|
||||||
// OnRequestRemoval: signature request, remove unexisting fallback
|
// OnRequestRemoval: signature request, remove unexisting fallback
|
||||||
ntr1.OnRequestRemoval(requests[0])
|
ntr1.OnRequestRemoval(requests[0])
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
|
|
||||||
|
@ -673,13 +684,13 @@ func TestNotary(t *testing.T) {
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
// make fallbacks valid and remove the last fallback
|
// make fallbacks valid and remove the last fallback
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
unlucky = requests[nSigs-1]
|
unlucky = requests[nSigs-1]
|
||||||
ntr1.OnRequestRemoval(unlucky)
|
ntr1.OnRequestRemoval(unlucky)
|
||||||
// then (m-1) out of n fallbacks should be completed
|
// then (m-1) out of n fallbacks should be completed
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests[:nSigs-1], true)
|
checkFallbackTxs(t, requests[:nSigs-1], true)
|
||||||
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
|
||||||
|
@ -690,20 +701,20 @@ func TestNotary(t *testing.T) {
|
||||||
setFinalizeWithError(true)
|
setFinalizeWithError(true)
|
||||||
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
|
||||||
// make fallbacks valid and then remove all of them
|
// make fallbacks valid and then remove all of them
|
||||||
_, err = bc.genBlocks(int(nvbDiffFallback))
|
e.GenerateNewBlocks(t, int(nvbDiffFallback))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for i := range requests {
|
for i := range requests {
|
||||||
ntr1.OnRequestRemoval(requests[i])
|
ntr1.OnRequestRemoval(requests[i])
|
||||||
}
|
}
|
||||||
// then the whole request should be removed, i.e. there are no completed transactions
|
// then the whole request should be removed, i.e. there are no completed transactions
|
||||||
setFinalizeWithError(false)
|
setFinalizeWithError(false)
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
|
|
||||||
// // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this
|
// // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this
|
||||||
ntr1.OnRequestRemoval(requests[0])
|
ntr1.OnRequestRemoval(requests[0])
|
||||||
require.NoError(t, bc.AddBlock(bc.newBlock()))
|
e.AddNewBlock(t)
|
||||||
checkMainTx(t, requesters, requests, len(requests), false)
|
checkMainTx(t, requesters, requests, len(requests), false)
|
||||||
checkFallbackTxs(t, requests, false)
|
checkFallbackTxs(t, requests, false)
|
||||||
|
|
||||||
|
@ -712,11 +723,11 @@ func TestNotary(t *testing.T) {
|
||||||
requester1, _ := wallet.NewAccount()
|
requester1, _ := wallet.NewAccount()
|
||||||
requester2, _ := wallet.NewAccount()
|
requester2, _ := wallet.NewAccount()
|
||||||
amount := int64(100_0000_0000)
|
amount := int64(100_0000_0000)
|
||||||
feer := NewNotaryFeerStub(bc)
|
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
|
||||||
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50))
|
e.CheckGASBalance(t, notaryHash, big.NewInt(amount))
|
||||||
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(amount))
|
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
|
||||||
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50))
|
e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount))
|
||||||
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(2*amount))
|
|
||||||
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool
|
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool
|
||||||
requests = createMixedRequest([]requester{
|
requests = createMixedRequest([]requester{
|
||||||
{
|
{
|
||||||
|
@ -728,6 +739,7 @@ func TestNotary(t *testing.T) {
|
||||||
typ: notary.Signature,
|
typ: notary.Signature,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
feer := network.NewNotaryFeer(bc)
|
||||||
require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0]))
|
require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0]))
|
||||||
require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1]))
|
require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1]))
|
||||||
require.Eventually(t, func() bool {
|
require.Eventually(t, func() bool {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package core
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
gio "io"
|
gio "io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,168 +15,41 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/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/services/oracle"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"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/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap/zaptest"
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var oracleModulePath = filepath.Join("..", "services", "oracle")
|
||||||
oracleModulePath = filepath.Join("..", "services", "oracle")
|
|
||||||
oracleContractNEFPath = filepath.Join("test_data", "oracle_contract", "oracle.nef")
|
|
||||||
oracleContractManifestPath = filepath.Join("test_data", "oracle_contract", "oracle.manifest.json")
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestGenerateOracleContract generates helper contract that is able to call
|
func putOracleRequest(t *testing.T, oracleValidatorInvoker *neotest.ContractInvoker,
|
||||||
// native Oracle contract and has callback method. It uses test chain to define
|
|
||||||
// Oracle and StdLib native hashes and saves generated NEF and manifest to ... folder.
|
|
||||||
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
|
|
||||||
func TestGenerateOracleContract(t *testing.T) {
|
|
||||||
const saveState = false
|
|
||||||
|
|
||||||
bc := newTestChain(t)
|
|
||||||
oracleHash := bc.contracts.Oracle.Hash
|
|
||||||
stdHash := bc.contracts.Std.Hash
|
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
emit.Int(w.BinWriter, 5)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.Int(w.BinWriter, int64(callflag.All))
|
|
||||||
emit.String(w.BinWriter, "request")
|
|
||||||
emit.Bytes(w.BinWriter, oracleHash.BytesBE())
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
|
|
||||||
// `handle` method aborts if len(userData) == 2 and does NOT perform witness checks
|
|
||||||
// for the sake of contract code simplicity (the contract is used in multiple testchains).
|
|
||||||
offset := w.Len()
|
|
||||||
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.OVER)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.SIZE)
|
|
||||||
emit.Int(w.BinWriter, 2)
|
|
||||||
emit.Instruction(w.BinWriter, opcode.JMPNE, []byte{3})
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
|
||||||
emit.Int(w.BinWriter, 4) // url, userData, code, result
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
|
|
||||||
emit.String(w.BinWriter, "lastOracleResponse")
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
|
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
||||||
|
|
||||||
m := manifest.NewManifest("TestOracle")
|
|
||||||
m.ABI.Methods = []manifest.Method{
|
|
||||||
{
|
|
||||||
Name: "requestURL",
|
|
||||||
Offset: 0,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("url", smartcontract.StringType),
|
|
||||||
manifest.NewParameter("filter", smartcontract.StringType),
|
|
||||||
manifest.NewParameter("callback", smartcontract.StringType),
|
|
||||||
manifest.NewParameter("userData", smartcontract.AnyType),
|
|
||||||
manifest.NewParameter("gasForResponse", smartcontract.IntegerType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "handle",
|
|
||||||
Offset: offset,
|
|
||||||
Parameters: []manifest.Parameter{
|
|
||||||
manifest.NewParameter("url", smartcontract.StringType),
|
|
||||||
manifest.NewParameter("userData", smartcontract.AnyType),
|
|
||||||
manifest.NewParameter("code", smartcontract.IntegerType),
|
|
||||||
manifest.NewParameter("result", smartcontract.ByteArrayType),
|
|
||||||
},
|
|
||||||
ReturnType: smartcontract.VoidType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
perm := manifest.NewPermission(manifest.PermissionHash, oracleHash)
|
|
||||||
perm.Methods.Add("request")
|
|
||||||
m.Permissions = append(m.Permissions, *perm)
|
|
||||||
|
|
||||||
// Generate NEF file.
|
|
||||||
script := w.Bytes()
|
|
||||||
ne, err := nef.NewFile(script)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Write NEF file.
|
|
||||||
bytes, err := ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(oracleContractNEFPath, bytes, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write manifest file.
|
|
||||||
mData, err := json.Marshal(m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
if saveState {
|
|
||||||
err = os.WriteFile(oracleContractManifestPath, mData, os.ModePerm)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
require.False(t, saveState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getOracleContractState reads pre-compiled oracle contract generated by
|
|
||||||
// TestGenerateOracleContract and returns its state.
|
|
||||||
func getOracleContractState(t *testing.T, sender util.Uint160, id int32) *state.Contract {
|
|
||||||
errNotFound := errors.New("auto-generated oracle contract is not found, use TestGenerateOracleContract to regenerate")
|
|
||||||
|
|
||||||
neBytes, err := os.ReadFile(oracleContractNEFPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("nef: %w", errNotFound))
|
|
||||||
ne, err := nef.FileFromBytes(neBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mBytes, err := os.ReadFile(oracleContractManifestPath)
|
|
||||||
require.NoError(t, err, fmt.Errorf("manifest: %w", errNotFound))
|
|
||||||
m := &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(mBytes, m)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return &state.Contract{
|
|
||||||
ContractBase: state.ContractBase{
|
|
||||||
NEF: ne,
|
|
||||||
Hash: state.CreateContractHash(sender, ne.Checksum, m.Name),
|
|
||||||
Manifest: *m,
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func putOracleRequest(t *testing.T, h util.Uint160, bc *Blockchain,
|
|
||||||
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
|
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
|
||||||
var filtItem interface{}
|
var filtItem interface{}
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
filtItem = *filter
|
filtItem = *filter
|
||||||
}
|
}
|
||||||
res, err := invokeContractMethod(bc, gas+50_000_000+5_000_000, h, "requestURL",
|
return oracleValidatorInvoker.Invoke(t, stackitem.Null{}, "requestURL", url, filtItem, cb, userData, gas)
|
||||||
url, filtItem, cb, userData, gas)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return res.Container
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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{
|
return oracle.Config{
|
||||||
Log: zaptest.NewLogger(t),
|
Log: zaptest.NewLogger(t),
|
||||||
Network: netmode.UnitTestNet,
|
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,
|
*wallet.Account,
|
||||||
*oracle.Oracle,
|
*oracle.Oracle,
|
||||||
map[uint64]*responseWithSig,
|
map[uint64]*responseWithSig,
|
||||||
|
@ -217,7 +90,19 @@ func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) (
|
||||||
// Compatibility test from C# code.
|
// Compatibility test from C# code.
|
||||||
// https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61
|
// https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61
|
||||||
func TestCreateResponseTx(t *testing.T) {
|
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(30), bc.GetBaseExecFee())
|
||||||
require.Equal(t, int64(1000), bc.FeePerByte())
|
require.Equal(t, int64(1000), bc.FeePerByte())
|
||||||
|
@ -236,10 +121,10 @@ func TestCreateResponseTx(t *testing.T) {
|
||||||
Code: transaction.Success,
|
Code: transaction.Success,
|
||||||
Result: []byte{0},
|
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()})
|
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
||||||
bc.SetOracle(orc)
|
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)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 166, tx.Size())
|
assert.Equal(t, 166, tx.Size())
|
||||||
assert.Equal(t, int64(2198650), tx.NetworkFee)
|
assert.Equal(t, int64(2198650), tx.NetworkFee)
|
||||||
|
@ -247,7 +132,7 @@ func TestCreateResponseTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOracle_InvalidWallet(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))
|
_, err := oracle.NewOracle(getOracleConfig(t, bc, "./testdata/oracle1.json", "invalid", nil))
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
@ -257,45 +142,65 @@ func TestOracle_InvalidWallet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOracle(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")
|
acc1, orc1, m1, ch1 := getTestOracle(t, bc, "./testdata/oracle1.json", "one")
|
||||||
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||||
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
||||||
// Must be set in native contract for tx verification.
|
// 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())
|
orc1.UpdateOracleNodes(oracleNodes.Copy())
|
||||||
orc2.UpdateOracleNodes(oracleNodes.Copy())
|
orc2.UpdateOracleNodes(oracleNodes.Copy())
|
||||||
|
|
||||||
orcNative := bc.contracts.Oracle
|
nativeOracleState := bc.GetContractState(nativeOracleH)
|
||||||
md, ok := orcNative.GetMethod(manifest.MethodVerify, -1)
|
require.NotNil(t, nativeOracleState)
|
||||||
require.True(t, ok)
|
md := nativeOracleState.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
|
||||||
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
require.NotNil(t, md)
|
||||||
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
|
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)
|
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
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, cInvoker, "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, cInvoker, "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, cInvoker, "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, cInvoker, "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, cInvoker, "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, cInvoker, "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, cInvoker, "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, cInvoker, "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.maxallowed", nil, "handle", []byte{}, 100_000_000)
|
||||||
|
|
||||||
flt := "$.Values[1]"
|
flt := "$.Values[1]"
|
||||||
putOracleRequest(t, cs.Hash, bc, "https://get.filter", &flt, "handle", []byte{}, 10_000_000)
|
putOracleRequest(t, cInvoker, "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.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 {
|
checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest {
|
||||||
req, err := oracleCtr.GetRequestInternal(bc.dao, id)
|
// Use a hack to get request from Oracle contract, because we can't use GetRequestInternal directly.
|
||||||
require.NoError(t, err)
|
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}
|
reqs := map[uint64]*state.OracleRequest{id: req}
|
||||||
orc1.ProcessRequestsInternal(reqs)
|
orc1.ProcessRequestsInternal(reqs)
|
||||||
|
@ -328,7 +233,7 @@ func TestOracle(t *testing.T) {
|
||||||
actualHash := cp.Hash()
|
actualHash := cp.Hash()
|
||||||
require.Equal(t, actualHash, cachedHash, "transaction hash was changed during ")
|
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) {
|
t.Run("NormalRequest", func(t *testing.T) {
|
||||||
|
@ -436,21 +341,34 @@ func TestOracle(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOracleFull(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")
|
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||||
mp := bc.GetMemPool()
|
mp := bc.GetMemPool()
|
||||||
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||||
bc.SetOracle(orc)
|
bc.SetOracle(orc)
|
||||||
|
|
||||||
cs := getOracleContractState(t, util.Uint160{}, 42)
|
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
orc.Start()
|
orc.Start()
|
||||||
t.Cleanup(orc.Shutdown)
|
t.Cleanup(func() {
|
||||||
|
orc.Shutdown()
|
||||||
|
bc.Close()
|
||||||
|
})
|
||||||
|
|
||||||
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||||
putOracleRequest(t, cs.Hash, bc, "https://get.1234", new(string), "handle", []byte{}, 10_000_000)
|
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 },
|
require.Eventually(t, func() bool { return mp.Count() == 1 },
|
||||||
time.Second*3, time.Millisecond*200)
|
time.Second*3, time.Millisecond*200)
|
||||||
|
@ -461,17 +379,20 @@ func TestOracleFull(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotYetRunningOracle(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")
|
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
|
||||||
mp := bc.GetMemPool()
|
mp := bc.GetMemPool()
|
||||||
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
|
||||||
bc.SetOracle(orc)
|
bc.SetOracle(orc)
|
||||||
|
|
||||||
cs := getOracleContractState(t, util.Uint160{}, 42)
|
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
|
||||||
|
|
||||||
go bc.Run()
|
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 req state.OracleRequest
|
||||||
var reqs = make(map[uint64]*state.OracleRequest)
|
var reqs = make(map[uint64]*state.OracleRequest)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -10,18 +11,25 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/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/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"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/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/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/network/payload"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
|
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
|
@ -70,95 +78,113 @@ func newMajorityMultisigWithGAS(t *testing.T, n int) (util.Uint160, keys.PublicK
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateRoot(t *testing.T) {
|
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)
|
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()
|
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()
|
tmpDir := t.TempDir()
|
||||||
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
||||||
cfg := createStateRootConfig(w.Path(), "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.NoError(t, err)
|
||||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
|
||||||
r, err := bc.stateRoot.GetStateRoot(bc.BlockHeight())
|
r, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight())
|
||||||
require.NoError(t, err)
|
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) {
|
t.Run("invalid message", func(t *testing.T) {
|
||||||
require.Error(t, srv.OnPayload(&payload.Extensible{Data: []byte{42}}))
|
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) {
|
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)
|
require.NoError(t, err)
|
||||||
data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r))
|
data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
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) {
|
t.Run("invalid height", func(t *testing.T) {
|
||||||
r, err := bc.stateRoot.GetStateRoot(1)
|
r, err := bc.GetStateModule().GetStateRoot(1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
r.Index = 10
|
r.Index = 10
|
||||||
data := testSignStateRoot(t, r, pubs, accs...)
|
data := testSignStateRoot(t, r, pubs, accs...)
|
||||||
require.Error(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
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) {
|
t.Run("invalid signer", func(t *testing.T) {
|
||||||
accInv, err := wallet.NewAccount()
|
accInv, err := wallet.NewAccount()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
|
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
|
||||||
require.NoError(t, accInv.ConvertMultisig(1, pubs))
|
require.NoError(t, accInv.ConvertMultisig(1, pubs))
|
||||||
transferTokenFromMultisigAccount(t, bc, accInv.Contract.ScriptHash(), bc.contracts.GAS.Hash, 1_0000_0000)
|
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), accInv.Contract.ScriptHash(), 1_0000_0000, nil)
|
||||||
r, err := bc.stateRoot.GetStateRoot(1)
|
r, err := bc.GetStateModule().GetStateRoot(1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
data := testSignStateRoot(t, r, pubs, accInv)
|
data := testSignStateRoot(t, r, pubs, accInv)
|
||||||
err = srv.OnPayload(&payload.Extensible{Data: data})
|
err = srv.OnPayload(&payload.Extensible{Data: data})
|
||||||
require.True(t, errors.Is(err, ErrWitnessHashMismatch), "got: %v", err)
|
require.True(t, errors.Is(err, core.ErrWitnessHashMismatch), "got: %v", err)
|
||||||
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
|
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)
|
require.NoError(t, err)
|
||||||
data := testSignStateRoot(t, r, pubs, accs...)
|
data := testSignStateRoot(t, r, pubs, accs...)
|
||||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
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.NoError(t, err)
|
||||||
require.NotEqual(t, 0, len(r.Witness))
|
require.NotEqual(t, 0, len(r.Witness))
|
||||||
require.Equal(t, h, r.Witness[0].ScriptHash())
|
require.Equal(t, h, r.Witness[0].ScriptHash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type memoryStore struct {
|
||||||
|
*storage.MemoryStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (memoryStore) Close() error { return nil }
|
||||||
|
|
||||||
func TestStateRootInitNonZeroHeight(t *testing.T) {
|
func TestStateRootInitNonZeroHeight(t *testing.T) {
|
||||||
st := memoryStore{storage.NewMemoryStore()}
|
st := memoryStore{storage.NewMemoryStore()}
|
||||||
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
||||||
|
|
||||||
var root util.Uint256
|
var root util.Uint256
|
||||||
t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup
|
t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup
|
||||||
bc := newTestChainWithCustomCfgAndStore(t, st, nil)
|
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
|
||||||
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||||
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
|
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()
|
tmpDir := t.TempDir()
|
||||||
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
||||||
cfg := createStateRootConfig(w.Path(), "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.NoError(t, err)
|
||||||
r, err := bc.stateRoot.GetStateRoot(2)
|
r, err := bc.GetStateModule().GetStateRoot(2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
data := testSignStateRoot(t, r, pubs, accs...)
|
data := testSignStateRoot(t, r, pubs, accs...)
|
||||||
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
|
||||||
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight())
|
require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
|
||||||
root = bc.stateRoot.CurrentLocalStateRoot()
|
root = bc.GetStateModule().CurrentLocalStateRoot()
|
||||||
})
|
})
|
||||||
|
|
||||||
bc2 := newTestChainWithCustomCfgAndStore(t, st, nil)
|
bc2, _, _ := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
|
||||||
srv := bc2.GetStateModule()
|
srv := bc2.GetStateModule()
|
||||||
require.EqualValues(t, 2, srv.CurrentValidatedHeight())
|
require.EqualValues(t, 2, srv.CurrentValidatedHeight())
|
||||||
require.Equal(t, root, srv.CurrentLocalStateRoot())
|
require.Equal(t, root, srv.CurrentLocalStateRoot())
|
||||||
|
@ -186,7 +212,22 @@ func createStateRootConfig(walletPath, password string) config.StateRoot {
|
||||||
|
|
||||||
func TestStateRootFull(t *testing.T) {
|
func TestStateRootFull(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
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)
|
h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
|
||||||
w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two")
|
w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two")
|
||||||
|
@ -194,7 +235,8 @@ func TestStateRootFull(t *testing.T) {
|
||||||
|
|
||||||
var lastValidated atomic.Value
|
var lastValidated atomic.Value
|
||||||
var lastHeight atomic.Uint32
|
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)
|
lastHeight.Store(ep.ValidBlockStart)
|
||||||
lastValidated.Store(ep)
|
lastValidated.Store(ep)
|
||||||
})
|
})
|
||||||
|
@ -202,16 +244,17 @@ func TestStateRootFull(t *testing.T) {
|
||||||
srv.Start()
|
srv.Start()
|
||||||
t.Cleanup(srv.Shutdown)
|
t.Cleanup(srv.Shutdown)
|
||||||
|
|
||||||
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
|
validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
|
||||||
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
|
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)
|
require.Eventually(t, func() bool { return lastHeight.Load() == 2 }, time.Second, time.Millisecond)
|
||||||
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1)
|
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1, getDesignatedByRole)
|
||||||
_, err = persistBlock(bc)
|
e.AddNewBlock(t)
|
||||||
require.NoError(t, err)
|
|
||||||
require.Eventually(t, func() bool { return lastHeight.Load() == 3 }, time.Second, time.Millisecond)
|
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, err)
|
||||||
require.NoError(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
require.NoError(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
||||||
require.NotNil(t, lastValidated.Load().(*payload.Extensible))
|
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.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg))
|
||||||
require.NotEqual(t, stateroot.RootT, msg.Type) // not a sender for this root
|
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.NoError(t, err)
|
||||||
require.Error(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
|
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)))
|
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)
|
require.Equal(t, r.Root, actual.Root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible,
|
func checkVoteBroadcasted(t *testing.T, bc *core.Blockchain, p *payload.Extensible,
|
||||||
height uint32, valIndex byte) {
|
height uint32, valIndex byte, getDesignatedByRole func(t *testing.T, h uint32) keys.PublicKeys) {
|
||||||
require.NotNil(t, p)
|
require.NotNil(t, p)
|
||||||
m := new(stateroot.Message)
|
m := new(stateroot.Message)
|
||||||
require.NoError(t, testserdes.DecodeBinary(p.Data, m))
|
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, height, vote.Height)
|
||||||
require.Equal(t, int32(valIndex), vote.ValidatorIndex)
|
require.Equal(t, int32(valIndex), vote.ValidatorIndex)
|
||||||
|
|
||||||
pubs, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.StateValidator, bc.BlockHeight())
|
pubs := getDesignatedByRole(t, bc.BlockHeight())
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, len(pubs) > int(valIndex))
|
require.True(t, len(pubs) > int(valIndex))
|
||||||
require.True(t, pubs[valIndex].VerifyHashable(vote.Signature, uint32(netmode.UnitTestNet), r))
|
require.True(t, pubs[valIndex].VerifyHashable(vote.Signature, uint32(netmode.UnitTestNet), r))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package core
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"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/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
"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/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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -18,40 +19,41 @@ func TestStateSyncModule_Init(t *testing.T) {
|
||||||
stateSyncInterval = 2
|
stateSyncInterval = 2
|
||||||
maxTraceable uint32 = 3
|
maxTraceable uint32 = 3
|
||||||
)
|
)
|
||||||
spoutCfg := func(c *config.Config) {
|
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||||
c.ProtocolConfiguration.StateRootInHeader = true
|
c.StateRootInHeader = true
|
||||||
c.ProtocolConfiguration.P2PStateExchangeExtensions = true
|
c.P2PStateExchangeExtensions = true
|
||||||
c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval
|
c.StateSyncInterval = stateSyncInterval
|
||||||
c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable
|
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++ {
|
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)
|
spoutCfg(c)
|
||||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
c.KeepOnlyLatestState = true
|
||||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = true
|
c.RemoveUntraceableBlocks = true
|
||||||
}
|
}
|
||||||
t.Run("error: module disabled by config", func(t *testing.T) {
|
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)
|
boltCfg(c)
|
||||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = false
|
c.RemoveUntraceableBlocks = false
|
||||||
})
|
})
|
||||||
module := bcBolt.GetStateSyncModule()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.Error(t, module.Init(bcSpout.BlockHeight())) // module inactive (non-archival node)
|
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) {
|
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()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(uint32(2*stateSyncInterval-1)))
|
require.NoError(t, module.Init(uint32(2*stateSyncInterval-1)))
|
||||||
require.False(t, module.IsActive())
|
require.False(t, module.IsActive())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("inactive: bolt chain height is close enough to spout chain height", func(t *testing.T) {
|
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++ {
|
for i := 1; i < int(bcSpout.BlockHeight())-stateSyncInterval; i++ {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
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) {
|
t.Run("error: bolt chain is too low to start state sync process", func(t *testing.T) {
|
||||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
bcBolt, validatorsBolt, committeeBolt := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||||
require.NoError(t, bcBolt.AddBlock(bcBolt.newBlock()))
|
eBolt := neotest.NewExecutor(t, bcBolt, validatorsBolt, committeeBolt)
|
||||||
|
eBolt.AddNewBlock(t)
|
||||||
|
|
||||||
module := bcBolt.GetStateSyncModule()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.Error(t, module.Init(uint32(3*stateSyncInterval)))
|
require.Error(t, module.Init(uint32(3*stateSyncInterval)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("initialized: no previous state sync point", func(t *testing.T) {
|
t.Run("initialized: no previous state sync point", func(t *testing.T) {
|
||||||
bcBolt := newTestChainWithCustomCfg(t, boltCfg)
|
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||||
|
|
||||||
module := bcBolt.GetStateSyncModule()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
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) {
|
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()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
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) {
|
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()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
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) {
|
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()
|
module := bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
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
|
// 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
|
// and regular blocks processing was started
|
||||||
require.NoError(t, bcBolt.AddBlock(bcBolt.newBlock()))
|
eBolt.AddNewBlock(t)
|
||||||
module = bcBolt.GetStateSyncModule()
|
module = bcBolt.GetStateSyncModule()
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||||
require.False(t, module.IsActive())
|
require.False(t, module.IsActive())
|
||||||
|
@ -282,27 +286,31 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
maxTraceable uint32 = 6
|
maxTraceable uint32 = 6
|
||||||
stateSyncPoint = 20
|
stateSyncPoint = 20
|
||||||
)
|
)
|
||||||
spoutCfg := func(c *config.Config) {
|
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||||
c.ProtocolConfiguration.StateRootInHeader = true
|
c.StateRootInHeader = true
|
||||||
c.ProtocolConfiguration.P2PStateExchangeExtensions = true
|
c.P2PStateExchangeExtensions = true
|
||||||
c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval
|
c.StateSyncInterval = stateSyncInterval
|
||||||
c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable
|
c.MaxTraceableBlocks = maxTraceable
|
||||||
|
c.P2PSigExtensions = true // `initBasicChain` assumes Notary is enabled.
|
||||||
}
|
}
|
||||||
bcSpout := newTestChainWithCustomCfg(t, spoutCfg)
|
bcSpoutStore := storage.NewMemoryStore()
|
||||||
initBasicChain(t, bcSpout)
|
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)
|
// 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()))
|
require.Equal(t, stateSyncPoint+2, int(bcSpout.BlockHeight()))
|
||||||
|
|
||||||
boltCfg := func(c *config.Config) {
|
boltCfg := func(c *config.ProtocolConfiguration) {
|
||||||
spoutCfg(c)
|
spoutCfg(c)
|
||||||
c.ProtocolConfiguration.KeepOnlyLatestState = true
|
c.KeepOnlyLatestState = true
|
||||||
c.ProtocolConfiguration.RemoveUntraceableBlocks = true
|
c.RemoveUntraceableBlocks = true
|
||||||
}
|
}
|
||||||
bcBoltStore := memoryStore{storage.NewMemoryStore()}
|
bcBoltStore := storage.NewMemoryStore()
|
||||||
bcBolt := initTestChain(t, bcBoltStore, boltCfg)
|
bcBolt, _, _ := chain.NewMultiWithCustomConfigAndStore(t, boltCfg, bcBoltStore, false)
|
||||||
go bcBolt.Run()
|
go bcBolt.Run() // Will close it manually at the end.
|
||||||
module := bcBolt.GetStateSyncModule()
|
module := bcBolt.GetStateSyncModule()
|
||||||
|
|
||||||
t.Run("error: add headers before initialisation", func(t *testing.T) {
|
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())
|
require.Equal(t, bcSpout.BlockHeight(), bcBolt.BlockHeight())
|
||||||
|
|
||||||
// compare storage states
|
// compare storage states
|
||||||
fetchStorage := func(bc *Blockchain) []storage.KeyValue {
|
fetchStorage := func(ps storage.Store, storagePrefix byte) []storage.KeyValue {
|
||||||
var kv []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)
|
key := slice.Copy(k)
|
||||||
value := slice.Copy(v)
|
value := slice.Copy(v)
|
||||||
if key[0] == byte(storage.STTempStorage) {
|
if key[0] == byte(storage.STTempStorage) {
|
||||||
|
@ -437,25 +445,19 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
})
|
})
|
||||||
return kv
|
return kv
|
||||||
}
|
}
|
||||||
expected := fetchStorage(bcSpout)
|
// Both blockchains are running, so we need to wait until recent changes will be persisted
|
||||||
actual := fetchStorage(bcBolt)
|
// 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)
|
require.ElementsMatch(t, expected, actual)
|
||||||
|
|
||||||
// no temp items should be left
|
// no temp items should be left
|
||||||
require.Eventually(t, func() bool {
|
|
||||||
var haveItems bool
|
var haveItems bool
|
||||||
bcBolt.dao.Store.Seek(storage.SeekRange{Prefix: []byte{byte(storage.STStorage)}}, func(_, _ []byte) bool {
|
bcBoltStore.Seek(storage.SeekRange{Prefix: []byte{byte(storage.STStorage)}}, func(_, _ []byte) bool {
|
||||||
haveItems = true
|
haveItems = true
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
return !haveItems
|
require.False(t, 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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// 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, validator)
|
||||||
checkMultiSigner(t, committee)
|
checkMultiSigner(t, committee)
|
||||||
|
|
||||||
|
@ -50,21 +50,28 @@ func NewExecutor(t *testing.T, bc blockchainer.Blockchainer, validator, committe
|
||||||
}
|
}
|
||||||
|
|
||||||
// TopBlock returns block with the highest index.
|
// 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())))
|
b, err := e.Chain.GetBlock(e.Chain.GetHeaderHash(int(e.Chain.BlockHeight())))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// NativeHash returns native contract hash by name.
|
// 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)
|
h, err := e.Chain.GetNativeContractScriptHash(name)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return h
|
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.
|
// 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)
|
h := e.NativeHash(t, name)
|
||||||
cs := e.Chain.GetContractState(h)
|
cs := e.Chain.GetContractState(h)
|
||||||
require.NotNil(t, cs)
|
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.
|
// 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()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
|
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
|
||||||
require.NoError(t, w.Err)
|
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.
|
// NewTx creates new transaction which invokes contract method.
|
||||||
// Transaction is signed with signer.
|
// 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 {
|
hash util.Uint160, method string, args ...interface{}) *transaction.Transaction {
|
||||||
tx := e.NewUnsignedTx(t, hash, method, args...)
|
tx := e.NewUnsignedTx(t, hash, method, args...)
|
||||||
return e.SignTx(t, tx, -1, signers...)
|
return e.SignTx(t, tx, -1, signers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTx signs a transaction using provided 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 {
|
for _, acc := range signers {
|
||||||
tx.Signers = append(tx.Signers, transaction.Signer{
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
||||||
Account: acc.ScriptHash(),
|
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).
|
// 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.
|
// 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()
|
acc, err := wallet.NewAccount()
|
||||||
require.NoError(t, err)
|
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.
|
// precalculated contract hash matches the actual one.
|
||||||
// data is an optional argument to `_deploy`.
|
// data is an optional argument to `_deploy`.
|
||||||
// Returns hash of the deploy transaction.
|
// Returns hash of the deploy transaction.
|
||||||
func (e *Executor) DeployContract(t *testing.T, c *Contract, data interface{}) util.Uint256 {
|
func (e *Executor) DeployContract(t testing.TB, c *Contract, data interface{}) util.Uint256 {
|
||||||
tx := e.NewDeployTx(t, e.Chain, c, data)
|
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.AddNewBlock(t, tx)
|
||||||
e.CheckHalt(t, tx.Hash())
|
e.CheckHalt(t, tx.Hash())
|
||||||
|
|
||||||
|
@ -148,9 +163,9 @@ func (e *Executor) DeployContract(t *testing.T, c *Contract, data interface{}) u
|
||||||
return tx.Hash()
|
return tx.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeployContractCheckFAULT compiles and deploys contract to bc. It checks that deploy
|
// DeployContractCheckFAULT compiles and deploys contract to bc using validator
|
||||||
// transaction FAULTed with the specified error.
|
// account. It checks that deploy transaction FAULTed with the specified error.
|
||||||
func (e *Executor) DeployContractCheckFAULT(t *testing.T, c *Contract, data interface{}, errMessage string) {
|
func (e *Executor) DeployContractCheckFAULT(t testing.TB, c *Contract, data interface{}, errMessage string) {
|
||||||
tx := e.NewDeployTx(t, e.Chain, c, data)
|
tx := e.NewDeployTx(t, e.Chain, c, data)
|
||||||
e.AddNewBlock(t, tx)
|
e.AddNewBlock(t, tx)
|
||||||
e.CheckFault(t, tx.Hash(), errMessage)
|
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
|
// InvokeScript adds transaction with the specified script to the chain and
|
||||||
// returns its hash. It does no faults check.
|
// returns its hash. It does no faults check.
|
||||||
func (e *Executor) InvokeScript(t *testing.T, script []byte, signers []Signer) util.Uint256 {
|
func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) util.Uint256 {
|
||||||
tx := transaction.New(script, 0)
|
tx := e.PrepareInvocation(t, script, signers)
|
||||||
tx.Nonce = Nonce()
|
|
||||||
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
|
||||||
e.SignTx(t, tx, -1, signers...)
|
|
||||||
e.AddNewBlock(t, tx)
|
e.AddNewBlock(t, tx)
|
||||||
return tx.Hash()
|
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
|
// InvokeScriptCheckHALT adds transaction with the specified script to the chain
|
||||||
// and checks it's HALTed with the specified items on stack.
|
// 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)
|
hash := e.InvokeScript(t, script, signers)
|
||||||
e.CheckHalt(t, hash, stack...)
|
e.CheckHalt(t, hash, stack...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvokeScriptCheckFAULT adds transaction with the specified script to the
|
// InvokeScriptCheckFAULT adds transaction with the specified script to the
|
||||||
// chain and checks it's FAULTed with the specified error.
|
// 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)
|
hash := e.InvokeScript(t, script, signers)
|
||||||
e.CheckFault(t, hash, errMessage)
|
e.CheckFault(t, hash, errMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckHalt checks that transaction persisted with HALT state.
|
// 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)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
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.
|
// CheckFault checks that transaction persisted with FAULT state.
|
||||||
// Raised exception is also checked to contain s as a substring.
|
// 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)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.FaultState, aer[0].VMState)
|
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
|
// CheckTxNotificationEvent checks that specified event was emitted at the specified position
|
||||||
// during transaction script execution. Negative index corresponds to backwards enumeration.
|
// 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)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
l := len(aer[0].Events)
|
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.
|
// 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)
|
actual := e.Chain.GetUtilityTokenBalance(acc)
|
||||||
require.Equal(t, expected, actual, fmt.Errorf("invalid GAS balance: expected %s, got %s", expected.String(), actual.String()))
|
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.
|
// 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)
|
rawManifest, err := json.Marshal(c.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -237,11 +273,11 @@ func (e *Executor) NewDeployTx(t *testing.T, bc blockchainer.Blockchainer, c *Co
|
||||||
tx.Nonce = Nonce()
|
tx.Nonce = Nonce()
|
||||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
tx.Signers = []transaction.Signer{{
|
tx.Signers = []transaction.Signer{{
|
||||||
Account: e.Validator.ScriptHash(),
|
Account: signer.ScriptHash(),
|
||||||
Scopes: transaction.Global,
|
Scopes: transaction.Global,
|
||||||
}}
|
}}
|
||||||
addNetworkFee(bc, tx, e.Validator)
|
addNetworkFee(bc, tx, signer)
|
||||||
require.NoError(t, e.Validator.SignTx(netmode.UnitTestNet, tx))
|
require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx))
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +302,7 @@ func addNetworkFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, si
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnsignedBlock creates new unsigned block from txs.
|
// 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)
|
lastBlock := e.TopBlock(t)
|
||||||
b := &block.Block{
|
b := &block.Block{
|
||||||
Header: block.Header{
|
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.
|
// 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...)
|
b := e.NewUnsignedBlock(t, txs...)
|
||||||
e.SignBlock(b)
|
e.SignBlock(b)
|
||||||
require.NoError(t, e.Chain.AddBlock(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.
|
// 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++ {
|
for i := 0; i < count; i++ {
|
||||||
e.AddNewBlock(t)
|
blocks[i] = e.AddNewBlock(t)
|
||||||
}
|
}
|
||||||
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignBlock add validators signature to b.
|
// 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.
|
// 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...)
|
b := e.AddNewBlock(t, txs...)
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
e.CheckHalt(t, tx.Hash())
|
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.
|
// 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)
|
tx, height, err := e.Chain.GetTransaction(h)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return tx, height
|
return tx, height
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByIndex returns block by the specified index.
|
// 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)
|
h := e.Chain.GetHeaderHash(idx)
|
||||||
require.NotEmpty(t, h)
|
require.NotEmpty(t, h)
|
||||||
b, err := e.Chain.GetBlock(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.
|
// 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)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(aer))
|
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
|
// 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
|
// contents is always in RAM. The Signer returned is validator (and committee at
|
||||||
// the same time).
|
// 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)
|
return NewSingleWithCustomConfig(t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSingleWithCustomConfig is similar to NewSingle, but allows to override the
|
// NewSingleWithCustomConfig is similar to NewSingle, but allows to override the
|
||||||
// default configuration.
|
// default configuration.
|
||||||
func NewSingleWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
|
func NewSingleWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
|
||||||
st := storage.NewMemoryStore()
|
return NewSingleWithCustomConfigAndStore(t, f, nil, true)
|
||||||
return NewSingleWithCustomConfigAndStore(t, f, st, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but
|
// 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
|
// 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
|
// responsibility to do that before using the chain and its caller's responsibility
|
||||||
// also to properly Close the chain when done.
|
// 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{
|
protoCfg := config.ProtocolConfiguration{
|
||||||
Magic: netmode.UnitTestNet,
|
Magic: netmode.UnitTestNet,
|
||||||
MaxTraceableBlocks: MaxTraceableBlocks,
|
MaxTraceableBlocks: MaxTraceableBlocks,
|
||||||
|
@ -150,6 +149,9 @@ func NewSingleWithCustomConfigAndStore(t *testing.T, f func(cfg *config.Protocol
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f(&protoCfg)
|
f(&protoCfg)
|
||||||
}
|
}
|
||||||
|
if st == nil {
|
||||||
|
st = storage.NewMemoryStore()
|
||||||
|
}
|
||||||
log := zaptest.NewLogger(t)
|
log := zaptest.NewLogger(t)
|
||||||
bc, err := core.NewBlockchain(st, protoCfg, log)
|
bc, err := core.NewBlockchain(st, protoCfg, log)
|
||||||
require.NoError(t, err)
|
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
|
// NewMulti creates new blockchain instance with four validators and six
|
||||||
// committee members, otherwise not differring much from NewSingle. The
|
// committee members, otherwise not differring much from NewSingle. The
|
||||||
// second value returned contains validators Signer, the third -- committee one.
|
// 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)
|
return NewMultiWithCustomConfig(t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMultiWithCustomConfig is similar to NewMulti except it allows to override the
|
// NewMultiWithCustomConfig is similar to NewMulti except it allows to override the
|
||||||
// default configuration.
|
// 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{
|
protoCfg := config.ProtocolConfiguration{
|
||||||
Magic: netmode.UnitTestNet,
|
Magic: netmode.UnitTestNet,
|
||||||
MaxTraceableBlocks: MaxTraceableBlocks,
|
MaxTraceableBlocks: MaxTraceableBlocks,
|
||||||
|
@ -182,12 +205,11 @@ func NewMultiWithCustomConfig(t *testing.T, f func(*config.ProtocolConfiguration
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f(&protoCfg)
|
f(&protoCfg)
|
||||||
}
|
}
|
||||||
|
if st == nil {
|
||||||
|
st = storage.NewMemoryStore()
|
||||||
|
}
|
||||||
|
|
||||||
st := storage.NewMemoryStore()
|
|
||||||
log := zaptest.NewLogger(t)
|
log := zaptest.NewLogger(t)
|
||||||
bc, err := core.NewBlockchain(st, protoCfg, log)
|
bc, err := core.NewBlockchain(st, protoCfg, log)
|
||||||
require.NoError(t, err)
|
return bc, neotest.NewMultiSigner(multiValidatorAcc...), neotest.NewMultiSigner(multiCommitteeAcc...), err
|
||||||
go bc.Run()
|
|
||||||
t.Cleanup(bc.Close)
|
|
||||||
return bc, neotest.NewMultiSigner(multiValidatorAcc...), neotest.NewMultiSigner(multiCommitteeAcc...)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,15 @@ type ContractInvoker struct {
|
||||||
Signers []Signer
|
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.
|
// CommitteeInvoker creates new ContractInvoker for contract with hash h and committee multisignature signer.
|
||||||
func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
|
func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
|
||||||
return &ContractInvoker{
|
return &ContractInvoker{
|
||||||
|
@ -38,7 +47,7 @@ func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestInvoke creates test VM and invokes method with args.
|
// 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...)
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
||||||
b := c.NewUnsignedBlock(t, tx)
|
b := c.NewUnsignedBlock(t, tx)
|
||||||
ic := c.Chain.GetTestVM(trigger.Application, tx, b)
|
ic := c.Chain.GetTestVM(trigger.Application, tx, b)
|
||||||
|
@ -57,18 +66,18 @@ func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInvoke creates new invocation transaction.
|
// 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...)
|
return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInvokeNoSign creates new unsigned invocation transaction.
|
// 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...)
|
return c.Executor.NewUnsignedTx(t, c.Hash, method, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke invokes method with args, persists transaction and checks the result.
|
// Invoke invokes method with args, persists transaction and checks the result.
|
||||||
// Returns transaction hash.
|
// 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...)
|
tx := c.PrepareInvoke(t, method, args...)
|
||||||
c.AddNewBlock(t, tx)
|
c.AddNewBlock(t, tx)
|
||||||
c.CheckHalt(t, tx.Hash(), stackitem.Make(result))
|
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
|
// InvokeAndCheck invokes method with args, persists transaction and checks the result
|
||||||
// using provided function. Returns transaction hash.
|
// 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...)
|
tx := c.PrepareInvoke(t, method, args...)
|
||||||
c.AddNewBlock(t, tx)
|
c.AddNewBlock(t, tx)
|
||||||
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
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.
|
// 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...)
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
||||||
c.Executor.SignTx(t, tx, sysFee, c.Signers...)
|
c.Executor.SignTx(t, tx, sysFee, c.Signers...)
|
||||||
c.AddNewBlock(t, tx)
|
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.
|
// InvokeFail invokes method with args, persists transaction and checks the error message.
|
||||||
// Returns transaction hash.
|
// 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...)
|
tx := c.PrepareInvoke(t, method, args...)
|
||||||
c.AddNewBlock(t, tx)
|
c.AddNewBlock(t, tx)
|
||||||
c.CheckFault(t, tx.Hash(), message)
|
c.CheckFault(t, tx.Hash(), message)
|
||||||
|
|
|
@ -25,7 +25,7 @@ type Contract struct {
|
||||||
var contracts = make(map[string]*Contract)
|
var contracts = make(map[string]*Contract)
|
||||||
|
|
||||||
// CompileSource compiles contract from reader and returns it's NEF, manifest and hash.
|
// 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.
|
// nef.NewFile() cares about version a lot.
|
||||||
config.Version = "neotest"
|
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.
|
// 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 {
|
if c, ok := contracts[srcPath]; ok {
|
||||||
return c
|
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.Permissions[i] = manifest.Permission(conf.Permissions[i])
|
||||||
}
|
}
|
||||||
o.SafeMethods = conf.SafeMethods
|
o.SafeMethods = conf.SafeMethods
|
||||||
|
o.Overloads = conf.Overloads
|
||||||
|
o.SourceURL = conf.SourceURL
|
||||||
m, err := compiler.CreateManifest(di, o)
|
m, err := compiler.CreateManifest(di, o)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ func (m multiSigner) Single(n int) SingleSigner {
|
||||||
return NewSingleSigner(wallet.NewAccountFromPrivateKey(m.accounts[n].PrivateKey()))
|
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)
|
ms, ok := s.(multiSigner)
|
||||||
require.True(t, ok, "expected to be a multi-signer")
|
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{})
|
check func(t *testing.T, e *executor, result interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const genesisBlockHash = "a4ae00f6ac7496cac14e709fbf8b8ecb4c9831d8a6ee396056af9350fcf22671"
|
const genesisBlockHash = "f42e2ae74bbea6aa1789fdc4efa35ad55b04335442637c091eafb5b0e779dae7"
|
||||||
const testContractHash = "1ab08f5508edafa6f28e3db3227442a9e70aac52"
|
const testContractHash = "2db7d679c538ace5f00495c9e9d8ea95f1e0f5a5"
|
||||||
const deploymentTxHash = "017c9edb217477aeb3e0c35462361209fdb7bf104dc8e285e2385af8713926b4"
|
const deploymentTxHash = "496bccb5cb0a008ef9b7a32c459e508ef24fbb0830f82bac9162afa4ca804839"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
verifyContractHash = "7deef31e5c616e157cdf02a5446f36d0a4eead52"
|
verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c"
|
||||||
verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
|
verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
|
||||||
verifyWithArgsContractHash = "6df009754ce475a6a5730c9e488f80e8e47bc1f1"
|
verifyWithArgsContractHash = "0dce75f52adb1a4c5c6eaa6a34eb26db2e5b3781"
|
||||||
nnsContractHash = "1a7530a4c6cfdd40ffed40775aa5453febab24c0"
|
nnsContractHash = "ee92563903e4efd53565784080b2dbdc5c37e21f"
|
||||||
nnsToken1ID = "6e656f2e636f6d"
|
nnsToken1ID = "6e656f2e636f6d"
|
||||||
nfsoContractHash = "aaf8913c501e25c42877e79f04cb7c2c1ab47e57"
|
nfsoContractHash = "5f9ebd6b001b54c7bc70f96e0412fcf415dfe09f"
|
||||||
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
|
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
|
||||||
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
||||||
)
|
)
|
||||||
|
@ -279,7 +279,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
result: func(e *executor) interface{} {
|
result: func(e *executor) interface{} {
|
||||||
return &map[string]interface{}{
|
return &map[string]interface{}{
|
||||||
"name": "neo.com",
|
"name": "neo.com",
|
||||||
"expiration": "HrL+G4YB",
|
"expiration": "lhbLRl0B",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -882,7 +882,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
name: "positive, with notifications",
|
name: "positive, with notifications",
|
||||||
params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
|
params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
|
||||||
result: func(e *executor) interface{} {
|
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{
|
return &result.Invoke{
|
||||||
State: "HALT",
|
State: "HALT",
|
||||||
GasConsumed: 32167260,
|
GasConsumed: 32167260,
|
||||||
|
@ -915,7 +915,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
chg := []storage.Operation{{
|
chg := []storage.Operation{{
|
||||||
State: "Changed",
|
State: "Changed",
|
||||||
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
|
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",
|
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},
|
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",
|
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},
|
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.
|
// Can be returned in any order.
|
||||||
assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
|
assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
|
||||||
|
@ -937,7 +937,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
name: "positive, verbose",
|
name: "positive, verbose",
|
||||||
params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
|
params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
|
||||||
result: func(e *executor) interface{} {
|
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)
|
stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib)
|
||||||
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
|
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
|
||||||
return &result.Invoke{
|
return &result.Invoke{
|
||||||
|
@ -1205,12 +1205,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"sendrawtransaction": {
|
"sendrawtransaction": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
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{} },
|
result: func(e *executor) interface{} { return &result.RelayResult{} },
|
||||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||||
res, ok := inv.(*result.RelayResult)
|
res, ok := inv.(*result.RelayResult)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
expectedHash := "8ea251d812fbbdecaebfc164fb6afbd78b7db94f7dacb69421cd5d4e364522d2"
|
expectedHash := "acc3e13102c211068d06ff64034d6f7e2d4db00c1703d0dec8afa73560664fe1"
|
||||||
assert.Equal(t, expectedHash, res.Hash.StringLE())
|
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))
|
require.NoError(t, json.Unmarshal(res, actual))
|
||||||
checkNep17TransfersAux(t, e, actual, sent, rcvd)
|
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("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", 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{14}, []int{3}) })
|
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{13, 14}, []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{15, 16}, []int{4}) })
|
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(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Amount: "46748035310",
|
Amount: "47102293830",
|
||||||
LastUpdated: 19,
|
LastUpdated: 19,
|
||||||
}},
|
}},
|
||||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
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{}) {
|
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) {
|
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
|
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.NoError(t, err)
|
||||||
require.Equal(t, 1, len(blockPutNewTestValue.Transactions))
|
require.Equal(t, 4, len(blockPutNewTestValue.Transactions))
|
||||||
txPutNewTestValue := blockPutNewTestValue.Transactions[0]
|
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
|
blockSetRecord, err := e.chain.GetBlock(e.chain.GetHeaderHash(15)) // add type A record to `neo.com` domain via NNS
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -2336,6 +2339,30 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
||||||
Index: 17,
|
Index: 17,
|
||||||
TxHash: blockDeploy5.Hash(),
|
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,
|
Timestamp: blockPutNewTestValue.Timestamp,
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
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