Merge pull request #1426 from nspcc-dev/rpc/getcontractstate_by_id_or_name

rpc: allow to `getcontractstate` by id or name
This commit is contained in:
Roman Khimov 2020-11-05 13:17:42 +03:00 committed by GitHub
commit 679846c1a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 347 additions and 117 deletions

View file

@ -11,7 +11,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/stretchr/testify/require"
)
@ -91,7 +90,7 @@ func TestSignMultisigTx(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--address", multisigAddr,
"--out", txPath,
client.NeoContractHash.StringLE(), "transfer",
e.Chain.GoverningTokenHash().StringLE(), "transfer",
"bytes:"+multisigHash.StringBE(),
"bytes:"+priv.GetScriptHash().StringBE(),
"int:1",

View file

@ -10,7 +10,6 @@ import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
@ -138,6 +137,8 @@ func TestNEP5MultiTransfer(t *testing.T) {
e := newExecutor(t, true)
defer e.Close(t)
neoContractHash, err := e.Chain.GetNativeContractScriptHash("neo")
require.NoError(t, err)
args := []string{
"neo-go", "wallet", "nep5", "multitransfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
@ -145,7 +146,7 @@ func TestNEP5MultiTransfer(t *testing.T) {
"--from", validatorAddr,
"neo:" + privs[0].Address() + ":42",
"GAS:" + privs[1].Address() + ":7",
client.NeoContractHash.StringLE() + ":" + privs[2].Address() + ":13",
neoContractHash.StringLE() + ":" + privs[2].Address() + ":13",
}
e.In.WriteString("one\r")
@ -168,27 +169,31 @@ func TestNEP5ImportToken(t *testing.T) {
walletPath := path.Join(tmpDir, "walletForImport.json")
defer os.Remove(walletPath)
neoContractHash, err := e.Chain.GetNativeContractScriptHash("neo")
require.NoError(t, err)
gasContractHash, err := e.Chain.GetNativeContractScriptHash("gas")
require.NoError(t, err)
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
e.Run(t, "neo-go", "wallet", "nep5", "import",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath,
"--token", client.GasContractHash.StringLE())
"--token", gasContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep5", "import",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath,
"--token", client.NeoContractHash.StringLE())
"--token", neoContractHash.StringLE())
t.Run("Info", func(t *testing.T) {
checkGASInfo := func(t *testing.T) {
e.checkNextLine(t, "^Name:\\s*GAS")
e.checkNextLine(t, "^Symbol:\\s*gas")
e.checkNextLine(t, "^Hash:\\s*"+client.GasContractHash.StringLE())
e.checkNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
e.checkNextLine(t, "^Decimals:\\s*8")
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(client.GasContractHash))
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
}
t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep5", "info",
"--wallet", walletPath, "--token", client.GasContractHash.StringLE())
"--wallet", walletPath, "--token", gasContractHash.StringLE())
checkGASInfo(t)
})
t.Run("NoToken", func(t *testing.T) {
@ -199,14 +204,14 @@ func TestNEP5ImportToken(t *testing.T) {
require.NoError(t, err)
e.checkNextLine(t, "^Name:\\s*NEO")
e.checkNextLine(t, "^Symbol:\\s*neo")
e.checkNextLine(t, "^Hash:\\s*"+client.NeoContractHash.StringLE())
e.checkNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
e.checkNextLine(t, "^Decimals:\\s*0")
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(client.NeoContractHash))
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
})
t.Run("Remove", func(t *testing.T) {
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep5", "remove",
"--wallet", walletPath, "--token", client.NeoContractHash.StringLE())
"--wallet", walletPath, "--token", neoContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep5", "info",
"--wallet", walletPath)
checkGASInfo(t)

View file

