Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cli tool #1108

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 201 additions & 0 deletions localtool/cli.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

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 <input> 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
}
28 changes: 28 additions & 0 deletions localtool/config.con
Original file line number Diff line number Diff line change
@@ -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"}
]
]
}
]
}
83 changes: 83 additions & 0 deletions localtool/test_native.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include<ontiolib/ontio.hpp>
using std::string;
using std::vector;

namespace ontio {
struct test_conext {
address admin;
std::map<string, address> 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))
15 changes: 11 additions & 4 deletions wasmtest/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package common

import (
"bytes"
"fmt"

"encoding/json"
utils2 "github.com/ontio/ontology/cmd/utils"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Loading