diff --git a/localtool/cli.go b/localtool/cli.go
new file mode 100644
index 0000000000..61e05ccad6
--- /dev/null
+++ b/localtool/cli.go
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018 The ontology Authors
+ * This file is part of The ontology library.
+ *
+ * The ontology is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The ontology is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with The ontology. If not, see .
+ */
+
+package main
+
+import (
+ "encoding/json"
+ //"fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "runtime"
+
+ "github.com/ontio/ontology/cmd/utils"
+ "github.com/ontio/ontology/common/log"
+ "github.com/ontio/ontology/errors"
+ "github.com/ontio/ontology/wasmtest/common"
+ "github.com/urfave/cli"
+)
+
+type TestConfigElt struct {
+ Contract string `json:"contract"`
+ Balanceaddr []common.BalanceAddr `json:"balanceaddr"`
+ Testcase [][]common.TestCase `json:"testcase"`
+}
+
+type TestConfig struct {
+ Testconfigelt []TestConfigElt `json:"testconfig"`
+}
+
+func ontologyCLI(ctx *cli.Context) error {
+ // Get the deployObject.
+ args := ctx.Args()
+ if len(args) != 1 {
+ cli.ShowAppHelp(ctx)
+ return errors.NewErr("[Option Error]: need arg.")
+ }
+ deployobject := args[0]
+
+ // Load the deployObject. Assert deployObject is directory or not.
+ contracts, objIsDir, err := common.GetContact(deployobject)
+ if err != nil {
+ cli.ShowAppHelp(ctx)
+ return err
+ }
+
+ // check option.
+ config, paramsStr, err := parseOption(ctx, objIsDir)
+ if err != nil {
+ cli.ShowAppHelp(ctx)
+ return err
+ }
+
+ acct, database := common.InitOntologyLedger()
+ err = common.DeployContract(acct, database, contracts)
+ if err != nil {
+ return err
+ }
+
+ testContext := common.MakeTestContext(acct, contracts)
+
+ if config != nil {
+ for _, configelt := range config.Testconfigelt {
+ err := common.InitBalanceAddress(configelt.Balanceaddr, acct, database)
+ if err != nil {
+ return err
+ }
+ // currently only use the index zero array.
+ for _, testcase := range configelt.Testcase[0] {
+ err := common.TestWithConfigElt(acct, database, configelt.Contract, testcase, testContext)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ } else {
+ err = common.InvokeSpecifiedContract(acct, database, path.Base(deployobject), paramsStr, testContext)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func parseOption(ctx *cli.Context, objIsDir bool) (*TestConfig, string, error) {
+ if ctx.IsSet(utils.GetFlagName(ContractParamsFlag)) && ctx.IsSet(utils.GetFlagName(ConfigFlag)) {
+ return nil, "", errors.NewErr("[Option Error]: You can only specify --param or --config")
+ }
+
+ LogLevel := ctx.Uint(utils.GetFlagName(LogLevelFlag))
+ log.InitLog(int(LogLevel), log.PATH, log.Stdout)
+
+ if objIsDir {
+ if ctx.IsSet(utils.GetFlagName(ContractParamsFlag)) {
+ return nil, "", errors.NewErr("[Option Error]: Can not specify --param when input is a directory")
+ }
+
+ if !ctx.IsSet((utils.GetFlagName(ConfigFlag))) {
+ return nil, "", errors.NewErr("[Option Error]: Must specify --config config.json file when input is a directory")
+ }
+ }
+
+ if ctx.IsSet(utils.GetFlagName(ConfigFlag)) {
+ configFileName := ctx.String(utils.GetFlagName(ConfigFlag))
+ configBuff, err := ioutil.ReadFile(configFileName)
+ if err != nil {
+ return nil, "", err
+ }
+
+ var config TestConfig
+ err = json.Unmarshal([]byte(configBuff), &config)
+ if err != nil {
+ return nil, "", err
+ }
+
+ if len(config.Testconfigelt) == 0 {
+ return nil, "", errors.NewErr("No testcase in config file")
+ }
+
+ for _, configelt := range config.Testconfigelt {
+ if len(configelt.Contract) == 0 {
+ return nil, "", errors.NewErr("[Config format error]: Do not specify contract name")
+ }
+
+ if len(configelt.Testcase) == 0 {
+ return nil, "", errors.NewErr("[Config format error]: Do not specify testcase")
+ }
+ }
+
+ return &config, "", nil
+ }
+
+ if ctx.IsSet(utils.GetFlagName(ContractParamsFlag)) {
+ paramsStr := ctx.String(utils.GetFlagName(ContractParamsFlag))
+ return nil, paramsStr, nil
+ }
+
+ return nil, "", nil
+}
+
+func main() {
+ if err := setupAPP().Run(os.Args); err != nil {
+ os.Exit(1)
+ }
+}
+
+var (
+ ConfigFlag = cli.StringFlag{
+ Name: "config,c",
+ Usage: "the contract filename to be tested.",
+ }
+ ContractParamsFlag = cli.StringFlag{
+ Name: "param,p",
+ Usage: "specify contract param when input is a file.",
+ }
+ LogLevelFlag = cli.UintFlag{
+ Name: "loglevel,l",
+ Usage: "set the log levela.",
+ Value: log.InfoLog,
+ }
+)
+
+func setupAPP() *cli.App {
+ app := cli.NewApp()
+ app.Usage = "cli"
+ app.UsageText = "cli [option] input"
+ app.Action = ontologyCLI
+ app.Version = "1.0.0"
+ app.Copyright = "Copyright in 2019 The Ontology Authors"
+ app.Flags = []cli.Flag{
+ ConfigFlag,
+ ContractParamsFlag,
+ LogLevelFlag,
+ }
+ app.Before = func(context *cli.Context) error {
+ runtime.GOMAXPROCS(runtime.NumCPU())
+ return nil
+ }
+ app.ExitErrHandler = func(context *cli.Context, err error) {
+ if err != nil {
+ log.Fatalf("%v", err)
+ }
+ }
+ return app
+}
diff --git a/localtool/config.con b/localtool/config.con
new file mode 100644
index 0000000000..4198abcaba
--- /dev/null
+++ b/localtool/config.con
@@ -0,0 +1,28 @@
+{
+ "testconfig":[
+ {
+ "contract" : "a.wasm",
+ "balanceaddr": [
+ {
+ "address":"Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT",
+ "balance":30
+ },
+ {
+ "address":"Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV",
+ "balance":20
+ }
+ ],
+ "testcase": [
+ [{"method":"balanceOf", "param":"address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT"},
+ {"method":"balanceOf", "param":"address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"},
+ {"needcontext":true, "method":"setadmin"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "method":"transfertoowner", "param":"address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:20"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "method":"transfertoowner", "param":"address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,int:30"},
+ {"needcontext":true, "method":"test_native_ont", "param":"string:balanceOf,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "needcontext":true, "method":"test_native_ont", "param":"string:transfer,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "needcontext":true, "method":"test_native_ont", "param":"string:approve,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"}
+ ]
+ ]
+ }
+ ]
+}
diff --git a/localtool/test_native.cpp b/localtool/test_native.cpp
new file mode 100644
index 0000000000..a028dc17cd
--- /dev/null
+++ b/localtool/test_native.cpp
@@ -0,0 +1,83 @@
+#include
+using std::string;
+using std::vector;
+
+namespace ontio {
+ struct test_conext {
+ address admin;
+ std::map addrmap;
+ ONTLIB_SERIALIZE( test_conext, (admin) (addrmap))
+ };
+};
+
+using namespace ontio;
+
+class hello: public contract {
+ address owner;
+ key kn = make_key(string("owner"));
+ public:
+ using contract::contract;
+ uint128_t test_native_ont(string &method, address &from, address &to, asset &amount, test_conext &tc) {
+ if (method == "balanceOf") {
+ asset balance = ont::balanceof(tc.admin);
+ check(balance == 1000000000, "init balance wrong");
+ } else if (method == "transfer") {
+ /*keep admin alway initbalance.*/
+ check(ont::transfer(tc.admin, to, amount), "transfer failed");
+ check(ont::balanceof(to) == amount, "transfer amount wrong");
+ check(ont::transfer(to, tc.admin, amount), "transfer failed");
+ check(ont::balanceof(to) == 0, "transfer amount wrong");
+ } else if (method == "approve") {
+ /*keep admin alway initbalance.*/
+ check(ont::approve(tc.admin, from, amount),"approve failed");
+ check(ont::allowance(tc.admin, from) == amount, "allowance amount wrong");
+ check(ont::transferfrom(from, tc.admin, to, amount),"transferfrom failed");
+ check(ont::allowance(tc.admin, from) == 0, "allowance amount wrong");
+ check(ont::balanceof(to) == amount, "transfer amount wrong");
+ check(ont::transfer(to, tc.admin, amount), "transfer failed");
+ check(ont::balanceof(to) == 0, "transfer amount wrong");
+ check(ont::balanceof(from) == 0, "transfer amount wrong");
+ }
+
+ return 1;
+ }
+
+ int128_t balanceOf(address &from) {
+ asset balance = ont::balanceof(from);
+ int64_t t = int64_t(balance.amount);
+ printf("balanceOf is : %lld", t);
+ return balance.amount;
+ }
+
+ int128_t testranfer(address& from, address &to, asset &amount) {
+ check(ont::transfer(from, to, amount), "transfer failed");
+ return 1;
+ }
+
+ int128_t transfertoowner(address &from, asset &amount) {
+ check(storage_get(kn, owner), "get owner key failed");
+ check(ont::transfer(from, owner, amount), "transfer failed");
+ return 1;
+ }
+
+ int128_t setadmin(test_conext &tc) {
+ storage_put(kn, tc.admin);
+ check(storage_get(kn, owner), "get owner key failed");
+ check(owner == tc.admin, "storage failed");
+ return 1;
+ }
+
+ string testcase(void) {
+ return string(R"(
+ [
+ [{"needcontext":true, "method":"test_native_ont", "param":"string:balanceOf,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "needcontext":true, "method":"test_native_ont", "param":"string:transfer,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"},
+ {"env":{"witness":["Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT","Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV"]}, "needcontext":true, "method":"test_native_ont", "param":"string:approve,address:Ad4pjz2bqep4RhQrUAzMuZJkBC3qJ1tZuT,address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,int:1000", "expected":"int:1"}
+ ]
+ ]
+ )");
+ }
+
+};
+
+ONTIO_DISPATCH( hello,(testcase)(test_native_ont)(balanceOf)(testranfer)(transfertoowner)(setadmin))
diff --git a/wasmtest/common/common.go b/wasmtest/common/common.go
index 0411dcd5bb..9b79e36662 100644
--- a/wasmtest/common/common.go
+++ b/wasmtest/common/common.go
@@ -19,6 +19,7 @@ package common
import (
"bytes"
+ "fmt"
"encoding/json"
utils2 "github.com/ontio/ontology/cmd/utils"
@@ -86,11 +87,14 @@ type TestContext struct {
}
func GenWasmTransaction(testCase TestCase, contract common.Address, testConext *TestContext) (*types.Transaction, error) {
+ var allParam []interface{}
params, err := utils2.ParseParams(testCase.Param)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("[%s]: You follow example \"int:2,string:\"hello world\",address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,[int:1,int:2],bytearray:60c56b6a00527a\" ", err)
+ }
+ if len(testCase.Method) != 0 {
+ allParam = append([]interface{}{}, testCase.Method)
}
- allParam := append([]interface{}{}, testCase.Method)
allParam = append(allParam, params...)
tx, err := utils.NewWasmVMInvokeTransaction(0, 100000000, contract, allParam)
if err != nil {
@@ -162,11 +166,14 @@ func buildTestConextForNeo(testConext *TestContext) []byte {
}
func GenNeoVMTransaction(testCase TestCase, contract common.Address, testConext *TestContext) (*types.Transaction, error) {
+ var allParam []interface{}
params, err := utils2.ParseParams(testCase.Param)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("[%s]: You follow example \"int:2,string:\"hello world\",address:Ab1z3Sxy7ovn4AuScdmMh4PRMvcwCMzSNV,[int:1,int:2],bytearray:60c56b6a00527a\" ", err)
+ }
+ if len(testCase.Method) != 0 {
+ allParam = append([]interface{}{}, testCase.Method)
}
- allParam := append([]interface{}{}, testCase.Method)
allParam = append(allParam, params...)
tx, err := common2.NewNeovmInvokeTransaction(0, 100000000, contract, allParam)
if err != nil {
diff --git a/wasmtest/common/testframe.go b/wasmtest/common/testframe.go
new file mode 100644
index 0000000000..423ee9a5ed
--- /dev/null
+++ b/wasmtest/common/testframe.go
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2018 The ontology Authors
+ * This file is part of The ontology library.
+ *
+ * The ontology is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The ontology is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with The ontology. If not, see .
+ */
+
+package common
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+
+ "github.com/go-interpreter/wagon/exec"
+ "github.com/go-interpreter/wagon/wasm"
+ "github.com/ontio/ontology-crypto/keypair"
+ "github.com/ontio/ontology/account"
+ "github.com/ontio/ontology/cmd/utils"
+ "github.com/ontio/ontology/common"
+ "github.com/ontio/ontology/common/config"
+ "github.com/ontio/ontology/common/constants"
+ "github.com/ontio/ontology/common/log"
+ "github.com/ontio/ontology/core/genesis"
+ "github.com/ontio/ontology/core/ledger"
+ "github.com/ontio/ontology/core/payload"
+ "github.com/ontio/ontology/core/signature"
+ "github.com/ontio/ontology/core/types"
+ utils2 "github.com/ontio/ontology/core/utils"
+ "github.com/ontio/ontology/events"
+ common2 "github.com/ontio/ontology/http/base/common"
+ "github.com/ontio/ontology/smartcontract/service/native/ont"
+ utils3 "github.com/ontio/ontology/smartcontract/service/native/utils"
+ "github.com/ontio/ontology/smartcontract/service/wasmvm"
+ "github.com/ontio/ontology/smartcontract/states"
+ vmtypes "github.com/ontio/ontology/vm/neovm/types"
+)
+
+const (
+ testcaseMethod = "testcase"
+)
+
+func init() {
+ runtime.GOMAXPROCS(4)
+}
+
+func checkErr(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func InitOntologyLedger() (*account.Account, *ledger.Ledger) {
+ datadir := "testdata"
+ err := os.RemoveAll(datadir)
+ defer func() {
+ _ = os.RemoveAll(datadir)
+ _ = os.RemoveAll(log.PATH)
+ }()
+
+ acct := account.NewAccount("")
+
+ buf := keypair.SerializePublicKey(acct.PublicKey)
+ config.DefConfig.Genesis.ConsensusType = "solo"
+ config.DefConfig.Genesis.SOLO.GenBlockTime = 3
+ config.DefConfig.Genesis.SOLO.Bookkeepers = []string{hex.EncodeToString(buf)}
+ config.DefConfig.P2PNode.NetworkId = 0
+
+ bookkeepers := []keypair.PublicKey{acct.PublicKey}
+ //Init event hub
+ events.Init()
+
+ //log.Info("1. Loading the Ledger")
+ database, err := ledger.NewLedger(datadir, 1000000)
+ checkErr(err)
+ ledger.DefLedger = database
+ genblock, err := genesis.BuildGenesisBlock(bookkeepers, config.DefConfig.Genesis)
+ checkErr(err)
+ err = database.Init(bookkeepers, genblock)
+ checkErr(err)
+ return acct, database
+}
+
+type BalanceAddr struct {
+ Address string `json:"address"`
+ Balance uint64 `json:"balance"`
+}
+
+func InitBalanceAddress(balanceAddr []BalanceAddr, acct *account.Account, database *ledger.Ledger) error {
+ for _, elt := range balanceAddr {
+ to, err := common.AddressFromBase58(elt.Address)
+ if err != nil {
+ return err
+ }
+ var sts []*ont.State
+ sts = append(sts, &ont.State{
+ From: acct.Address,
+ To: to,
+ Value: elt.Balance,
+ })
+
+ mutable, err := common2.NewNativeInvokeTransaction(0, 200000000, utils3.OntContractAddress, 0, ont.TRANSFER_NAME, []interface{}{sts})
+
+ tx, err := mutable.IntoImmutable()
+ if err != nil {
+ panic("build ont tansfer tx failed")
+ }
+
+ tx.SignedAddr = append(tx.SignedAddr, acct.Address)
+
+ block, _ := MakeBlock(acct, []*types.Transaction{tx})
+ err = database.AddBlock(block, common.UINT256_EMPTY)
+ checkErr(err)
+ event, err := database.GetEventNotifyByTx(tx.Hash())
+ js, _ := json.Marshal(event.Notify)
+ log.Infof("Notify info : %s", string(js))
+ }
+ return nil
+}
+
+type ExecEnv struct {
+ Contract common.Address
+ Time uint32
+ Height uint32
+ Tx *types.Transaction
+ BlockHash common.Uint256
+}
+
+func checkExecResult(testCase TestCase, result *states.PreExecResult, execEnv ExecEnv) {
+ assertEq(result.State, byte(1))
+ ret := result.Result.(string)
+ switch testCase.Method {
+ case "timestamp":
+ sink := common.NewZeroCopySink(nil)
+ sink.WriteUint64(uint64(execEnv.Time))
+ assertEq(ret, hex.EncodeToString(sink.Bytes()))
+ case "block_height":
+ sink := common.NewZeroCopySink(nil)
+ sink.WriteUint32(uint32(execEnv.Height))
+ assertEq(ret, hex.EncodeToString(sink.Bytes()))
+ case "self_address", "entry_address":
+ assertEq(ret, hex.EncodeToString(execEnv.Contract[:]))
+ case "caller_address":
+ assertEq(ret, hex.EncodeToString(common.ADDRESS_EMPTY[:]))
+ case "current_txhash":
+ hash := execEnv.Tx.Hash()
+ assertEq(ret, hex.EncodeToString(hash[:]))
+ case "current_blockhash":
+ assertEq(ret, hex.EncodeToString(execEnv.BlockHash[:]))
+ //case "sha256":
+ // let data :&[u8]= source.read().unwrap();
+ // sink.write(runtime::sha256(&data))
+ //}
+ default:
+ if len(testCase.Expect) != 0 {
+ expect, err := utils.ParseParams(testCase.Expect)
+ checkErr(err)
+ if execEnv.Tx.TxType == types.InvokeNeo {
+ val := buildNeoVmValueFromExpect(expect)
+ cv, err := val.ConvertNeoVmValueHexString()
+ checkErr(err)
+ assertEq(cv, result.Result)
+ } else if execEnv.Tx.TxType == types.InvokeWasm {
+ exp, err := utils2.BuildWasmContractParam(expect)
+ checkErr(err)
+ assertEq(ret, hex.EncodeToString(exp))
+ } else {
+ panic("error tx type")
+ }
+ }
+
+ var js []byte
+ if len(result.Notify) != 0 {
+ js, _ = json.Marshal(result.Notify)
+ log.Infof("Notify info : %s", string(js))
+ }
+
+ if result.Result != nil {
+ jsres, _ := json.Marshal(result.Result)
+ log.Infof("Return result: %s", string(jsres))
+ }
+
+ if len(testCase.Notify) != 0 {
+ assertEq(true, strings.Contains(string(js), testCase.Notify))
+ }
+ }
+}
+
+func buildNeoVmValueFromExpect(expectlist []interface{}) *vmtypes.VmValue {
+ if len(expectlist) > 1 {
+ panic("only support return one value")
+ }
+ expect := expectlist[0]
+
+ switch expect.(type) {
+ case string:
+ val, err := vmtypes.VmValueFromBytes([]byte(expect.(string)))
+ if err != nil {
+ panic(err)
+ }
+ return &val
+ case []byte:
+ val, err := vmtypes.VmValueFromBytes(expect.([]byte))
+ if err != nil {
+ panic(err)
+ }
+ return &val
+ case int64:
+ val := vmtypes.VmValueFromInt64(expect.(int64))
+ return &val
+ case bool:
+ val := vmtypes.VmValueFromBool(expect.(bool))
+ return &val
+ case common.Address:
+ addr := expect.(common.Address)
+ val, err := vmtypes.VmValueFromBytes(addr[:])
+ if err != nil {
+ panic(err)
+ }
+ return &val
+ default:
+ fmt.Printf("unspport param type %s", reflect.TypeOf(expect))
+ panic("unspport param type")
+ }
+}
+
+func MakeBlock(acc *account.Account, txs []*types.Transaction) (*types.Block, error) {
+ nextBookkeeper, err := types.AddressFromBookkeepers([]keypair.PublicKey{acc.PublicKey})
+ if err != nil {
+ return nil, fmt.Errorf("GetBookkeeperAddress error:%s", err)
+ }
+ prevHash := ledger.DefLedger.GetCurrentBlockHash()
+ height := ledger.DefLedger.GetCurrentBlockHeight()
+
+ nonce := uint64(height)
+ var txHash []common.Uint256
+ for _, t := range txs {
+ txHash = append(txHash, t.Hash())
+ }
+
+ txRoot := common.ComputeMerkleRoot(txHash)
+
+ blockRoot := ledger.DefLedger.GetBlockRootWithNewTxRoots(height+1, []common.Uint256{txRoot})
+ header := &types.Header{
+ Version: 0,
+ PrevBlockHash: prevHash,
+ TransactionsRoot: txRoot,
+ BlockRoot: blockRoot,
+ Timestamp: constants.GENESIS_BLOCK_TIMESTAMP + height + 1,
+ Height: height + 1,
+ ConsensusData: nonce,
+ NextBookkeeper: nextBookkeeper,
+ }
+ block := &types.Block{
+ Header: header,
+ Transactions: txs,
+ }
+
+ blockHash := block.Hash()
+
+ sig, err := signature.Sign(acc, blockHash[:])
+ if err != nil {
+ return nil, fmt.Errorf("signature, Sign error:%s", err)
+ }
+
+ block.Header.Bookkeepers = []keypair.PublicKey{acc.PublicKey}
+ block.Header.SigData = [][]byte{sig}
+ return block, nil
+}
+
+func assertEq(a interface{}, b interface{}) {
+ if reflect.DeepEqual(a, b) == false {
+ panic(fmt.Sprintf("not equal: a= %v, b=%v", a, b))
+ }
+}
+
+func MakeTestContext(acct *account.Account, contract map[string][]byte) *TestContext {
+ addrMap := make(map[string]common.Address)
+ for file, code := range contract {
+ addrMap[path.Base(file)] = common.AddressFromVmCode(code)
+ }
+
+ testContext := TestContext{
+ Admin: acct.Address,
+ AddrMap: addrMap,
+ }
+ return &testContext
+}
+
+func ExecTxCheckRes(tx *types.Transaction, testCase TestCase, database *ledger.Ledger, addr common.Address, acct *account.Account) error {
+ res, err := database.PreExecuteContract(tx)
+ log.Infof("testcase consume gas: %d", res.Gas)
+ if err != nil {
+ return err
+ }
+
+ height := database.GetCurrentBlockHeight()
+ header, err := database.GetHeaderByHeight(height)
+ checkErr(err)
+ blockTime := header.Timestamp + 1
+
+ execEnv := ExecEnv{Time: blockTime, Height: height + 1, Tx: tx, BlockHash: header.Hash(), Contract: addr}
+ checkExecResult(testCase, res, execEnv)
+
+ block, _ := MakeBlock(acct, []*types.Transaction{tx})
+ err = database.AddBlock(block, common.UINT256_EMPTY)
+ checkErr(err)
+ return nil
+}
+
+func loadContractsByDir(dir string, contracts map[string][]byte) error {
+ fnames, err := filepath.Glob(filepath.Join(dir, "*"))
+ if err != nil {
+ return err
+ }
+
+ for _, name := range fnames {
+ if !strings.HasSuffix(name, ".wasm") && !strings.HasSuffix(name, ".avm") && !strings.HasSuffix(name, ".wasm.str") && !strings.HasSuffix(name, ".avm.str") {
+ continue
+ }
+ raw, err := ioutil.ReadFile(name)
+ if err != nil {
+ return err
+ }
+
+ if strings.HasSuffix(name, ".str") {
+ code, err := hex.DecodeString(strings.TrimSpace(string(raw)))
+ if err != nil {
+ return err
+ }
+ contracts[path.Base(name)] = code
+ } else {
+ contracts[path.Base(name)] = raw
+ }
+ }
+
+ return nil
+}
+
+func DeployContract(acct *account.Account, database *ledger.Ledger, contract map[string][]byte) error {
+ txes := make([]*types.Transaction, 0, len(contract))
+ for file, cont := range contract {
+ var tx *types.Transaction
+ var err error
+ if strings.HasSuffix(file, ".wasm") || strings.HasSuffix(file, ".wasm.str") {
+ tx, err = NewDeployWasmContract(acct, cont)
+ } else if strings.HasSuffix(file, ".avm") || strings.HasSuffix(file, ".avm.str") {
+ tx, err = NewDeployNeoContract(acct, cont)
+ } else {
+ panic("error file suffix")
+ }
+
+ if err != nil {
+ return err
+ }
+
+ res, err := database.PreExecuteContract(tx)
+ log.Infof("deploy %s consume gas: %d", file, res.Gas)
+ if err != nil {
+ return err
+ }
+ txes = append(txes, tx)
+ }
+
+ block, err := MakeBlock(acct, txes)
+ checkErr(err)
+ err = database.AddBlock(block, common.UINT256_EMPTY)
+ checkErr(err)
+ return nil
+}
+
+func NewDeployWasmContract(signer *account.Account, code []byte) (*types.Transaction, error) {
+ mutable, err := utils.NewDeployCodeTransaction(0, 100000000, code, payload.WASMVM_TYPE, "name", "version",
+ "author", "email", "desc")
+ if err != nil {
+ return nil, err
+ }
+ err = utils.SignTransaction(signer, mutable)
+ if err != nil {
+ return nil, err
+ }
+ tx, err := mutable.IntoImmutable()
+ return tx, err
+}
+
+func NewDeployNeoContract(signer *account.Account, code []byte) (*types.Transaction, error) {
+ mutable, err := utils.NewDeployCodeTransaction(0, 100000000, code, payload.NEOVM_TYPE, "name", "version",
+ "author", "email", "desc")
+ if err != nil {
+ return nil, err
+ }
+ err = utils.SignTransaction(signer, mutable)
+ if err != nil {
+ return nil, err
+ }
+ tx, err := mutable.IntoImmutable()
+ return tx, err
+}
+
+func InvokeSpecifiedContract(acct *account.Account, database *ledger.Ledger, contractfile string, paramsStr string, testContext *TestContext) error {
+ var tx *types.Transaction
+ if _, exist := testContext.AddrMap[contractfile]; !exist {
+ panic("Contract not exist")
+ }
+
+ if strings.HasSuffix(contractfile, ".avm") || strings.HasSuffix(contractfile, ".avm.str") {
+ testCase := TestCase{Param: paramsStr}
+ t, err := GenNeoVMTransaction(testCase, testContext.AddrMap[contractfile], testContext)
+ tx = t
+ if err != nil {
+ return err
+ }
+ } else if strings.HasSuffix(contractfile, ".wasm") || strings.HasSuffix(contractfile, ".wasm.str") {
+ testCase := TestCase{Param: paramsStr}
+ t, err := GenWasmTransaction(testCase, testContext.AddrMap[contractfile], testContext)
+ tx = t
+ if err != nil {
+ return err
+ }
+ } else {
+ panic("error suffix type")
+ }
+
+ res, err := database.PreExecuteContract(tx)
+ log.Infof("testcase consume gas: %d", res.Gas)
+ if err != nil {
+ return err
+ }
+ block, err := MakeBlock(acct, []*types.Transaction{tx})
+ checkErr(err)
+ err = database.AddBlock(block, common.UINT256_EMPTY)
+ checkErr(err)
+ if len(res.Notify) != 0 {
+ js, _ := json.Marshal(res.Notify)
+ log.Infof("Notify info : %s", string(js))
+ }
+
+ if res.Result != nil {
+ js, _ := json.Marshal(res.Result)
+ log.Infof("Return result: %s", string(js))
+ }
+
+ return nil
+}
+
+func GetContact(deployobject string) (map[string][]byte, bool, error) {
+ var objIsDir bool
+ obj, err := os.Stat(deployobject)
+ if err != nil {
+ return nil, false, err
+ }
+
+ contract := make(map[string][]byte)
+ if obj.IsDir() {
+ objIsDir = true
+ err := loadContractsByDir(deployobject, contract)
+ if err != nil {
+ return nil, false, err
+ }
+ } else {
+ objIsDir = false
+ if !strings.HasSuffix(deployobject, ".wasm") && !strings.HasSuffix(deployobject, ".avm") && !strings.HasSuffix(deployobject, ".wasm.str") && !strings.HasSuffix(deployobject, ".avm.str") {
+ return nil, false, fmt.Errorf("[contract file suffix error]: filename \"%s\" must be .wasm/.avm/.wasm.str/.avm.str.", deployobject)
+ }
+ raw, err := ioutil.ReadFile(deployobject)
+ if err != nil {
+ return nil, false, err
+ }
+ if strings.HasSuffix(deployobject, ".str") {
+ code, err := hex.DecodeString(strings.TrimSpace(string(raw)))
+ if err != nil {
+ return nil, false, err
+ }
+ contract[path.Base(deployobject)] = code
+ } else {
+ contract[path.Base(deployobject)] = raw
+ }
+ }
+
+ if len(contract) == 0 {
+ return nil, false, fmt.Errorf("no contract to test.")
+ }
+
+ return contract, objIsDir, nil
+}
+
+func TestWithConfigElt(acct *account.Account, database *ledger.Ledger, file string, testCase TestCase, testContext *TestContext) error {
+ var tx *types.Transaction
+ val, _ := json.Marshal(testCase)
+ log.Info("executing testcase: ", string(val))
+ addr, exist := testContext.AddrMap[file]
+ if !exist {
+ return fmt.Errorf("Contract %s not exist. ", file)
+ }
+
+ if strings.HasSuffix(file, ".avm") || strings.HasSuffix(file, ".avm.str") {
+ t, err := GenNeoVMTransaction(testCase, addr, testContext)
+ if err != nil {
+ return err
+ }
+ tx = t
+ } else if strings.HasSuffix(file, ".wasm") || strings.HasSuffix(file, ".wasm.str") {
+ t, err := GenWasmTransaction(testCase, addr, testContext)
+ if err != nil {
+ return err
+ }
+ tx = t
+ }
+
+ err := ExecTxCheckRes(tx, testCase, database, addr, acct)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ====================================
+func TestWithbatchMode(acct *account.Account, database *ledger.Ledger, contract map[string][]byte, testContext *TestContext) {
+ for file, cont := range contract {
+ testSpecifiedContractWithbatchMode(acct, database, file, cont, testContext)
+ }
+
+ log.Info("contract test succeed")
+}
+
+func testSpecifiedContractWithbatchMode(acct *account.Account, database *ledger.Ledger, file string, cont []byte, testContext *TestContext) {
+ log.Infof("exacting testcase from %s", file)
+ addr := testContext.AddrMap[file]
+
+ if strings.HasSuffix(file, ".avm") || strings.HasSuffix(file, ".avm.str") {
+ testCases := GenNeoTextCaseTransaction(addr, database)
+ for _, testCase := range testCases[0] { // only handle group 0 currently
+ err := TestWithConfigElt(acct, database, file, testCase, testContext)
+ if err != nil {
+ panic(err)
+ }
+ }
+ } else if strings.HasSuffix(file, ".wasm") || strings.HasSuffix(file, ".wasm.str") {
+ testCases := ExactTestCase(cont)
+ for _, testCase := range testCases[0] { // only handle group 0 currently
+ err := TestWithConfigElt(acct, database, file, testCase, testContext)
+ if err != nil {
+ panic(err)
+ }
+ }
+ } else {
+ panic("testSpecifiedContractWithbatchMode: error suffix contract name")
+ }
+}
+
+func ExactTestCase(code []byte) [][]TestCase {
+ m, err := wasm.ReadModule(bytes.NewReader(code), func(name string) (*wasm.Module, error) {
+ switch name {
+ case "env":
+ return wasmvm.NewHostModule(), nil
+ }
+ return nil, fmt.Errorf("module %q unknown", name)
+ })
+ checkErr(err)
+
+ compiled, err := exec.CompileModule(m)
+ checkErr(err)
+
+ vm, err := exec.NewVMWithCompiled(compiled, 10*1024*1024)
+ checkErr(err)
+
+ param := common.NewZeroCopySink(nil)
+ param.WriteString(testcaseMethod)
+ host := &wasmvm.Runtime{Input: param.Bytes()}
+ vm.HostData = host
+ vm.RecoverPanic = true
+ envGasLimit := uint64(100000000000000)
+ envExecStep := uint64(100000000000000)
+ vm.AvaliableGas = &exec.Gas{GasLimit: &envGasLimit, GasPrice: 0, GasFactor: 5, ExecStep: &envExecStep}
+ vm.CallStackDepth = 1024
+
+ entry := compiled.RawModule.Export.Entries["invoke"]
+ index := int64(entry.Index)
+ _, err = vm.ExecCode(index)
+ checkErr(err)
+
+ var testCase [][]TestCase
+ source := common.NewZeroCopySource(host.Output)
+ jsonCase, _, _, _ := source.NextString()
+
+ if len(jsonCase) == 0 {
+ panic("failed to get testcase data from contract")
+ }
+
+ err = json.Unmarshal([]byte(jsonCase), &testCase)
+ checkErr(err)
+
+ return testCase
+}
+
+func GenNeoTextCaseTransaction(contract common.Address, database *ledger.Ledger) [][]TestCase {
+ params := make([]interface{}, 0)
+ method := string("testcase")
+ // neovm entry api is def Main(method, args). and testcase method api need no other args, so pass a random args to entry api.
+ operation := 1
+ params = append(params, method)
+ params = append(params, operation)
+ tx, err := common2.NewNeovmInvokeTransaction(0, 100000000, contract, params)
+ imt, err := tx.IntoImmutable()
+ if err != nil {
+ panic(err)
+ }
+ res, err := database.PreExecuteContract(imt)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := res.Result.(string)
+ jsonCase, err := common.HexToBytes(ret)
+
+ if err != nil {
+ panic(err)
+ }
+ if len(jsonCase) == 0 {
+ panic("failed to get testcase data from contract")
+ }
+ var testCase [][]TestCase
+ err = json.Unmarshal([]byte(jsonCase), &testCase)
+ if err != nil {
+ panic("failed Unmarshal")
+ }
+ return testCase
+}
diff --git a/wasmtest/run-wasm-tests.sh b/wasmtest/run-wasm-tests.sh
index 4bb2d6f18f..ff30087cfe 100644
--- a/wasmtest/run-wasm-tests.sh
+++ b/wasmtest/run-wasm-tests.sh
@@ -16,14 +16,15 @@ fi
rustup target add wasm32-unknown-unknown
which ontio-wasm-build || cargo install --git=https://github.com/ontio/ontio-wasm-build
+contractdir="testwasmdata"
# build rust wasm contracts
-mkdir -p testwasmdata
+mkdir -p $contractdir
cd contracts-rust && bash travis.build.sh && cd ../
cd contracts-cplus && bash travis.build.bash && cd ../
# verify and optimize wasm contract
-for wasm in testwasmdata/*.wasm ; do
+for wasm in $contractdir/*.wasm ; do
ontio-wasm-build $wasm $wasm
done
diff --git a/wasmtest/wasm-test.go b/wasmtest/wasm-test.go
index 34646edfaa..ad1f49d556 100644
--- a/wasmtest/wasm-test.go
+++ b/wasmtest/wasm-test.go
@@ -19,446 +19,33 @@
package main
import (
- "bytes"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
-
- "github.com/go-interpreter/wagon/exec"
- "github.com/go-interpreter/wagon/wasm"
- "github.com/ontio/ontology-crypto/keypair"
- "github.com/ontio/ontology/account"
- "github.com/ontio/ontology/cmd/utils"
- "github.com/ontio/ontology/common"
- "github.com/ontio/ontology/common/config"
- "github.com/ontio/ontology/common/constants"
"github.com/ontio/ontology/common/log"
- "github.com/ontio/ontology/core/genesis"
- "github.com/ontio/ontology/core/ledger"
- "github.com/ontio/ontology/core/payload"
- "github.com/ontio/ontology/core/signature"
- "github.com/ontio/ontology/core/types"
- utils2 "github.com/ontio/ontology/core/utils"
- "github.com/ontio/ontology/events"
- common2 "github.com/ontio/ontology/http/base/common"
- "github.com/ontio/ontology/smartcontract/service/wasmvm"
- "github.com/ontio/ontology/smartcontract/states"
- vmtypes "github.com/ontio/ontology/vm/neovm/types"
- common3 "github.com/ontio/ontology/wasmtest/common"
+ "github.com/ontio/ontology/wasmtest/common"
)
-const contractDir = "testwasmdata"
-const testcaseMethod = "testcase"
-
-func NewDeployWasmContract(signer *account.Account, code []byte) (*types.Transaction, error) {
- mutable, err := utils.NewDeployCodeTransaction(0, 100000000, code, payload.WASMVM_TYPE, "name", "version",
- "author", "email", "desc")
- if err != nil {
- return nil, err
- }
- err = utils.SignTransaction(signer, mutable)
- if err != nil {
- return nil, err
- }
- tx, err := mutable.IntoImmutable()
- return tx, err
-}
-
-func NewDeployNeoContract(signer *account.Account, code []byte) (*types.Transaction, error) {
- mutable, err := utils.NewDeployCodeTransaction(0, 100000000, code, payload.NEOVM_TYPE, "name", "version",
- "author", "email", "desc")
- if err != nil {
- return nil, err
- }
- err = utils.SignTransaction(signer, mutable)
- if err != nil {
- return nil, err
- }
- tx, err := mutable.IntoImmutable()
- return tx, err
-}
-
-func GenNeoTextCaseTransaction(contract common.Address, database *ledger.Ledger) [][]common3.TestCase {
- params := make([]interface{}, 0)
- method := string("testcase")
- // neovm entry api is def Main(method, args). and testcase method api need no other args, so pass a random args to entry api.
- operation := 1
- params = append(params, method)
- params = append(params, operation)
- tx, err := common2.NewNeovmInvokeTransaction(0, 100000000, contract, params)
- imt, err := tx.IntoImmutable()
- if err != nil {
- panic(err)
- }
- res, err := database.PreExecuteContract(imt)
- if err != nil {
- panic(err)
- }
+const (
+ testdir = "testwasmdata"
+)
- ret := res.Result.(string)
- jsonCase, err := common.HexToBytes(ret)
+func main() {
+ acct, database := common.InitOntologyLedger()
+ log.Info("loading contract")
+ contract, objIsDir, err := common.GetContact(testdir)
if err != nil {
panic(err)
}
- if len(jsonCase) == 0 {
- panic("failed to get testcase data from contract")
- }
- var testCase [][]common3.TestCase
- err = json.Unmarshal([]byte(jsonCase), &testCase)
- if err != nil {
- panic("failed Unmarshal")
- }
- return testCase
-}
-
-func ExactTestCase(code []byte) [][]common3.TestCase {
- m, err := wasm.ReadModule(bytes.NewReader(code), func(name string) (*wasm.Module, error) {
- switch name {
- case "env":
- return wasmvm.NewHostModule(), nil
- }
- return nil, fmt.Errorf("module %q unknown", name)
- })
- checkErr(err)
-
- compiled, err := exec.CompileModule(m)
- checkErr(err)
-
- vm, err := exec.NewVMWithCompiled(compiled, 10*1024*1024)
- checkErr(err)
-
- param := common.NewZeroCopySink(nil)
- param.WriteString(testcaseMethod)
- host := &wasmvm.Runtime{Input: param.Bytes()}
- vm.HostData = host
- vm.RecoverPanic = true
- envGasLimit := uint64(100000000000000)
- envExecStep := uint64(100000000000000)
- vm.AvaliableGas = &exec.Gas{GasLimit: &envGasLimit, GasPrice: 0, GasFactor: 5, ExecStep: &envExecStep}
- vm.CallStackDepth = 1024
-
- entry := compiled.RawModule.Export.Entries["invoke"]
- index := int64(entry.Index)
- _, err = vm.ExecCode(index)
- checkErr(err)
-
- var testCase [][]common3.TestCase
- source := common.NewZeroCopySource(host.Output)
- jsonCase, _, _, _ := source.NextString()
-
- if len(jsonCase) == 0 {
- panic("failed to get testcase data from contract")
+ if !objIsDir {
+ panic("testwasmdata is not a dir")
}
- err = json.Unmarshal([]byte(jsonCase), &testCase)
- checkErr(err)
-
- return testCase
-}
-
-func LoadContracts(dir string) (map[string][]byte, error) {
- contracts := make(map[string][]byte)
- fnames, err := filepath.Glob(filepath.Join(dir, "*"))
- if err != nil {
- return nil, err
- }
- for _, name := range fnames {
- if !(strings.HasSuffix(name, ".wasm") || strings.HasSuffix(name, ".avm")) {
- continue
- }
- raw, err := ioutil.ReadFile(name)
- if err != nil {
- return nil, err
- }
- contracts[path.Base(name)] = raw
- }
-
- return contracts, nil
-}
-
-func init() {
- log.InitLog(log.InfoLog, log.PATH, log.Stdout)
- runtime.GOMAXPROCS(4)
-}
-
-func checkErr(err error) {
+ log.Infof("deploying %d contracts", len(contract))
+ err = common.DeployContract(acct, database, contract)
if err != nil {
panic(err)
}
-}
-
-func execTxCheckRes(tx *types.Transaction, testCase common3.TestCase, database *ledger.Ledger, addr common.Address, acct *account.Account) {
- res, err := database.PreExecuteContract(tx)
- log.Infof("testcase consume gas: %d", res.Gas)
- checkErr(err)
-
- height := database.GetCurrentBlockHeight()
- header, err := database.GetHeaderByHeight(height)
- checkErr(err)
- blockTime := header.Timestamp + 1
- execEnv := ExecEnv{Time: blockTime, Height: height + 1, Tx: tx, BlockHash: header.Hash(), Contract: addr}
- checkExecResult(testCase, res, execEnv)
+ testContext := common.MakeTestContext(acct, contract)
- block, _ := makeBlock(acct, []*types.Transaction{tx})
- err = database.AddBlock(block, common.UINT256_EMPTY)
- checkErr(err)
-}
-
-func main() {
- datadir := "testdata"
- err := os.RemoveAll(datadir)
- defer func() {
- _ = os.RemoveAll(datadir)
- _ = os.RemoveAll(log.PATH)
- }()
- checkErr(err)
- log.Trace("Node version: ", config.Version)
-
- acct := account.NewAccount("")
- buf := keypair.SerializePublicKey(acct.PublicKey)
- config.DefConfig.Genesis.ConsensusType = "solo"
- config.DefConfig.Genesis.SOLO.GenBlockTime = 3
- config.DefConfig.Genesis.SOLO.Bookkeepers = []string{hex.EncodeToString(buf)}
- config.DefConfig.P2PNode.NetworkId = 0
-
- bookkeepers := []keypair.PublicKey{acct.PublicKey}
- //Init event hub
- events.Init()
-
- log.Info("1. Loading the Ledger")
- database, err := ledger.NewLedger(datadir, 1000000)
- checkErr(err)
- ledger.DefLedger = database
- genblock, err := genesis.BuildGenesisBlock(bookkeepers, config.DefConfig.Genesis)
- checkErr(err)
- err = database.Init(bookkeepers, genblock)
- checkErr(err)
-
- log.Info("loading wasm contract")
- contract, err := LoadContracts(contractDir)
- checkErr(err)
-
- log.Infof("deploying %d wasm contracts", len(contract))
- txes := make([]*types.Transaction, 0, len(contract))
- for file, cont := range contract {
- var tx *types.Transaction
- var err error
- if strings.HasSuffix(file, ".wasm") {
- tx, err = NewDeployWasmContract(acct, cont)
- } else if strings.HasSuffix(file, ".avm") {
- tx, err = NewDeployNeoContract(acct, cont)
- }
-
- checkErr(err)
-
- res, err := database.PreExecuteContract(tx)
- log.Infof("deploy %s consume gas: %d", file, res.Gas)
- checkErr(err)
- txes = append(txes, tx)
- }
-
- block, _ := makeBlock(acct, txes)
- err = database.AddBlock(block, common.UINT256_EMPTY)
- checkErr(err)
-
- addrMap := make(map[string]common.Address)
- for file, code := range contract {
- addrMap[path.Base(file)] = common.AddressFromVmCode(code)
- }
-
- testContext := common3.TestContext{
- Admin: acct.Address,
- AddrMap: addrMap,
- }
-
- for file, cont := range contract {
- log.Infof("exacting testcase from %s", file)
- addr := common.AddressFromVmCode(cont)
- if strings.HasSuffix(file, ".avm") {
- testCases := GenNeoTextCaseTransaction(addr, database)
- for _, testCase := range testCases[0] { // only handle group 0 currently
- val, _ := json.Marshal(testCase)
- log.Info("executing testcase: ", string(val))
- tx, err := common3.GenNeoVMTransaction(testCase, addr, &testContext)
- checkErr(err)
-
- execTxCheckRes(tx, testCase, database, addr, acct)
- }
- } else if strings.HasSuffix(file, ".wasm") {
- testCases := ExactTestCase(cont)
- for _, testCase := range testCases[0] { // only handle group 0 currently
- val, _ := json.Marshal(testCase)
- log.Info("executing testcase: ", string(val))
- tx, err := common3.GenWasmTransaction(testCase, addr, &testContext)
- checkErr(err)
-
- execTxCheckRes(tx, testCase, database, addr, acct)
- }
- }
- }
-
- log.Info("contract test succeed")
-}
-
-type ExecEnv struct {
- Contract common.Address
- Time uint32
- Height uint32
- Tx *types.Transaction
- BlockHash common.Uint256
-}
-
-func checkExecResult(testCase common3.TestCase, result *states.PreExecResult, execEnv ExecEnv) {
- assertEq(result.State, byte(1))
- ret := result.Result.(string)
- switch testCase.Method {
- case "timestamp":
- sink := common.NewZeroCopySink(nil)
- sink.WriteUint64(uint64(execEnv.Time))
- assertEq(ret, hex.EncodeToString(sink.Bytes()))
- case "block_height":
- sink := common.NewZeroCopySink(nil)
- sink.WriteUint32(uint32(execEnv.Height))
- assertEq(ret, hex.EncodeToString(sink.Bytes()))
- case "self_address", "entry_address":
- assertEq(ret, hex.EncodeToString(execEnv.Contract[:]))
- case "caller_address":
- assertEq(ret, hex.EncodeToString(common.ADDRESS_EMPTY[:]))
- case "current_txhash":
- hash := execEnv.Tx.Hash()
- assertEq(ret, hex.EncodeToString(hash[:]))
- case "current_blockhash":
- assertEq(ret, hex.EncodeToString(execEnv.BlockHash[:]))
- //case "sha256":
- // let data :&[u8]= source.read().unwrap();
- // sink.write(runtime::sha256(&data))
- //}
- default:
- if len(testCase.Expect) != 0 {
- expect, err := utils.ParseParams(testCase.Expect)
- checkErr(err)
- if execEnv.Tx.TxType == types.InvokeNeo {
- val := buildNeoVmValueFromExpect(expect)
- cv, err := val.ConvertNeoVmValueHexString()
- checkErr(err)
- assertEq(cv, result.Result)
- } else if execEnv.Tx.TxType == types.InvokeWasm {
- exp, err := utils2.BuildWasmContractParam(expect)
- checkErr(err)
- assertEq(ret, hex.EncodeToString(exp))
- } else {
- panic("error tx type")
- }
- }
- if len(testCase.Notify) != 0 {
- js, _ := json.Marshal(result.Notify)
- assertEq(true, strings.Contains(string(js), testCase.Notify))
- }
- }
-}
-
-func buildNeoVmValueFromExpect(expectlist []interface{}) *vmtypes.VmValue {
- if len(expectlist) > 1 {
- panic("only support return one value")
- }
- expect := expectlist[0]
-
- switch expect.(type) {
- case string:
- val, err := vmtypes.VmValueFromBytes([]byte(expect.(string)))
- if err != nil {
- panic(err)
- }
- return &val
- case []byte:
- val, err := vmtypes.VmValueFromBytes(expect.([]byte))
- if err != nil {
- panic(err)
- }
- return &val
- case int64:
- val := vmtypes.VmValueFromInt64(expect.(int64))
- return &val
- case bool:
- val := vmtypes.VmValueFromBool(expect.(bool))
- return &val
- case common.Address:
- addr := expect.(common.Address)
- val, err := vmtypes.VmValueFromBytes(addr[:])
- if err != nil {
- panic(err)
- }
- return &val
- default:
- fmt.Printf("unspport param type %s", reflect.TypeOf(expect))
- panic("unspport param type")
- }
-}
-
-func GenAccounts(num int) []*account.Account {
- var accounts []*account.Account
- for i := 0; i < num; i++ {
- acc := account.NewAccount("")
- accounts = append(accounts, acc)
- }
- return accounts
-}
-
-func makeBlock(acc *account.Account, txs []*types.Transaction) (*types.Block, error) {
- nextBookkeeper, err := types.AddressFromBookkeepers([]keypair.PublicKey{acc.PublicKey})
- if err != nil {
- return nil, fmt.Errorf("GetBookkeeperAddress error:%s", err)
- }
- prevHash := ledger.DefLedger.GetCurrentBlockHash()
- height := ledger.DefLedger.GetCurrentBlockHeight()
-
- nonce := uint64(height)
- var txHash []common.Uint256
- for _, t := range txs {
- txHash = append(txHash, t.Hash())
- }
-
- txRoot := common.ComputeMerkleRoot(txHash)
-
- blockRoot := ledger.DefLedger.GetBlockRootWithNewTxRoots(height+1, []common.Uint256{txRoot})
- header := &types.Header{
- Version: 0,
- PrevBlockHash: prevHash,
- TransactionsRoot: txRoot,
- BlockRoot: blockRoot,
- Timestamp: constants.GENESIS_BLOCK_TIMESTAMP + height + 1,
- Height: height + 1,
- ConsensusData: nonce,
- NextBookkeeper: nextBookkeeper,
- }
- block := &types.Block{
- Header: header,
- Transactions: txs,
- }
-
- blockHash := block.Hash()
-
- sig, err := signature.Sign(acc, blockHash[:])
- if err != nil {
- return nil, fmt.Errorf("signature, Sign error:%s", err)
- }
-
- block.Header.Bookkeepers = []keypair.PublicKey{acc.PublicKey}
- block.Header.SigData = [][]byte{sig}
- return block, nil
-}
-
-func assertEq(a interface{}, b interface{}) {
- if reflect.DeepEqual(a, b) == false {
- panic(fmt.Sprintf("not equal: a= %v, b=%v", a, b))
- }
+ common.TestWithbatchMode(acct, database, contract, testContext)
}