@ -2,8 +2,6 @@ package options
import (
"flag"
"net/http"
"net/http/httptest"
"testing"
"time"
@ -57,34 +55,3 @@ func TestGetTimeoutContext(t *testing.T) {
require.True(t, start.Before(dl) && dl.Before(end.Add(time.Nanosecond*20)))
})
}
func TestGetRPCClient(t *testing.T) {
// need test server for proper client.Init() handling
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
response := `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
_, err := w.Write([]byte(response))
if err != nil {
t.Fatalf("Error writing response: %s", err.Error())
}
}))
defer srv.Close()
t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := GetTimeoutContext(ctx)
_, ec := GetRPCClient(gctx, ctx)
require.Equal(t, 1, ec.ExitCode())
})
t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(RPCEndpointFlag, srv.URL, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := GetTimeoutContext(ctx)
_, ec := GetRPCClient(gctx, ctx)
require.Nil(t, ec)
})
}

32
cli/options_test.go Normal file
View file

@ -0,0 +1,32 @@
package main
import (
"flag"
"testing"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGetRPCClient(t *testing.T) {
e := newExecutor(t, true)
defer e.Close(t)
t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx)
require.Equal(t, 1, ec.ExitCode())
})
t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addr, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx)
require.Nil(t, ec)
})
}

View file

@ -80,7 +80,7 @@ func signMultisig(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
res, err := c.SendRawTransaction(tx)
if err != nil {

View file

@ -15,11 +15,6 @@ import (
"github.com/urfave/cli"
)
var (
neoToken = wallet.NewToken(client.NeoContractHash, "NEO", "neo", 0)
gasToken = wallet.NewToken(client.GasContractHash, "GAS", "gas", 8)
)
var (
tokenFlag = cli.StringFlag{
Name: "token",
@ -161,7 +156,11 @@ func getNEP5Balance(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
err = c.Init()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to init RPC client: %w", err), 1)
}
name := ctx.String("token")
@ -210,12 +209,6 @@ func getNEP5Balance(ctx *cli.Context) error {
}
func getMatchingToken(ctx *cli.Context, w *wallet.Wallet, name string) (*wallet.Token, error) {
switch strings.ToLower(name) {
case "neo", client.NeoContractHash.StringLE():
return neoToken, nil
case "gas", client.GasContractHash.StringLE():
return gasToken, nil
}
return getMatchingTokenAux(ctx, func(i int) *wallet.Token {
return w.Extra.Tokens[i]
}, len(w.Extra.Tokens), name)
@ -278,7 +271,7 @@ func importNEP5Token(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
tok, err := c.NEP5TokenInfo(tokenHash)
@ -371,7 +364,11 @@ func multiTransferNEP5(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
err = c.Init()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to init RPC client: %w", err), 1)
}
if ctx.NArg() == 0 {
@ -434,7 +431,11 @@ func transferNEP5(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
err = c.Init()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to init RPC client: %w", err), 1)
}
toFlag := ctx.Generic("to").(*flags.Address)

View file

@ -10,7 +10,6 @@ import (
"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/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
"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"
@ -96,12 +95,16 @@ func handleCandidate(ctx *cli.Context, method string) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
gas := flags.Fixed8FromContext(ctx, "gas")
neoContractHash, err := c.GetNativeContractHash("neo")
if err != nil {
return err
}
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, method, acc.PrivateKey().PublicKey().Bytes())
emit.AppCallWithOperationAndArgs(w.BinWriter, neoContractHash, method, acc.PrivateKey().PublicKey().Bytes())
emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{
Account: acc.Contract.ScriptHash(),
@ -149,7 +152,7 @@ func handleVote(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
var pubArg interface{}
@ -158,8 +161,12 @@ func handleVote(ctx *cli.Context) error {
}
gas := flags.Fixed8FromContext(ctx, "gas")
neoContractHash, err := c.GetNativeContractHash("neo")
if err != nil {
return cli.NewExitError(err, 1)
}
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, "vote", addr.BytesBE(), pubArg)
emit.AppCallWithOperationAndArgs(w.BinWriter, neoContractHash, "vote", addr.BytesBE(), pubArg)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{

View file

@ -12,7 +12,6 @@ import (
"github.com/nspcc-dev/neo-go/cli/options"
"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/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
@ -231,10 +230,14 @@ func claimGas(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
hash, err := c.TransferNEP5(acc, scriptHash, client.NeoContractHash, 0, 0)
neoContractHash, err := c.GetNativeContractHash("neo")
if err != nil {
return cli.NewExitError(err, 1)
}
hash, err := c.TransferNEP5(acc, scriptHash, neoContractHash, 0, 0)
if err != nil {
return cli.NewExitError(err, 1)
}
@ -423,10 +426,10 @@ func importDeployed(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return cli.NewExitError(err, 1)
}
cs, err := c.GetContractState(h)
cs, err := c.GetContractStateByHash(h)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch contract info: %w", err), 1)
}

View file

@ -70,13 +70,20 @@ key in the server which doesn't fit the model of our node-client interactions.
Lacking this signature the transaction is almost useless, so there is no point
in returning it.
Both methods also don't currently support arrays in function parameters.
It's possible to use `invokefunction` not only with contract scripthash, but also
with contract name (for native contracts) or contract ID (for all contracts). This
feature is not supported by the C# node.
##### `getunclaimedgas`
It's possible to call this method for any address with neo-go, unlike with C#
node where it only works for addresses from opened wallet.
##### `getcontractstate`
It's possible to get non-native contract state by its ID, unlike with C# node where
it only works for native contracts.
### Unsupported methods
Methods listed down below are not going to be supported for various reasons

View file

@ -1073,6 +1073,15 @@ func (bc *Blockchain) GetContractScriptHash(id int32) (util.Uint160, error) {
return bc.dao.GetContractScriptHash(id)
}
// GetNativeContractScriptHash returns native contract script hash by its name.
func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, error) {
c := bc.contracts.ByName(name)
if c != nil {
return c.Metadata().Hash, nil
}
return util.Uint160{}, errors.New("Unknown native contract")
}
// GetConfig returns the config stored in the blockchain.
func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
return bc.config

View file

@ -39,6 +39,7 @@ type Blockchainer interface {
HasBlock(util.Uint256) bool
HasTransaction(util.Uint256) bool
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
GetNativeContractScriptHash(string) (util.Uint160, error)
GetNextBlockValidators() ([]*keys.PublicKey, error)
GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators() ([]*keys.PublicKey, error)

View file

@ -2,6 +2,7 @@ package native
import (
"errors"
"strings"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/io"
@ -35,6 +36,17 @@ func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
return nil
}
// ByName returns native contract with the specified name.
func (cs *Contracts) ByName(name string) interop.Contract {
name = strings.ToLower(name)
for _, ctr := range cs.Contracts {
if strings.ToLower(ctr.Metadata().Name) == name {
return ctr
}
}
return nil
}
// NewContracts returns new set of native contracts with new GAS, NEO and Policy
// contracts.
func NewContracts() *Contracts {

View file

@ -89,6 +89,9 @@ func (chain testChain) GetContractState(hash util.Uint160) *state.Contract {
func (chain testChain) GetContractScriptHash(id int32) (util.Uint160, error) {
panic("TODO")
}
func (chain testChain) GetNativeContractScriptHash(name string) (util.Uint160, error) {
panic("TODO")
}
func (chain testChain) GetHeaderHash(int) util.Uint256 {
return util.Uint256{}
}

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/util"
)
const (
@ -53,6 +54,7 @@ type Options struct {
// cache stores cache values for the RPC client methods
type cache struct {
calculateValidUntilBlock calculateValidUntilBlockCache
nativeHashes map[string]util.Uint160
}
// calculateValidUntilBlockCache stores cached number of validators and
@ -95,21 +97,34 @@ func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
ctx: ctx,
cli: httpClient,
endpoint: url,
cache: cache{
nativeHashes: make(map[string]util.Uint160),
},
}
cl.opts = opts
cl.requestF = cl.makeHTTPRequest
return cl, nil
}
// Init sets magic of the network client connected to. This method should be called
// before any transaction-, header- or block-related requests in order to deserialize
// responses properly.
// Init sets magic of the network client connected to and native NEO and GAS
// contracts scripthashes. This method should be called before any transaction-,
// header- or block-related requests in order to deserialize responses properly.
func (c *Client) Init() error {
version, err := c.GetVersion()
if err != nil {
return fmt.Errorf("failed to get network magic: %w", err)
}
c.network = version.Magic
neoContractHash, err := c.GetContractStateByAddressOrName("neo")
if err != nil {
return fmt.Errorf("failed to get NEO contract scripthash: %w", err)
}
c.cache.nativeHashes["neo"] = neoContractHash.ScriptHash()
gasContractHash, err := c.GetContractStateByAddressOrName("gas")
if err != nil {
return fmt.Errorf("failed to get GAS contract scripthash: %w", err)
}
c.cache.nativeHashes["gas"] = gasContractHash.ScriptHash()
c.initDone = true
return nil
}

View file

@ -23,13 +23,6 @@ type TransferTarget struct {
Amount int64
}
var (
// NeoContractHash is a hash of the NEO native contract.
NeoContractHash, _ = util.Uint160DecodeStringBE("25059ecb4878d3a875f91c51ceded330d4575fde")
// GasContractHash is a hash of the GAS native contract.
GasContractHash, _ = util.Uint160DecodeStringBE("bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e66")
)
// NEP5Decimals invokes `decimals` NEP5 method on a specified contract.
func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) {
result, err := c.InvokeFunction(tokenHash, "decimals", []smartcontract.Parameter{}, nil)

View file

@ -4,6 +4,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"strings"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core"
@ -208,10 +209,25 @@ func (c *Client) GetCommittee() (keys.PublicKeys, error) {
return *resp, nil
}
// GetContractState queries contract information, according to the contract script hash.
func (c *Client) GetContractState(hash util.Uint160) (*state.Contract, error) {
// GetContractStateByHash queries contract information, according to the contract script hash.
func (c *Client) GetContractStateByHash(hash util.Uint160) (*state.Contract, error) {
return c.getContractState(hash.StringLE())
}
// GetContractStateByAddressOrName queries contract information, according to the contract address or name.
func (c *Client) GetContractStateByAddressOrName(addressOrName string) (*state.Contract, error) {
return c.getContractState(addressOrName)
}
// GetContractStateByID queries contract information, according to the contract ID.
func (c *Client) GetContractStateByID(id int32) (*state.Contract, error) {
return c.getContractState(id)
}
// getContractState is an internal representation of GetContractStateBy* methods.
func (c *Client) getContractState(param interface{}) (*state.Contract, error) {
var (
params = request.NewRawParams(hash.StringLE())
params = request.NewRawParams(param)
resp = &state.Contract{}
)
if err := c.performRequest("getcontractstate", params, resp); err != nil {
@ -597,3 +613,18 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs
func (c *Client) GetNetwork() netmode.Magic {
return c.network
}
// GetNativeContractHash returns native contract hash by its name. It is not case-sensitive.
func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
lowercasedName := strings.ToLower(name)
hash, ok := c.cache.nativeHashes[lowercasedName]
if ok {
return hash, nil
}
cs, err := c.GetContractStateByAddressOrName(name)
if err != nil {
return util.Uint160{}, err
}
c.cache.nativeHashes[lowercasedName] = cs.ScriptHash()
return cs.ScriptHash(), nil
}

View file

@ -315,13 +315,57 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
},
"getcontractstate": {
{
name: "positive",
name: "positive, by hash",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("1b4357bff5a01bdf2a6581247cf9ed1e24629176")
if err != nil {
panic(err)
}
return c.GetContractState(hash)
return c.GetContractStateByHash(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {
panic(err)
}
m := manifest.NewManifest(hash.Hash160(script))
m.Features = smartcontract.HasStorage
cs := &state.Contract{
ID: 0,
Script: script,
Manifest: *m,
}
_ = cs.ScriptHash()
return cs
},
},
{
name: "positive, by address",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractStateByAddressOrName("NWiu5oejTu925aeL9Hc1LX8SvaJhE23h15")
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {
panic(err)
}
m := manifest.NewManifest(hash.Hash160(script))
m.Features = smartcontract.HasStorage
cs := &state.Contract{
ID: 0,
Script: script,
Manifest: *m,
}
_ = cs.ScriptHash()
return cs
},
},
{
name: "positive, by id",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractStateByID(0)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
@ -645,7 +689,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
},
"invokefunction": {
{
name: "positive",
name: "positive, by scripthash",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("91b83e96f2a7c4fdf0c1688441ec61986c7cae26")
if err != nil {
@ -991,7 +1035,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
{
name: "getcontractstate_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractState(util.Uint160{})
return c.GetContractStateByHash(util.Uint160{})
},
},
{
@ -1178,7 +1222,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
{
name: "getcontractstate_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractState(util.Uint160{})
return c.GetContractStateByHash(util.Uint160{})
},
},
{
@ -1372,13 +1416,7 @@ func initTestServer(t *testing.T, resp string) *httptest.Server {
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
var response string
switch r.Method {
case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
default:
response = resp
}
response := wrapInitResponse(r, resp)
ws.SetWriteDeadline(time.Now().Add(2 * time.Second))
err = ws.WriteMessage(1, []byte(response))
if err != nil {
@ -1393,27 +1431,49 @@ func initTestServer(t *testing.T, resp string) *httptest.Server {
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
requestHandler(t, r.Method, w, resp)
requestHandler(t, r, w, resp)
}))
return srv
}
func requestHandler(t *testing.T, method string, w http.ResponseWriter, resp string) {
func requestHandler(t *testing.T, r *request.In, w http.ResponseWriter, resp string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var response string
switch method {
case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
default:
response = resp
}
response := wrapInitResponse(r, resp)
_, err := w.Write([]byte(response))
if err != nil {
t.Fatalf("Error writing response: %s", err.Error())
}
}
func wrapInitResponse(r *request.In, resp string) string {
var response string
switch r.Method {
case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
case "getcontractstate":
p, err := r.Params()
if err != nil {
response = resp
}
name, err := p.ValueWithType(0, request.StringT).GetString()
if err != nil {
response = resp
}
switch name {
case "neo":
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-1,"script":"DANORU9Ba2d4Cw==","manifest":{"abi":{"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"unclaimedGas","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer"},{"name":"registerCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"unregisterCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"vote","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"getCandidates","offset":0,"parameters":null,"returntype":"Array"},{"name":"getСommittee","offset":0,"parameters":null,"returntype":"Array"},{"name":"getNextBlockValidators","offset":0,"parameters":null,"returntype":"Array"},{"name":"getGasPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setGasPerBlock","offset":0,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Boolean"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"features":{"payable":false,"storage":false},"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf","unclaimedGas","getCandidates","getСommittee","getNextBlockValidators"],"extra":null},"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525"}}`
case "gas":
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-2,"script":"DANHQVNBa2d4Cw==","manifest":{"abi":{"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"features":{"payable":false,"storage":false},"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf"],"extra":null},"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"}}`
default:
response = resp
}
default:
response = resp
}
return response
}
func TestCalculateValidUntilBlock(t *testing.T) {
var (
getBlockCountCalled int
@ -1434,7 +1494,7 @@ func TestCalculateValidUntilBlock(t *testing.T) {
getValidatorsCalled++
response = `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`
}
requestHandler(t, r.Method, w, response)
requestHandler(t, r, w, response)
}))
defer srv.Close()
@ -1462,8 +1522,13 @@ func TestCalculateValidUntilBlock(t *testing.T) {
func TestGetNetwork(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := request.NewIn()
err := r.DecodeData(req.Body)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
// request handler already have `getversion` response wrapper
requestHandler(t, "getversion", w, "")
requestHandler(t, r, w, "")
}))
defer srv.Close()
endpoint := srv.URL

View file

@ -277,7 +277,10 @@ func TestCreateNEP5TransferTx(t *testing.T) {
acc, err := wallet.NewAccountFromWIF(priv.WIF())
require.NoError(t, err)
tx, err := c.CreateNEP5TransferTx(acc, util.Uint160{}, client.GasContractHash, 1000, 0)
gasContractHash, err := c.GetNativeContractHash("gas")
require.NoError(t, err)
tx, err := c.CreateNEP5TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0)
require.NoError(t, err)
require.NoError(t, acc.SignTx(tx))
require.NoError(t, chain.VerifyTx(tx))

View file

@ -765,6 +765,42 @@ func (s *Server) contractIDFromParam(param *request.Param) (int32, *response.Err
return result, nil
}
// getContractScriptHashFromParam returns the contract script hash by hex contract hash, address, id or native contract name.
func (s *Server) contractScriptHashFromParam(param *request.Param) (util.Uint160, *response.Error) {
var result util.Uint160
if param == nil {
return result, response.ErrInvalidParams
}
switch param.Type {
case request.StringT:
var err error
result, err = param.GetUint160FromAddressOrHex()
if err == nil {
return result, nil
}
name, err := param.GetString()
if err != nil {
return result, response.ErrInvalidParams
}
result, err = s.chain.GetNativeContractScriptHash(name)
if err != nil {
return result, response.NewRPCError("Unknown contract: querying by name is supported for native contracts only", "", nil)
}
case request.NumberT:
id, err := param.GetInt()
if err != nil {
return result, response.ErrInvalidParams
}
result, err = s.chain.GetContractScriptHash(int32(id))
if err != nil {
return result, response.NewRPCError("Unknown contract", "", err)
}
default:
return result, response.ErrInvalidParams
}
return result, nil
}
func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) {
id, rErr := s.contractIDFromParam(ps.Value(0))
if rErr == response.ErrUnknown {
@ -828,11 +864,12 @@ func (s *Server) getTransactionHeight(ps request.Params) (interface{}, *response
return height, nil
}
// getContractState returns contract state (contract information, according to the contract script hash).
// getContractState returns contract state (contract information, according to the contract script hash,
// contract id or native contract name).
func (s *Server) getContractState(reqParams request.Params) (interface{}, *response.Error) {
scriptHash, err := reqParams.ValueWithType(0, request.StringT).GetUint160FromHex()
scriptHash, err := s.contractScriptHashFromParam(reqParams.Value(0))
if err != nil {
return nil, response.ErrInvalidParams
return nil, err
}
cs := s.chain.GetContractState(scriptHash)
if cs == nil {
@ -947,9 +984,9 @@ func (s *Server) getCommittee(_ request.Params) (interface{}, *response.Error) {
// invokeFunction implements the `invokeFunction` RPC call.
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *response.Error) {
scriptHash, err := reqParams.ValueWithType(0, request.StringT).GetUint160FromHex()
if err != nil {
return nil, response.ErrInvalidParams
scriptHash, responseErr := s.contractScriptHashFromParam(reqParams.Value(0))
if responseErr != nil {
return nil, responseErr
}
tx := &transaction.Transaction{}
checkWitnessHashesIndex := len(reqParams)

View file

@ -96,7 +96,7 @@ var rpcTestCases = map[string][]rpcTestCase{
},
"getcontractstate": {
{
name: "positive",
name: "positive, by hash",
params: fmt.Sprintf(`["%s"]`, testContractHash),
result: func(e *executor) interface{} { return &state.Contract{} },
check: func(t *testing.T, e *executor, cs interface{}) {
@ -106,10 +106,50 @@ var rpcTestCases = map[string][]rpcTestCase{
},
},
{
name: "negative",
name: "positive, by id",
params: `[0]`,
result: func(e *executor) interface{} { return &state.Contract{} },
check: func(t *testing.T, e *executor, cs interface{}) {
res, ok := cs.(*state.Contract)
require.True(t, ok)
assert.Equal(t, int32(0), res.ID)
},
},
{
name: "positive, native by id",
params: `[-3]`,
result: func(e *executor) interface{} { return &state.Contract{} },
check: func(t *testing.T, e *executor, cs interface{}) {
res, ok := cs.(*state.Contract)
require.True(t, ok)
assert.Equal(t, int32(-3), res.ID)
},
},
{
name: "positive, native by name",
params: `["Policy"]`,
result: func(e *executor) interface{} { return &state.Contract{} },
check: func(t *testing.T, e *executor, cs interface{}) {
res, ok := cs.(*state.Contract)
require.True(t, ok)
assert.Equal(t, int32(-3), res.ID)
},
},
{
name: "negative, bad hash",
params: `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6c3"]`,
fail: true,
},
{
name: "negative, bad ID",
params: `[-8]`,
fail: true,
},
{
name: "negative, bad native name",
params: `["unknown_native"]`,
fail: true,
},
{
name: "no params",
params: `[]`,