Merge pull request #2351 from nspcc-dev/nep11d-tests

*: add NEP11-D tests and refactor NEP11-related CLI and RPC client commands
This commit is contained in:
Roman Khimov 2022-02-10 16:00:05 +03:00 committed by GitHub
commit 32a7e97a87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 846 additions and 209 deletions

View file

@ -40,6 +40,7 @@ const (
testWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG" testWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG"
validatorWallet = "testdata/wallet1_solo.json" validatorWallet = "testdata/wallet1_solo.json"
validatorPass = "one"
) )
var ( var (

View file

@ -2,12 +2,15 @@ package main
import ( import (
"bytes" "bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"path/filepath" "path/filepath"
"strings" "strconv"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
@ -35,6 +38,8 @@ func TestNEP11Import(t *testing.T) {
// deploy NFT NeoNameService contract // deploy NFT NeoNameService contract
nnsContractHash := deployNNSContract(t, e) nnsContractHash := deployNNSContract(t, e)
// deploy NFT-D NeoFS Object contract
nfsContractHash := deployNFSContract(t, e)
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo) neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
require.NoError(t, err) require.NoError(t, err)
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath) e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
@ -47,33 +52,38 @@ func TestNEP11Import(t *testing.T) {
// missing token hash // missing token hash
e.RunWithError(t, args...) e.RunWithError(t, args...)
// good // good: non-divisible
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...) e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
// good: divisible
e.Run(t, append(args, "--token", nfsContractHash.StringLE())...)
// already exists // already exists
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE())...) e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE())...)
// not a NEP-11 token // not a NEP-11 token
e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...) e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...)
checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) {
e.checkNextLine(t, "^Name:\\s*"+name)
e.checkNextLine(t, "^Symbol:\\s*"+symbol)
e.checkNextLine(t, "^Hash:\\s*"+h.StringLE())
e.checkNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals))
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(h))
e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
}
t.Run("Info", func(t *testing.T) { t.Run("Info", func(t *testing.T) {
checkNNSInfo := func(t *testing.T) {
e.checkNextLine(t, "^Name:\\s*NameService")
e.checkNextLine(t, "^Symbol:\\s*NNS")
e.checkNextLine(t, "^Hash:\\s*"+nnsContractHash.StringLE())
e.checkNextLine(t, "^Decimals:\\s*0")
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(nnsContractHash))
e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
}
t.Run("WithToken", func(t *testing.T) { t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info", e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath, "--token", nnsContractHash.StringLE()) "--wallet", walletPath, "--token", nnsContractHash.StringLE())
checkNNSInfo(t) checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
}) })
t.Run("NoToken", func(t *testing.T) { t.Run("NoToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info", e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath) "--wallet", walletPath)
checkNNSInfo(t) checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
e.checkNextLine(t, "")
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
}) })
}) })
@ -83,12 +93,13 @@ func TestNEP11Import(t *testing.T) {
"--wallet", walletPath, "--token", nnsContractHash.StringLE()) "--wallet", walletPath, "--token", nnsContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep11", "info", e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath) "--wallet", walletPath)
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
_, err := e.Out.ReadString('\n') _, err := e.Out.ReadString('\n')
require.Equal(t, err, io.EOF) require.Equal(t, err, io.EOF)
}) })
} }
func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) { func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := newExecutor(t, true) e := newExecutor(t, true)
tmpDir := t.TempDir() tmpDir := t.TempDir()
@ -190,7 +201,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// ownerOf: missing token ID // ownerOf: missing token ID
e.RunWithError(t, cmdOwnerOf...) e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--id", string(tokenID)) cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(tokenID))
// ownerOf: good // ownerOf: good
e.Run(t, cmdOwnerOf...) e.Run(t, cmdOwnerOf...)
@ -209,7 +220,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// tokensOf: good // tokensOf: good
e.Run(t, cmdTokensOf...) e.Run(t, cmdTokensOf...)
require.Equal(t, string(tokenID), e.getNextLine(t)) require.Equal(t, hex.EncodeToString(tokenID), e.getNextLine(t))
// properties: no contract // properties: no contract
cmdProperties := []string{ cmdProperties := []string{
@ -221,12 +232,11 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// properties: no token ID // properties: no token ID
e.RunWithError(t, cmdProperties...) e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--id", string(tokenID)) cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(tokenID))
// properties: ok // properties: ok
e.Run(t, cmdProperties...) e.Run(t, cmdProperties...)
marshalledID := strings.Replace(string(tokenID), "+", "\\u002B", -1) require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.getNextLine(t))
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, marshalledID), e.getNextLine(t))
// tokensOf: good, several tokens // tokensOf: good, several tokens
tokenID1 := mint(t) tokenID1 := mint(t)
@ -236,8 +246,8 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
fst, snd = snd, fst fst, snd = snd, fst
} }
require.Equal(t, string(fst), e.getNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, string(snd), e.getNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// tokens: missing contract hash // tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens", cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
@ -248,8 +258,8 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// tokens: good, several tokens // tokens: good, several tokens
e.Run(t, cmdTokens...) e.Run(t, cmdTokens...)
require.Equal(t, string(fst), e.getNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, string(snd), e.getNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// balance check: several tokens, ok // balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
@ -276,7 +286,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// transfer: good // transfer: good
e.In.WriteString(nftOwnerPass + "\r") e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, append(cmdTransfer, "--id", string(tokenID))...) e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...)
e.checkTxPersisted(t) e.checkTxPersisted(t)
// check balance after transfer // check balance after transfer
@ -292,7 +302,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--to", verifyH.StringLE(), "--to", verifyH.StringLE(),
"--from", nftOwnerAddr, "--from", nftOwnerAddr,
"--token", h.StringLE(), "--token", h.StringLE(),
"--id", string(tokenID1), "--id", hex.EncodeToString(tokenID1),
"--force", "--force",
"string:some_data", "string:some_data",
} }
@ -321,10 +331,267 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
checkBalanceResult(t, nftOwnerAddr, "0") checkBalanceResult(t, nftOwnerAddr, "0")
} }
func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := newExecutor(t, true)
tmpDir := t.TempDir()
// copy wallet to temp dir in order not to overwrite the original file
bytesRead, err := ioutil.ReadFile(validatorWallet)
require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json")
err = ioutil.WriteFile(wall, bytesRead, 0755)
require.NoError(t, err)
// deploy NeoFS Object contract
h := deployNFSContract(t, e)
mint := func(t *testing.T, containerID, objectID util.Uint256) []byte {
// mint 1.00 NFSO token by transferring 10 GAS to NFSO contract
e.In.WriteString(validatorPass + "\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall,
"--to", h.StringLE(),
"--token", "GAS",
"--amount", "10",
"--force",
"--from", validatorAddr,
"--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]",
)
txMint, _ := e.checkTxPersisted(t)
// get NFT ID from AER
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
require.Equal(t, 2, len(aer[0].Events))
nfsoMintEvent := aer[0].Events[1]
require.Equal(t, "Transfer", nfsoMintEvent.Name)
tokenID, err := nfsoMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes()
require.NoError(t, err)
require.NotNil(t, tokenID)
return tokenID
}
container1ID := util.Uint256{1, 2, 3}
object1ID := util.Uint256{4, 5, 6}
token1ID := mint(t, container1ID, object1ID)
container2ID := util.Uint256{7, 8, 9}
object2ID := util.Uint256{10, 11, 12}
token2ID := mint(t, container2ID, object2ID)
// check properties
e.Run(t, "neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--token", h.StringLE(),
"--id", hex.EncodeToString(token1ID))
jProps := e.getNextLine(t)
props := make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"])
e.checkEOF(t)
// check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--address", validatorAddr}
checkBalanceResult := func(t *testing.T, acc string, amount string, id []byte) {
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
if id == nil {
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
} else {
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+", "+hex.EncodeToString(id)+"\\)")
}
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
e.checkEOF(t)
}
// balance check: by symbol, token is not imported
e.RunWithError(t, append(cmdCheckBalance, "--token", "NFSO")...)
// overall NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "2", nil)
// particular NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...)
checkBalanceResult(t, validatorAddr, "1", token2ID)
// import token
e.Run(t, "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall,
"--token", h.StringLE())
// overall balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, validatorAddr, "2", nil)
// particular balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...)
checkBalanceResult(t, validatorAddr, "1", token1ID)
// remove token from wallet
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", wall, "--token", h.StringLE())
// ownerOfD: missing contract hash
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
"--rpc-endpoint", "http://" + e.RPC.Addr,
}
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOfD: missing token ID
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(token1ID))
// ownerOfD: good
e.Run(t, cmdOwnerOf...)
e.checkNextLine(t, validatorAddr)
// tokensOf: missing contract hash
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addr,
}
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", validatorAddr)
// tokensOf: good
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(token1ID), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(token2ID), e.getNextLine(t))
e.checkEOF(t)
// properties: no contract
cmdProperties := []string{
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addr,
}
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID))
// properties: ok
e.Run(t, cmdProperties...)
jProps = e.getNextLine(t)
props = make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"])
e.checkEOF(t)
// tokensOf: good, several tokens
e.Run(t, cmdTokensOf...)
fst, snd := token1ID, token2ID
if bytes.Compare(token1ID, token2ID) == 1 {
fst, snd = snd, fst
}
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addr,
}
e.RunWithError(t, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: good, several tokens
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "2", nil)
cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--to", nftOwnerAddr,
"--from", validatorAddr,
"--force",
}
// transfer: unimported token with symbol id specified
e.In.WriteString(validatorPass + "\r")
e.RunWithError(t, append(cmdTransfer,
"--token", "NFSO")...)
cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
// transfer: no id specified
e.In.WriteString(validatorPass + "\r")
e.RunWithError(t, cmdTransfer...)
// transfer: good
e.In.WriteString(validatorPass + "\r")
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...)
e.checkTxPersisted(t)
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "1", nil) // only token2ID expected to be on the balance
// transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--to", verifyH.StringLE(),
"--from", validatorAddr,
"--token", h.StringLE(),
"--id", hex.EncodeToString(token2ID),
"--amount", "0.25",
"--force",
"string:some_data",
}
e.In.WriteString(validatorPass + "\r")
e.Run(t, cmdTransfer...)
tx, _ := e.checkTxPersisted(t)
// check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 2, len(aer[0].Events))
validatorHash, err := address.StringToUint160(validatorAddr)
require.NoError(t, err)
require.Equal(t, state.NotificationEvent{
ScriptHash: verifyH,
Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(validatorHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(25)),
stackitem.NewByteArray(token2ID),
stackitem.NewByteArray([]byte("some_data")),
}),
}, aer[0].Events[1])
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "0.75", nil)
}
func deployNFSContract(t *testing.T, e *executor) util.Uint160 {
return deployContract(t, e, "../examples/nft-d/nft.go", "../examples/nft-d/nft.yml", validatorWallet, validatorAddr, validatorPass)
}
func deployNFTContract(t *testing.T, e *executor) util.Uint160 { func deployNFTContract(t *testing.T, e *executor) util.Uint160 {
return deployContract(t, e, "../examples/nft-nd/nft.go", "../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass) return deployContract(t, e, "../examples/nft-nd/nft.go", "../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass)
} }
func deployNNSContract(t *testing.T, e *executor) util.Uint160 { func deployNNSContract(t *testing.T, e *executor) util.Uint160 {
return deployContract(t, e, "../examples/nft-nd-nns/", "../examples/nft-nd-nns/nns.yml", validatorWallet, validatorAddr, "one") return deployContract(t, e, "../examples/nft-nd-nns/", "../examples/nft-nd-nns/nns.yml", validatorWallet, validatorAddr, validatorPass)
} }

View file

@ -1,6 +1,7 @@
package wallet package wallet
import ( import (
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -31,7 +32,7 @@ func newNEP11Commands() []cli.Command {
} }
tokenID := cli.StringFlag{ tokenID := cli.StringFlag{
Name: "id", Name: "id",
Usage: "Token ID", Usage: "Hex-encoded token ID",
} }
balanceFlags := make([]cli.Flag, len(baseBalanceFlags)) balanceFlags := make([]cli.Flag, len(baseBalanceFlags))
@ -107,7 +108,17 @@ func newNEP11Commands() []cli.Command {
Name: "ownerOf", Name: "ownerOf",
Usage: "print owner of non-divisible NEP-11 token with the specified ID", Usage: "print owner of non-divisible NEP-11 token with the specified ID",
UsageText: "ownerOf --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>", UsageText: "ownerOf --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>",
Action: printNEP11Owner, Action: printNEP11NDOwner,
Flags: append([]cli.Flag{
tokenAddressFlag,
tokenID,
}, options.RPC...),
},
{
Name: "ownerOfD",
Usage: "print set of owners of divisible NEP-11 token with the specified ID",
UsageText: "ownerOfD --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>",
Action: printNEP11DOwner,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
tokenAddressFlag, tokenAddressFlag,
tokenID, tokenID,
@ -196,6 +207,10 @@ func getNEP11Balance(ctx *cli.Context) error {
} }
tokenID := ctx.String("id") tokenID := ctx.String("id")
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
for k, acc := range accounts { for k, acc := range accounts {
addrHash, err := address.StringToUint160(acc.Address) addrHash, err := address.StringToUint160(acc.Address)
if err != nil { if err != nil {
@ -208,10 +223,10 @@ func getNEP11Balance(ctx *cli.Context) error {
fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address) fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address)
var amount int64 var amount int64
if tokenID == "" { if len(tokenIDBytes) == 0 {
amount, err = c.NEP11BalanceOf(token.Hash, addrHash) amount, err = c.NEP11BalanceOf(token.Hash, addrHash)
} else { } else {
amount, err = c.NEP11DBalanceOf(token.Hash, addrHash, tokenID) amount, err = c.NEP11DBalanceOf(token.Hash, addrHash, tokenIDBytes)
} }
if err != nil { if err != nil {
continue continue
@ -220,7 +235,7 @@ func getNEP11Balance(ctx *cli.Context) error {
format := "%s: %s (%s)\n" format := "%s: %s (%s)\n"
formatArgs := []interface{}{token.Symbol, token.Name, token.Hash.StringLE()} formatArgs := []interface{}{token.Symbol, token.Name, token.Hash.StringLE()}
if tokenID != "" { if len(tokenIDBytes) != 0 {
format = "%s: %s (%s, %s)\n" format = "%s: %s (%s, %s)\n"
formatArgs = append(formatArgs, tokenID) formatArgs = append(formatArgs, tokenID)
} }
@ -234,7 +249,7 @@ func transferNEP11(ctx *cli.Context) error {
return transferNEP(ctx, manifest.NEP11StandardName) return transferNEP(ctx, manifest.NEP11StandardName)
} }
func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, token, to util.Uint160, tokenID string, amount *big.Int, data interface{}, cosigners []client.SignerAccount) error { func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, token, to util.Uint160, tokenID []byte, amount *big.Int, data interface{}, cosigners []client.SignerAccount) error {
gas := flags.Fixed8FromContext(ctx, "gas") gas := flags.Fixed8FromContext(ctx, "gas")
sysgas := flags.Fixed8FromContext(ctx, "sysgas") sysgas := flags.Fixed8FromContext(ctx, "sysgas")
@ -279,7 +294,15 @@ func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Ac
return nil return nil
} }
func printNEP11Owner(ctx *cli.Context) error { func printNEP11NDOwner(ctx *cli.Context) error {
return printNEP11Owner(ctx, false)
}
func printNEP11DOwner(ctx *cli.Context) error {
return printNEP11Owner(ctx, true)
}
func printNEP11Owner(ctx *cli.Context, divisible bool) error {
var err error var err error
tokenHash := ctx.Generic("token").(*flags.Address) tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet { if !tokenHash.IsSet {
@ -290,6 +313,10 @@ func printNEP11Owner(ctx *cli.Context) error {
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.NewExitError(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
@ -299,12 +326,22 @@ func printNEP11Owner(ctx *cli.Context) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
result, err := c.NEP11NDOwnerOf(tokenHash.Uint160(), tokenID) if divisible {
if err != nil { result, err := c.NEP11DOwnerOf(tokenHash.Uint160(), tokenIDBytes)
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `ownerOf` method: %s", err.Error()), 1) if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1)
}
for _, h := range result {
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(h))
}
} else {
result, err := c.NEP11NDOwnerOf(tokenHash.Uint160(), tokenIDBytes)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1)
}
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(result))
} }
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(result))
return nil return nil
} }
@ -334,7 +371,7 @@ func printNEP11TokensOf(ctx *cli.Context) error {
} }
for i := range result { for i := range result {
fmt.Fprintln(ctx.App.Writer, result[i]) fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result[i]))
} }
return nil return nil
} }
@ -360,7 +397,7 @@ func printNEP11Tokens(ctx *cli.Context) error {
} }
for i := range result { for i := range result {
fmt.Fprintln(ctx.App.Writer, result[i]) fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result[i]))
} }
return nil return nil
} }
@ -376,6 +413,10 @@ func printNEP11Properties(ctx *cli.Context) error {
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.NewExitError(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
@ -385,7 +426,7 @@ func printNEP11Properties(ctx *cli.Context) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
result, err := c.NEP11Properties(tokenHash.Uint160(), tokenID) result, err := c.NEP11Properties(tokenHash.Uint160(), tokenIDBytes)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1) return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1)
} }

View file

@ -1,6 +1,7 @@
package wallet package wallet
import ( import (
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -618,14 +619,18 @@ func transferNEP(ctx *cli.Context, standard string) error {
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.NewExitError(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid token ID: %w", err), 1)
}
if amountArg == "" { if amountArg == "" {
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, nil, data, cosignersAccounts) return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenIDBytes, nil, data, cosignersAccounts)
} }
amount, err := fixedn.FromString(amountArg, int(token.Decimals)) amount, err := fixedn.FromString(amountArg, int(token.Decimals))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1) return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
} }
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, amount, data, cosignersAccounts) return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenIDBytes, amount, data, cosignersAccounts)
default: default:
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1) return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1)
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
@ -30,7 +31,7 @@ const (
balancePrefix = "b" balancePrefix = "b"
// tokenOwnerPrefix contains map from [token id + owner] to token's owner. // tokenOwnerPrefix contains map from [token id + owner] to token's owner.
tokenOwnerPrefix = "t" tokenOwnerPrefix = "t"
// tokenPrefix contains map from token id to empty array. // tokenPrefix contains map from token id to its properties (serialised containerID + objectID).
tokenPrefix = "i" tokenPrefix = "i"
) )
@ -231,12 +232,13 @@ func Properties(id []byte) map[string]string {
if !isTokenValid(ctx, id) { if !isTokenValid(ctx, id) {
panic("unknown token") panic("unknown token")
} }
t := std.Deserialize(id).(ObjectIdentifier) key := mkTokenKey(id)
props := storage.Get(ctx, key).([]byte)
t := std.Deserialize(props).(ObjectIdentifier)
result := map[string]string{ result := map[string]string{
"name": "NFSO " + string(id), "name": "NeoFS Object " + std.Base64Encode(id), // Not a hex for contract simplicity.
"fullName": "NeoFS Object", "containerID": std.Base64Encode(t.ContainerID),
"containerID": string(t.ContainerID), "objectID": std.Base64Encode(t.ObjectID),
"objectID": string(t.ObjectID),
} }
return result return result
} }
@ -360,11 +362,11 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
panic("invalid 'data'") panic("invalid 'data'")
} }
containerID := tokenInfo[0].([]byte) containerID := tokenInfo[0].([]byte)
if len(containerID) != 32 { if len(containerID) != interop.Hash256Len {
panic("invalid container ID") panic("invalid container ID")
} }
objectID := tokenInfo[1].([]byte) objectID := tokenInfo[1].([]byte)
if len(objectID) != 32 { if len(objectID) != interop.Hash256Len {
panic("invalid object ID") panic("invalid object ID")
} }
@ -372,18 +374,19 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
ContainerID: containerID, ContainerID: containerID,
ObjectID: objectID, ObjectID: objectID,
} }
id := std.Serialize(t) props := std.Serialize(t)
id := crypto.Ripemd160(props)
var ctx = storage.GetContext() var ctx = storage.GetContext()
if isTokenValid(ctx, id) { if isTokenValid(ctx, id) {
panic("NFSO for the specified address is already minted") panic("NFSO for the specified object is already minted")
} }
key := mkTokenKey(id) key := mkTokenKey(id)
storage.Put(ctx, key, []byte{}) storage.Put(ctx, key, props)
total := totalSupply(ctx) total := totalSupply(ctx)
addOwner(ctx, from, id) addOwner(ctx, id, from)
addToBalance(ctx, from, id, multiplier) addToBalance(ctx, from, id, multiplier)
total++ total++

View file

@ -1,4 +1,4 @@
name: "NFSO NFT" name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/ sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"] supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]

View file

@ -273,7 +273,7 @@ func Properties(id []byte) map[string]string {
panic("unknown token") panic("unknown token")
} }
result := map[string]string{ result := map[string]string{
"name": "HASHY " + string(id), "name": "HASHY " + std.Base64Encode(id), // Not a hex for contract simplicity.
} }
return result return result
} }

View file

@ -68,6 +68,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
return nil, util.Uint160{}, nil, fmt.Errorf("failed to parse configuration: %w", err) return nil, util.Uint160{}, nil, fmt.Errorf("failed to parse configuration: %w", err)
} }
o.Name = conf.Name o.Name = conf.Name
o.SourceURL = conf.SourceURL
o.ContractEvents = conf.Events o.ContractEvents = conf.Events
o.ContractSupportedStandards = conf.SupportedStandards o.ContractSupportedStandards = conf.SupportedStandards
o.Permissions = make([]manifest.Permission, len(conf.Permissions)) o.Permissions = make([]manifest.Permission, len(conf.Permissions))
@ -75,6 +76,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
o.Permissions[i] = manifest.Permission(conf.Permissions[i]) o.Permissions[i] = manifest.Permission(conf.Permissions[i])
} }
o.SafeMethods = conf.SafeMethods o.SafeMethods = conf.SafeMethods
o.Overloads = conf.Overloads
} }
ne, di, err := compiler.CompileWithOptions(name, r, o) ne, di, err := compiler.CompileWithOptions(name, r, o)

View file

@ -279,16 +279,34 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
priv0 := testchain.PrivateKeyByID(0) priv0 := testchain.PrivateKeyByID(0)
priv0ScriptHash := priv0.GetScriptHash() priv0ScriptHash := priv0.GetScriptHash()
priv1 := testchain.PrivateKeyByID(1)
priv1ScriptHash := priv1.GetScriptHash()
acc0 := wallet.NewAccountFromPrivateKey(priv0)
acc1 := wallet.NewAccountFromPrivateKey(priv1)
deployContractFromPriv0 := func(t *testing.T, path, contractName string, configPath *string, expectedID int32) (util.Uint256, util.Uint256, util.Uint160) {
txDeploy, _ := newDeployTx(t, bc, priv0ScriptHash, path, contractName, configPath)
txDeploy.Nonce = getNextNonce()
txDeploy.ValidUntilBlock = validUntilBlock
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy))
b := bc.newBlock(txDeploy)
require.NoError(t, bc.AddBlock(b)) // block #11
checkTxHalt(t, bc, txDeploy.Hash())
sh, err := bc.GetContractScriptHash(expectedID)
require.NoError(t, err)
return b.Hash(), txDeploy.Hash(), sh
}
require.Equal(t, big.NewInt(5000_0000), bc.GetUtilityTokenBalance(priv0ScriptHash)) // gas bounty require.Equal(t, big.NewInt(5000_0000), bc.GetUtilityTokenBalance(priv0ScriptHash)) // gas bounty
// Move some NEO to one simple account.
// Block #1: move 1000 GAS and neoAmount NEO to priv0.
txMoveNeo, err := testchain.NewTransferFromOwner(bc, neoHash, priv0ScriptHash, neoAmount, getNextNonce(), validUntilBlock) txMoveNeo, err := testchain.NewTransferFromOwner(bc, neoHash, priv0ScriptHash, neoAmount, getNextNonce(), validUntilBlock)
require.NoError(t, err) require.NoError(t, err)
// Move some GAS to one simple account. // Move some GAS to one simple account.
txMoveGas, err := testchain.NewTransferFromOwner(bc, gasHash, priv0ScriptHash, int64(fixedn.Fixed8FromInt64(1000)), txMoveGas, err := testchain.NewTransferFromOwner(bc, gasHash, priv0ScriptHash, int64(fixedn.Fixed8FromInt64(1000)),
getNextNonce(), validUntilBlock) getNextNonce(), validUntilBlock)
require.NoError(t, err) require.NoError(t, err)
b := bc.newBlock(txMoveNeo, txMoveGas) b := bc.newBlock(txMoveNeo, txMoveGas)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, txMoveGas.Hash()) checkTxHalt(t, bc, txMoveGas.Hash())
@ -322,25 +340,15 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
b.Header.EncodeBinary(buf.BinWriter) b.Header.EncodeBinary(buf.BinWriter)
t.Logf("header: %s", hex.EncodeToString(buf.Bytes())) t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
acc0 := wallet.NewAccountFromPrivateKey(priv0) // Block #2: deploy test_contract.
// Push some contract into the chain.
cfgPath := prefix + "test_contract.yml" cfgPath := prefix + "test_contract.yml"
txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl", &cfgPath) block2H, txDeployH, cHash := deployContractFromPriv0(t, prefix+"test_contract.go", "Rubl", &cfgPath, 1)
txDeploy.Nonce = getNextNonce() t.Logf("txDeploy: %s", txDeployH.StringLE())
txDeploy.ValidUntilBlock = validUntilBlock t.Logf("Block2 hash: %s", block2H.StringLE())
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy))
b = bc.newBlock(txDeploy)
require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, txDeploy.Hash())
t.Logf("txDeploy: %s", txDeploy.Hash().StringLE())
t.Logf("Block2 hash: %s", b.Hash().StringLE())
// Now invoke this contract. // Block #3: invoke `putValue` method on the test_contract.
script := io.NewBufBinWriter() script := io.NewBufBinWriter()
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "testvalue") emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "testvalue")
txInv := transaction.New(script.Bytes(), 1*native.GASFactor) txInv := transaction.New(script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce() txInv.Nonce = getNextNonce()
txInv.ValidUntilBlock = validUntilBlock txInv.ValidUntilBlock = validUntilBlock
@ -352,8 +360,8 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
checkTxHalt(t, bc, txInv.Hash()) checkTxHalt(t, bc, txInv.Hash())
t.Logf("txInv: %s", txInv.Hash().StringLE()) t.Logf("txInv: %s", txInv.Hash().StringLE())
priv1 := testchain.PrivateKeyByID(1) // Block #4: transfer 0.0000_1 NEO from priv0 to priv1.
txNeo0to1 := newNEP17Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000) txNeo0to1 := newNEP17Transfer(neoHash, priv0ScriptHash, priv1ScriptHash, 1000)
txNeo0to1.Nonce = getNextNonce() txNeo0to1.Nonce = getNextNonce()
txNeo0to1.ValidUntilBlock = validUntilBlock txNeo0to1.ValidUntilBlock = validUntilBlock
txNeo0to1.Signers = []transaction.Signer{ txNeo0to1.Signers = []transaction.Signer{
@ -370,6 +378,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, txNeo0to1.Hash()) checkTxHalt(t, bc, txNeo0to1.Hash())
// Block #5: initialize rubles contract and transfer 1000 rubles from the contract to priv0.
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, cHash, "init", callflag.All) emit.AppCall(w.BinWriter, cHash, "init", callflag.All)
initTx := transaction.New(w.Bytes(), 1*native.GASFactor) initTx := transaction.New(w.Bytes(), 1*native.GASFactor)
@ -378,7 +387,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}} initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, initTx, acc0)) require.NoError(t, addNetworkFee(bc, initTx, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), initTx)) require.NoError(t, acc0.SignTx(testchain.Network(), initTx))
transferTx := newNEP17Transfer(cHash, cHash, priv0.GetScriptHash(), 1000) transferTx := newNEP17Transfer(cHash, cHash, priv0ScriptHash, 1000)
transferTx.Nonce = getNextNonce() transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock transferTx.ValidUntilBlock = validUntilBlock
transferTx.Signers = []transaction.Signer{ transferTx.Signers = []transaction.Signer{
@ -392,14 +401,14 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, addNetworkFee(bc, transferTx, acc0)) require.NoError(t, addNetworkFee(bc, transferTx, acc0))
transferTx.SystemFee += 1000000 transferTx.SystemFee += 1000000
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx)) require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
b = bc.newBlock(initTx, transferTx) b = bc.newBlock(initTx, transferTx)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, initTx.Hash()) checkTxHalt(t, bc, initTx.Hash())
checkTxHalt(t, bc, transferTx.Hash()) checkTxHalt(t, bc, transferTx.Hash())
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE()) t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1.GetScriptHash(), 123) // Block #6: transfer 123 rubles from priv0 to priv1
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1ScriptHash, 123)
transferTx.Nonce = getNextNonce() transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock transferTx.ValidUntilBlock = validUntilBlock
transferTx.Signers = []transaction.Signer{ transferTx.Signers = []transaction.Signer{
@ -413,24 +422,16 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, addNetworkFee(bc, transferTx, acc0)) require.NoError(t, addNetworkFee(bc, transferTx, acc0))
transferTx.SystemFee += 1000000 transferTx.SystemFee += 1000000
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx)) require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
b = bc.newBlock(transferTx) b = bc.newBlock(transferTx)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, transferTx.Hash()) checkTxHalt(t, bc, transferTx.Hash())
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE()) t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
// Push verification contract into the chain. // Block #7: push verification contract into the chain.
verifyPath := filepath.Join(prefix, "verify", "verification_contract.go") verifyPath := filepath.Join(prefix, "verify", "verification_contract.go")
txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, verifyPath, "Verify", nil) _, _, _ = deployContractFromPriv0(t, verifyPath, "Verify", nil, 2)
txDeploy2.Nonce = getNextNonce()
txDeploy2.ValidUntilBlock = validUntilBlock
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy2))
b = bc.newBlock(txDeploy2)
require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, txDeploy2.Hash())
// Deposit some GAS to notary contract for priv0 // Block #8: deposit some GAS to notary contract for priv0.
transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000)) transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000))
transferTx.Nonce = getNextNonce() transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock transferTx.ValidUntilBlock = validUntilBlock
@ -443,68 +444,59 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, addNetworkFee(bc, transferTx, acc0)) require.NoError(t, addNetworkFee(bc, transferTx, acc0))
transferTx.SystemFee += 10_0000 transferTx.SystemFee += 10_0000
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx)) require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
b = bc.newBlock(transferTx) b = bc.newBlock(transferTx)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, transferTx.Hash()) checkTxHalt(t, bc, transferTx.Hash())
t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE()) t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE())
// Designate new Notary node // Block #9: designate new Notary node.
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json")) ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt)) require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
bc.setNodesByRole(t, true, noderoles.P2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()}) bc.setNodesByRole(t, true, noderoles.P2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()})
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes())) t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
// Push verification contract with arguments into the chain. // Block #10: push verification contract with arguments into the chain.
verifyPath = filepath.Join(prefix, "verify_args", "verification_with_args_contract.go") verifyPath = filepath.Join(prefix, "verify_args", "verification_with_args_contract.go")
txDeploy3, _ := newDeployTx(t, bc, priv0ScriptHash, verifyPath, "VerifyWithArgs", nil) _, _, _ = deployContractFromPriv0(t, verifyPath, "VerifyWithArgs", nil, 3) // block #10
txDeploy3.Nonce = getNextNonce()
txDeploy3.ValidUntilBlock = validUntilBlock
require.NoError(t, addNetworkFee(bc, txDeploy3, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy3))
b = bc.newBlock(txDeploy3)
require.NoError(t, bc.AddBlock(b)) // block #10
checkTxHalt(t, bc, txDeploy3.Hash())
// Push NameService contract into the chain. // Block #11: push NameService contract into the chain.
nsPath := examplesPrefix + "nft-nd-nns/" nsPath := examplesPrefix + "nft-nd-nns/"
nsConfigPath := nsPath + "nns.yml" nsConfigPath := nsPath + "nns.yml"
txDeploy4, _ := newDeployTx(t, bc, priv0ScriptHash, nsPath, nsPath, &nsConfigPath) _, _, nsHash := deployContractFromPriv0(t, nsPath, nsPath, &nsConfigPath, 4) // block #11
txDeploy4.Nonce = getNextNonce()
txDeploy4.ValidUntilBlock = validUntilBlock
require.NoError(t, addNetworkFee(bc, txDeploy4, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy4))
b = bc.newBlock(txDeploy4)
require.NoError(t, bc.AddBlock(b)) // block #11
checkTxHalt(t, bc, txDeploy4.Hash())
nsHash, err := bc.GetContractScriptHash(4)
require.NoError(t, err)
t.Logf("contract (%s): \n\tHash: %s\n", nsPath, nsHash.StringLE())
// register `neo.com` with A record type and priv0 owner via NS // Block #12: transfer funds to committee for futher NS record registration.
transferFundsToCommittee(t, bc) // block #12 transferFundsToCommittee(t, bc) // block #12
// Block #13: add `.com` root to NNS.
res, err := invokeContractMethodGeneric(bc, -1, res, err := invokeContractMethodGeneric(bc, -1,
nsHash, "addRoot", true, "com") // block #13 nsHash, "addRoot", true, "com") // block #13
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.Null{}) checkResult(t, res, stackitem.Null{})
// Block #14: register `neo.com` via NNS.
res, err = invokeContractMethodGeneric(bc, -1, res, err = invokeContractMethodGeneric(bc, -1,
nsHash, "register", acc0, "neo.com", priv0ScriptHash) // block #14 nsHash, "register", acc0, "neo.com", priv0ScriptHash) // block #14
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.NewBool(true)) checkResult(t, res, stackitem.NewBool(true))
require.Equal(t, 1, len(res.Events)) // transfer
tokenID, err := res.Events[0].Item.Value().([]stackitem.Item)[3].TryBytes()
require.NoError(t, err)
t.Logf("NNS token #1 ID (hex): %s", hex.EncodeToString(tokenID))
// Block #15: set A record type with priv0 owner via NNS.
res, err = invokeContractMethodGeneric(bc, -1, nsHash, res, err = invokeContractMethodGeneric(bc, -1, nsHash,
"setRecord", acc0, "neo.com", int64(nns.A), "1.2.3.4") // block #15 "setRecord", acc0, "neo.com", int64(nns.A), "1.2.3.4") // block #15
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.Null{}) checkResult(t, res, stackitem.Null{})
// Invoke `test_contract.go`: put new value with the same key to check `getstate` RPC call // Block #16: invoke `test_contract.go`: put new value with the same key to check `getstate` RPC call
script.Reset() script.Reset()
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "newtestvalue") emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "newtestvalue")
// Invoke `test_contract.go`: put values to check `findstates` RPC call // Invoke `test_contract.go`: put values to check `findstates` RPC call
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa", "v1") emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa", "v1")
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa10", "v2") emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa10", "v2")
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa50", "v3") emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa50", "v3")
txInv = transaction.New(script.Bytes(), 1*native.GASFactor) txInv = transaction.New(script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce() txInv.Nonce = getNextNonce()
txInv.ValidUntilBlock = validUntilBlock txInv.ValidUntilBlock = validUntilBlock
@ -512,9 +504,75 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, addNetworkFee(bc, txInv, acc0)) require.NoError(t, addNetworkFee(bc, txInv, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txInv)) require.NoError(t, acc0.SignTx(testchain.Network(), txInv))
b = bc.newBlock(txInv) b = bc.newBlock(txInv)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b)) // block #16
checkTxHalt(t, bc, txInv.Hash()) checkTxHalt(t, bc, txInv.Hash())
// Block #17: deploy NeoFS Object contract (NEP11-Divisible).
nfsPath := examplesPrefix + "nft-d/"
nfsConfigPath := nfsPath + "nft.yml"
_, _, nfsHash := deployContractFromPriv0(t, nfsPath, nfsPath, &nfsConfigPath, 5) // block #17
// Block #18: mint 1.00 NFSO token by transferring 10 GAS to NFSO contract.
containerID := util.Uint256{1, 2, 3}
objectID := util.Uint256{4, 5, 6}
txGas0toNFS := newNEP17Transfer(gasHash, priv0ScriptHash, nfsHash, 10_0000_0000, containerID.BytesBE(), objectID.BytesBE())
txGas0toNFS.SystemFee += 4000_0000
txGas0toNFS.Nonce = getNextNonce()
txGas0toNFS.ValidUntilBlock = validUntilBlock
txGas0toNFS.Signers = []transaction.Signer{
{
Account: priv0ScriptHash,
Scopes: transaction.CalledByEntry,
},
}
require.NoError(t, addNetworkFee(bc, txGas0toNFS, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txGas0toNFS))
b = bc.newBlock(txGas0toNFS)
require.NoError(t, bc.AddBlock(b)) // block #18
checkTxHalt(t, bc, txGas0toNFS.Hash())
aer, _ := bc.GetAppExecResults(txGas0toNFS.Hash(), trigger.Application)
require.Equal(t, 2, len(aer[0].Events)) // GAS transfer + NFSO transfer
tokenID, err = aer[0].Events[1].Item.Value().([]stackitem.Item)[3].TryBytes()
require.NoError(t, err)
t.Logf("NFSO token #1 ID (hex): %s", hex.EncodeToString(tokenID))
// Block #19: transfer 0.25 NFSO from priv0 to priv1.
script.Reset()
emit.AppCall(script.BinWriter, nfsHash, "transfer", callflag.All, priv0ScriptHash, priv1ScriptHash, 25, tokenID, nil)
emit.Opcodes(script.BinWriter, opcode.ASSERT)
require.NoError(t, script.Err)
txNFS0to1 := transaction.New(script.Bytes(), 1*native.GASFactor)
txNFS0to1.Nonce = getNextNonce()
txNFS0to1.ValidUntilBlock = validUntilBlock
txNFS0to1.Signers = []transaction.Signer{{Account: priv0ScriptHash, Scopes: transaction.CalledByEntry}}
require.NoError(t, addNetworkFee(bc, txNFS0to1, acc0))
require.NoError(t, acc0.SignTx(testchain.Network(), txNFS0to1))
b = bc.newBlock(txNFS0to1)
require.NoError(t, bc.AddBlock(b)) // block #19
checkTxHalt(t, bc, txNFS0to1.Hash())
// Block #20: transfer 1000 GAS to priv1.
txMoveGas, err = testchain.NewTransferFromOwner(bc, gasHash, priv1ScriptHash, int64(fixedn.Fixed8FromInt64(1000)),
getNextNonce(), validUntilBlock)
require.NoError(t, err)
require.NoError(t, bc.AddBlock(bc.newBlock(txMoveGas)))
checkTxHalt(t, bc, txMoveGas.Hash()) // block #20
// Block #21: transfer 0.05 NFSO from priv1 back to priv0.
script.Reset()
emit.AppCall(script.BinWriter, nfsHash, "transfer", callflag.All, priv1ScriptHash, priv0.GetScriptHash(), 5, tokenID, nil)
emit.Opcodes(script.BinWriter, opcode.ASSERT)
require.NoError(t, script.Err)
txNFS1to0 := transaction.New(script.Bytes(), 1*native.GASFactor)
txNFS1to0.Nonce = getNextNonce()
txNFS1to0.ValidUntilBlock = validUntilBlock
txNFS1to0.Signers = []transaction.Signer{{Account: priv1ScriptHash, Scopes: transaction.CalledByEntry}}
require.NoError(t, addNetworkFee(bc, txNFS1to0, acc0))
require.NoError(t, acc1.SignTx(testchain.Network(), txNFS1to0))
b = bc.newBlock(txNFS1to0)
require.NoError(t, bc.AddBlock(b)) // block #21
checkTxHalt(t, bc, txNFS1to0.Hash())
// Compile contract to test `invokescript` RPC call // Compile contract to test `invokescript` RPC call
invokePath := filepath.Join(prefix, "invoke", "invokescript_contract.go") invokePath := filepath.Join(prefix, "invoke", "invokescript_contract.go")
invokeCfg := filepath.Join(prefix, "invoke", "invoke.yml") invokeCfg := filepath.Join(prefix, "invoke", "invoke.yml")

View file

@ -280,7 +280,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
var ( var (
stateSyncInterval = 4 stateSyncInterval = 4
maxTraceable uint32 = 6 maxTraceable uint32 = 6
stateSyncPoint = 16 stateSyncPoint = 20
) )
spoutCfg := func(c *config.Config) { spoutCfg := func(c *config.Config) {
c.ProtocolConfiguration.StateRootInHeader = true c.ProtocolConfiguration.StateRootInHeader = true
@ -291,10 +291,9 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
bcSpout := newTestChainWithCustomCfg(t, spoutCfg) bcSpout := newTestChainWithCustomCfg(t, spoutCfg)
initBasicChain(t, bcSpout) initBasicChain(t, bcSpout)
// make spout chain higher that latest state sync point // make spout chain higher that latest state sync point (add several blocks up to stateSyncPoint+2)
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock())) require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock())) require.Equal(t, stateSyncPoint+2, int(bcSpout.BlockHeight()))
require.Equal(t, uint32(stateSyncPoint+2), bcSpout.BlockHeight())
boltCfg := func(c *config.Config) { boltCfg := func(c *config.Config) {
spoutCfg(c) spoutCfg(c)

View file

@ -110,6 +110,12 @@ func topIterableFromStack(st []stackitem.Item, resultItemType interface{}) ([]in
result := make([]interface{}, len(iter.Values)) result := make([]interface{}, len(iter.Values))
for i := range iter.Values { for i := range iter.Values {
switch resultItemType.(type) { switch resultItemType.(type) {
case []byte:
bytes, err := iter.Values[i].TryBytes()
if err != nil {
return nil, fmt.Errorf("failed to deserialize []byte from stackitem #%d: %w", i, err)
}
result[i] = bytes
case string: case string:
bytes, err := iter.Values[i].TryBytes() bytes, err := iter.Values[i].TryBytes()
if err != nil { if err != nil {

View file

@ -51,15 +51,15 @@ func (c *Client) nepTotalSupply(tokenHash util.Uint160) (int64, error) {
} }
// nepBalanceOf invokes `balanceOf` NEP* method on a specified contract. // nepBalanceOf invokes `balanceOf` NEP* method on a specified contract.
func (c *Client) nepBalanceOf(tokenHash, acc util.Uint160, tokenID *string) (int64, error) { func (c *Client) nepBalanceOf(tokenHash, acc util.Uint160, tokenID []byte) (int64, error) {
params := []smartcontract.Parameter{{ params := []smartcontract.Parameter{{
Type: smartcontract.Hash160Type, Type: smartcontract.Hash160Type,
Value: acc, Value: acc,
}} }}
if tokenID != nil { if tokenID != nil {
params = append(params, smartcontract.Parameter{ params = append(params, smartcontract.Parameter{
Type: smartcontract.StringType, Type: smartcontract.ByteArrayType,
Value: *tokenID, Value: tokenID,
}) })
} }
result, err := c.InvokeFunction(tokenHash, "balanceOf", params, nil) result, err := c.InvokeFunction(tokenHash, "balanceOf", params, nil)

View file

@ -86,7 +86,7 @@ func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint1
} }
// NEP11TokensOf returns an array of token IDs for the specified owner of the specified NFT token. // NEP11TokensOf returns an array of token IDs for the specified owner of the specified NFT token.
func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) ([]string, error) { func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) ([][]byte, error) {
result, err := c.InvokeFunction(tokenHash, "tokensOf", []smartcontract.Parameter{ result, err := c.InvokeFunction(tokenHash, "tokensOf", []smartcontract.Parameter{
{ {
Type: smartcontract.Hash160Type, Type: smartcontract.Hash160Type,
@ -101,13 +101,13 @@ func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) ([]st
return nil, err return nil, err
} }
arr, err := topIterableFromStack(result.Stack, string("")) arr, err := topIterableFromStack(result.Stack, []byte{})
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get token IDs from stack: %w", err) return nil, fmt.Errorf("failed to get token IDs from stack: %w", err)
} }
ids := make([]string, len(arr)) ids := make([][]byte, len(arr))
for i := range ids { for i := range ids {
ids[i] = arr[i].(string) ids[i] = arr[i].([]byte)
} }
return ids, nil return ids, nil
} }
@ -116,10 +116,10 @@ func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) ([]st
// NEP11NDOwnerOf invokes `ownerOf` non-devisible NEP-11 method with the // NEP11NDOwnerOf invokes `ownerOf` non-devisible NEP-11 method with the
// specified token ID on a specified contract. // specified token ID on a specified contract.
func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID string) (util.Uint160, error) { func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID []byte) (util.Uint160, error) {
result, err := c.InvokeFunction(tokenHash, "ownerOf", []smartcontract.Parameter{ result, err := c.InvokeFunction(tokenHash, "ownerOf", []smartcontract.Parameter{
{ {
Type: smartcontract.StringType, Type: smartcontract.ByteArrayType,
Value: tokenID, Value: tokenID,
}, },
}, nil) }, nil)
@ -143,7 +143,7 @@ func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID string) (util.Ui
// (in FixedN format using contract's number of decimals) to given account and // (in FixedN format using contract's number of decimals) to given account and
// sends it to the network returning just a hash of it. // sends it to the network returning just a hash of it.
func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160, func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, amount int64, tokenID string, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) { tokenHash util.Uint160, amount int64, tokenID []byte, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
if !c.initDone { if !c.initDone {
return util.Uint256{}, errNetworkNotInitialized return util.Uint256{}, errNetworkNotInitialized
} }
@ -161,15 +161,15 @@ func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
// NEP11DBalanceOf invokes `balanceOf` divisible NEP-11 method on a // NEP11DBalanceOf invokes `balanceOf` divisible NEP-11 method on a
// specified contract. // specified contract.
func (c *Client) NEP11DBalanceOf(tokenHash, owner util.Uint160, tokenID string) (int64, error) { func (c *Client) NEP11DBalanceOf(tokenHash, owner util.Uint160, tokenID []byte) (int64, error) {
return c.nepBalanceOf(tokenHash, owner, &tokenID) return c.nepBalanceOf(tokenHash, owner, tokenID)
} }
// NEP11DOwnerOf returns list of the specified NEP-11 divisible token owners. // NEP11DOwnerOf returns list of the specified NEP-11 divisible token owners.
func (c *Client) NEP11DOwnerOf(tokenHash util.Uint160, tokenID string) ([]util.Uint160, error) { func (c *Client) NEP11DOwnerOf(tokenHash util.Uint160, tokenID []byte) ([]util.Uint160, error) {
result, err := c.InvokeFunction(tokenHash, "ownerOf", []smartcontract.Parameter{ result, err := c.InvokeFunction(tokenHash, "ownerOf", []smartcontract.Parameter{
{ {
Type: smartcontract.StringType, Type: smartcontract.ByteArrayType,
Value: tokenID, Value: tokenID,
}, },
}, nil) }, nil)
@ -198,9 +198,9 @@ func (c *Client) NEP11DOwnerOf(tokenHash util.Uint160, tokenID string) ([]util.U
// NEP11Properties invokes `properties` optional NEP-11 method on a // NEP11Properties invokes `properties` optional NEP-11 method on a
// specified contract. // specified contract.
func (c *Client) NEP11Properties(tokenHash util.Uint160, tokenID string) (*stackitem.Map, error) { func (c *Client) NEP11Properties(tokenHash util.Uint160, tokenID []byte) (*stackitem.Map, error) {
result, err := c.InvokeFunction(tokenHash, "properties", []smartcontract.Parameter{{ result, err := c.InvokeFunction(tokenHash, "properties", []smartcontract.Parameter{{
Type: smartcontract.StringType, Type: smartcontract.ByteArrayType,
Value: tokenID, Value: tokenID,
}}, nil) }}, nil)
if err != nil { if err != nil {
@ -215,7 +215,7 @@ func (c *Client) NEP11Properties(tokenHash util.Uint160, tokenID string) (*stack
} }
// NEP11Tokens returns list of the tokens minted by the contract. // NEP11Tokens returns list of the tokens minted by the contract.
func (c *Client) NEP11Tokens(tokenHash util.Uint160) ([]string, error) { func (c *Client) NEP11Tokens(tokenHash util.Uint160) ([][]byte, error) {
result, err := c.InvokeFunction(tokenHash, "tokens", []smartcontract.Parameter{}, nil) result, err := c.InvokeFunction(tokenHash, "tokens", []smartcontract.Parameter{}, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -225,13 +225,13 @@ func (c *Client) NEP11Tokens(tokenHash util.Uint160) ([]string, error) {
return nil, err return nil, err
} }
arr, err := topIterableFromStack(result.Stack, string("")) arr, err := topIterableFromStack(result.Stack, []byte{})
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get token IDs from stack: %w", err) return nil, fmt.Errorf("failed to get token IDs from stack: %w", err)
} }
tokens := make([]string, len(arr)) tokens := make([][]byte, len(arr))
for i := range tokens { for i := range tokens {
tokens[i] = arr[i].(string) tokens[i] = arr[i].([]byte)
} }
return tokens, nil return tokens, nil
} }

View file

@ -3,6 +3,7 @@ package server
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/hex"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testchain" "github.com/nspcc-dev/neo-go/internal/testchain"
@ -107,7 +108,7 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
acc0 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)) acc0 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0))
check := func(t *testing.T, extraFee int64) { check := func(t *testing.T, extraFee int64) {
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
tx.ValidUntilBlock = 20 tx.ValidUntilBlock = 25
tx.Signers = []transaction.Signer{{ tx.Signers = []transaction.Signer{{
Account: acc0.PrivateKey().GetScriptHash(), Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
@ -165,7 +166,7 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
check := func(t *testing.T, extraFee int64) { check := func(t *testing.T, extraFee int64) {
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
tx.ValidUntilBlock = 20 tx.ValidUntilBlock = 25
tx.Signers = []transaction.Signer{ tx.Signers = []transaction.Signer{
{ {
Account: acc0.PrivateKey().GetScriptHash(), Account: acc0.PrivateKey().GetScriptHash(),
@ -801,7 +802,7 @@ func TestClient_GetNativeContracts(t *testing.T) {
require.Equal(t, chain.GetNatives(), cs) require.Equal(t, chain.GetNatives(), cs)
} }
func TestClient_NEP11(t *testing.T) { func TestClient_NEP11_ND(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close() defer chain.Close()
defer func() { _ = rpcSrv.Shutdown() }() defer func() { _ = rpcSrv.Shutdown() }()
@ -810,7 +811,7 @@ func TestClient_NEP11(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init()) require.NoError(t, c.Init())
h, err := util.Uint160DecodeStringLE(nameServiceContractHash) h, err := util.Uint160DecodeStringLE(nnsContractHash)
require.NoError(t, err) require.NoError(t, err)
acc := testchain.PrivateKeyByID(0).GetScriptHash() acc := testchain.PrivateKeyByID(0).GetScriptHash()
@ -846,12 +847,12 @@ func TestClient_NEP11(t *testing.T) {
require.EqualValues(t, 1, b) require.EqualValues(t, 1, b)
}) })
t.Run("OwnerOf", func(t *testing.T) { t.Run("OwnerOf", func(t *testing.T) {
b, err := c.NEP11NDOwnerOf(h, "neo.com") b, err := c.NEP11NDOwnerOf(h, []byte("neo.com"))
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, acc, b) require.EqualValues(t, acc, b)
}) })
t.Run("Properties", func(t *testing.T) { t.Run("Properties", func(t *testing.T) {
p, err := c.NEP11Properties(h, "neo.com") p, err := c.NEP11Properties(h, []byte("neo.com"))
require.NoError(t, err) require.NoError(t, err)
blockRegisterDomain, err := chain.GetBlock(chain.GetHeaderHash(14)) // `neo.com` domain was registered in 14th block blockRegisterDomain, err := chain.GetBlock(chain.GetHeaderHash(14)) // `neo.com` domain was registered in 14th block
require.NoError(t, err) require.NoError(t, err)
@ -867,6 +868,73 @@ func TestClient_NEP11(t *testing.T) {
}) })
} }
func TestClient_NEP11_D(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer func() { _ = rpcSrv.Shutdown() }()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
priv0 := testchain.PrivateKeyByID(0).GetScriptHash()
priv1 := testchain.PrivateKeyByID(1).GetScriptHash()
token1ID, err := hex.DecodeString(nfsoToken1ID)
require.NoError(t, err)
t.Run("Decimals", func(t *testing.T) {
d, err := c.NEP11Decimals(nfsoHash)
require.NoError(t, err)
require.EqualValues(t, 2, d) // Divisible.
})
t.Run("TotalSupply", func(t *testing.T) {
s, err := c.NEP11TotalSupply(nfsoHash)
require.NoError(t, err)
require.EqualValues(t, 1, s) // the only NFSO of acc0
})
t.Run("Symbol", func(t *testing.T) {
sym, err := c.NEP11Symbol(nfsoHash)
require.NoError(t, err)
require.Equal(t, "NFSO", sym)
})
t.Run("TokenInfo", func(t *testing.T) {
tok, err := c.NEP11TokenInfo(nfsoHash)
require.NoError(t, err)
require.Equal(t, &wallet.Token{
Name: "NeoFS Object NFT",
Hash: nfsoHash,
Decimals: 2,
Symbol: "NFSO",
Standard: manifest.NEP11StandardName,
}, tok)
})
t.Run("BalanceOf", func(t *testing.T) {
b, err := c.NEP11BalanceOf(nfsoHash, priv0)
require.NoError(t, err)
require.EqualValues(t, 80, b)
})
t.Run("OwnerOf", func(t *testing.T) {
b, err := c.NEP11DOwnerOf(nfsoHash, token1ID)
require.NoError(t, err)
require.Equal(t, []util.Uint160{priv1, priv0}, b)
})
t.Run("Properties", func(t *testing.T) {
p, err := c.NEP11Properties(nfsoHash, token1ID)
require.NoError(t, err)
expected := stackitem.NewMap()
expected.Add(stackitem.Make([]byte("name")), stackitem.NewBuffer([]byte("NeoFS Object "+base64.StdEncoding.EncodeToString(token1ID))))
expected.Add(stackitem.Make([]byte("containerID")), stackitem.Make([]byte(base64.StdEncoding.EncodeToString(nfsoToken1ContainerID.BytesBE()))))
expected.Add(stackitem.Make([]byte("objectID")), stackitem.Make([]byte(base64.StdEncoding.EncodeToString(nfsoToken1ObjectID.BytesBE()))))
require.EqualValues(t, expected, p)
})
t.Run("Transfer", func(t *testing.T) {
_, err := c.TransferNEP11D(wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)),
testchain.PrivateKeyByID(1).GetScriptHash(),
nfsoHash, 20, token1ID, nil, 0, nil)
require.NoError(t, err)
})
}
func TestClient_NNS(t *testing.T) { func TestClient_NNS(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close() defer chain.Close()
@ -876,34 +944,31 @@ func TestClient_NNS(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init()) require.NoError(t, c.Init())
nsHash, err := util.Uint160DecodeStringLE(nameServiceContractHash)
require.NoError(t, err)
t.Run("NNSIsAvailable, false", func(t *testing.T) { t.Run("NNSIsAvailable, false", func(t *testing.T) {
b, err := c.NNSIsAvailable(nsHash, "neo.com") b, err := c.NNSIsAvailable(nnsHash, "neo.com")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, false, b) require.Equal(t, false, b)
}) })
t.Run("NNSIsAvailable, true", func(t *testing.T) { t.Run("NNSIsAvailable, true", func(t *testing.T) {
b, err := c.NNSIsAvailable(nsHash, "neogo.com") b, err := c.NNSIsAvailable(nnsHash, "neogo.com")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, true, b) require.Equal(t, true, b)
}) })
t.Run("NNSResolve, good", func(t *testing.T) { t.Run("NNSResolve, good", func(t *testing.T) {
b, err := c.NNSResolve(nsHash, "neo.com", nns.A) b, err := c.NNSResolve(nnsHash, "neo.com", nns.A)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "1.2.3.4", b) require.Equal(t, "1.2.3.4", b)
}) })
t.Run("NNSResolve, bad", func(t *testing.T) { t.Run("NNSResolve, bad", func(t *testing.T) {
_, err := c.NNSResolve(nsHash, "neogo.com", nns.A) _, err := c.NNSResolve(nnsHash, "neogo.com", nns.A)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("NNSResolve, forbidden", func(t *testing.T) { t.Run("NNSResolve, forbidden", func(t *testing.T) {
_, err := c.NNSResolve(nsHash, "neogo.com", nns.CNAME) _, err := c.NNSResolve(nnsHash, "neogo.com", nns.CNAME)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("NNSGetAllRecords, good", func(t *testing.T) { t.Run("NNSGetAllRecords, good", func(t *testing.T) {
rss, err := c.NNSGetAllRecords(nsHash, "neo.com") rss, err := c.NNSGetAllRecords(nnsHash, "neo.com")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []nns.RecordState{ require.Equal(t, []nns.RecordState{
{ {
@ -914,7 +979,7 @@ func TestClient_NNS(t *testing.T) {
}, rss) }, rss)
}) })
t.Run("NNSGetAllRecords, bad", func(t *testing.T) { t.Run("NNSGetAllRecords, bad", func(t *testing.T) {
_, err := c.NNSGetAllRecords(nsHash, "neopython.com") _, err := c.NNSGetAllRecords(nnsHash, "neopython.com")
require.Error(t, err) require.Error(t, err)
}) })
} }

View file

@ -75,7 +75,7 @@ func getUnitTestChain(t testing.TB, enableOracle bool, enableNotary bool) (*core
} }
func getTestBlocks(t *testing.T) []*block.Block { func getTestBlocks(t *testing.T) []*block.Block {
// File "./testdata/testblocks.acc" was generated by function core._ // File "./testdata/testblocks.acc" was generated by function core.TestCreateBasicChain
// ("neo-go/pkg/core/helper_test.go"). // ("neo-go/pkg/core/helper_test.go").
// To generate new "./testdata/testblocks.acc", follow the steps: // To generate new "./testdata/testblocks.acc", follow the steps:
// 1. Rename the function // 1. Rename the function

View file

@ -61,30 +61,27 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{}) check func(t *testing.T, e *executor, result interface{})
} }
const testContractHash = "5c9e40a12055c6b9e3f72271c9779958c842135d" const genesisBlockHash = "a4ae00f6ac7496cac14e709fbf8b8ecb4c9831d8a6ee396056af9350fcf22671"
const deploymentTxHash = "8de63ea12ca8a9c5233ebf8664a442c881ae1bb83708d82da7fa1da2305ecf14" const testContractHash = "1ab08f5508edafa6f28e3db3227442a9e70aac52"
const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" const deploymentTxHash = "017c9edb217477aeb3e0c35462361209fdb7bf104dc8e285e2385af8713926b4"
const verifyContractHash = "f68822e4ecd93de334bdf1f7c409eda3431bcbd0" const (
const verifyContractAVM = "VwIAQS1RCDAhcAwU7p6iLCfjS9AUj8QQjgj3To9QSLLbMHFoE87bKGnbKJdA" verifyContractHash = "7deef31e5c616e157cdf02a5446f36d0a4eead52"
const verifyWithArgsContractHash = "947c780f45b2a3d32e946355ee5cb57faf4decb7" verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
const invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGhB+CfsjCGqJgQRQAwUDQ8DAgkAAgEDBwMEBQIBAA4GDAnbMHFpQfgn7IwhqiYEEkATQA==" verifyWithArgsContractHash = "6df009754ce475a6a5730c9e488f80e8e47bc1f1"
nnsContractHash = "1a7530a4c6cfdd40ffed40775aa5453febab24c0"
nnsToken1ID = "6e656f2e636f6d"
nfsoContractHash = "aaf8913c501e25c42877e79f04cb7c2c1ab47e57"
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
)
const nameServiceContractHash = "3a602b3e7cfd760850bfac44f4a9bb0ebad3e2dc" var (
nnsHash, _ = util.Uint160DecodeStringLE(nnsContractHash)
var NNSHash = util.Uint160{0xdc, 0xe2, 0xd3, 0xba, 0x0e, 0xbb, 0xa9, 0xf4, 0x44, 0xac, 0xbf, 0x50, 0x08, 0x76, 0xfd, 0x7c, 0x3e, 0x2b, 0x60, 0x3a} nfsoHash, _ = util.Uint160DecodeStringLE(nfsoContractHash)
nfsoToken1ContainerID = util.Uint256{1, 2, 3}
var nep11Reg = &result.NEP11Balances{ nfsoToken1ObjectID = util.Uint256{4, 5, 6}
Address: "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", )
Balances: []result.NEP11AssetBalance{{
Asset: NNSHash,
Tokens: []result.NEP11TokenBalance{{
ID: "6e656f2e636f6d",
Amount: "1",
LastUpdated: 14,
}},
}},
}
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -245,12 +242,14 @@ var rpcTestCases = map[string][]rpcTestCase{
{ {
name: "positive", name: "positive",
params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`,
result: func(e *executor) interface{} { return nep11Reg }, result: func(e *executor) interface{} { return &result.NEP11Balances{} },
check: checkNep11Balances,
}, },
{ {
name: "positive_address", name: "positive_address",
params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`, params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`,
result: func(e *executor) interface{} { return nep11Reg }, result: func(e *executor) interface{} { return &result.NEP11Balances{} },
check: checkNep11Balances,
}, },
}, },
"getnep11properties": { "getnep11properties": {
@ -266,21 +265,21 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "no token", name: "no token",
params: `["` + NNSHash.StringLE() + `"]`, params: `["` + nnsContractHash + `"]`,
fail: true, fail: true,
}, },
{ {
name: "bad token", name: "bad token",
params: `["` + NNSHash.StringLE() + `", "abcdef"]`, params: `["` + nnsContractHash + `", "abcdef"]`,
fail: true, fail: true,
}, },
{ {
name: "positive", name: "positive",
params: `["` + NNSHash.StringLE() + `", "6e656f2e636f6d"]`, params: `["` + nnsContractHash + `", "6e656f2e636f6d"]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
return &map[string]interface{}{ return &map[string]interface{}{
"name": "neo.com", "name": "neo.com",
"expiration": "bhORxoMB", "expiration": "HrL+G4YB",
} }
}, },
}, },
@ -309,9 +308,8 @@ var rpcTestCases = map[string][]rpcTestCase{
{ {
name: "positive", name: "positive",
params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} { return &result.NEP11Transfers{} },
return &result.NEP11Transfers{Sent: []result.NEP11Transfer{}, Received: []result.NEP11Transfer{{Timestamp: 0x17c6edfe76e, Asset: util.Uint160{0xdc, 0xe2, 0xd3, 0xba, 0xe, 0xbb, 0xa9, 0xf4, 0x44, 0xac, 0xbf, 0x50, 0x8, 0x76, 0xfd, 0x7c, 0x3e, 0x2b, 0x60, 0x3a}, Address: "", ID: "6e656f2e636f6d", Amount: "1", Index: 0xe, NotifyIndex: 0x0, TxHash: util.Uint256{0x5b, 0x5a, 0x5b, 0xae, 0xf2, 0xc5, 0x63, 0x8a, 0x2e, 0xcc, 0x77, 0x27, 0xd9, 0x6b, 0xb9, 0xda, 0x3a, 0x7f, 0x30, 0xaa, 0xcf, 0xda, 0x7f, 0x8a, 0x10, 0xd3, 0x23, 0xbf, 0xd, 0x1f, 0x28, 0x69}}}, Address: "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn"} check: checkNep11Transfers,
},
}, },
}, },
"getnep17balances": { "getnep17balances": {
@ -812,7 +810,7 @@ var rpcTestCases = map[string][]rpcTestCase{
require.True(t, ok) require.True(t, ok)
expected := result.UnclaimedGas{ expected := result.UnclaimedGas{
Address: testchain.MultisigScriptHash(), Address: testchain.MultisigScriptHash(),
Unclaimed: *big.NewInt(8000), Unclaimed: *big.NewInt(10500),
} }
assert.Equal(t, expected, *actual) assert.Equal(t, expected, *actual)
}, },
@ -882,16 +880,16 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, with notifications", name: "positive, with notifications",
params: `["` + NNSHash.StringLE() + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`, params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
script := []byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b, 0x13, 0xc0, 0x1f, 0x0c, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x0c, 0x14, 0xdc, 0xe2, 0xd3, 0xba, 0x0e, 0xbb, 0xa9, 0xf4, 0x44, 0xac, 0xbf, 0x50, 0x08, 0x76, 0xfd, 0x7c, 0x3e, 0x2b, 0x60, 0x3a, 0x41, 0x62, 0x7d, 0x5b, 0x52} script := []byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b, 0x13, 0xc0, 0x1f, 0x0c, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x0c, 0x14, 0xc0, 0x24, 0xab, 0xeb, 0x3f, 0x45, 0xa5, 0x5a, 0x77, 0x40, 0xed, 0xff, 0x40, 0xdd, 0xcf, 0xc6, 0xa4, 0x30, 0x75, 0x1a, 0x41, 0x62, 0x7d, 0x5b, 0x52}
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 33767940, GasConsumed: 32167260,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make(true)}, Stack: []stackitem.Item{stackitem.Make(true)},
Notifications: []state.NotificationEvent{{ Notifications: []state.NotificationEvent{{
ScriptHash: NNSHash, ScriptHash: nnsHash,
Name: "Transfer", Name: "Transfer",
Item: stackitem.NewArray([]stackitem.Item{ Item: stackitem.NewArray([]stackitem.Item{
stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}), stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}),
@ -917,19 +915,19 @@ var rpcTestCases = map[string][]rpcTestCase{
chg := []storage.Operation{{ chg := []storage.Operation{{
State: "Changed", State: "Changed",
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb}, Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
Value: []byte{0xbc, 0xf8, 0x8b, 0xa, 0x56, 0x79, 0x12}, Value: []byte{0xe8, 0x80, 0x64, 0xcb, 0x53, 0x79, 0x12},
}, { }, {
State: "Added", State: "Added",
Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb}, Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb},
Value: []byte{0x41, 0x3, 0x21, 0x1, 0x1, 0x21, 0x1, 0x11, 0x0}, Value: []byte{0x41, 0x3, 0x21, 0x1, 0x1, 0x21, 0x1, 0x16, 0},
}, { }, {
State: "Changed", State: "Changed",
Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
Value: []byte{0x41, 0x3, 0x21, 0x4, 0x2f, 0xd9, 0xf5, 0x5, 0x21, 0x1, 0x11, 0x0}, Value: []byte{0x41, 0x3, 0x21, 0x4, 0x2f, 0xd9, 0xf5, 0x5, 0x21, 0x1, 0x16, 0},
}, { }, {
State: "Changed", State: "Changed",
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
Value: []byte{0x41, 0x1, 0x21, 0x5, 0x4, 0xfa, 0xb2, 0x9b, 0xd}, Value: []byte{0x41, 0x01, 0x21, 0x05, 0x9e, 0x0b, 0x0b, 0x18, 0x0b},
}} }}
// Can be returned in any order. // Can be returned in any order.
assert.ElementsMatch(t, chg, res.Diagnostics.Changes) assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
@ -937,14 +935,14 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, verbose", name: "positive, verbose",
params: `["` + NNSHash.StringLE() + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`, params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
script := []byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14, 0xdc, 0xe2, 0xd3, 0xba, 0xe, 0xbb, 0xa9, 0xf4, 0x44, 0xac, 0xbf, 0x50, 0x8, 0x76, 0xfd, 0x7c, 0x3e, 0x2b, 0x60, 0x3a, 0x41, 0x62, 0x7d, 0x5b, 0x52} script := []byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14, 0xc0, 0x24, 0xab, 0xeb, 0x3f, 0x45, 0xa5, 0x5a, 0x77, 0x40, 0xed, 0xff, 0x40, 0xdd, 0xcf, 0xc6, 0xa4, 0x30, 0x75, 0x1a, 0x41, 0x62, 0x7d, 0x5b, 0x52}
stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib)
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 17958510, GasConsumed: 15928320,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
Notifications: []state.NotificationEvent{}, Notifications: []state.NotificationEvent{},
@ -954,7 +952,7 @@ var rpcTestCases = map[string][]rpcTestCase{
Current: hash.Hash160(script), Current: hash.Hash160(script),
Calls: []*vm.InvocationTree{ Calls: []*vm.InvocationTree{
{ {
Current: NNSHash, Current: nnsHash,
Calls: []*vm.InvocationTree{ Calls: []*vm.InvocationTree{
{ {
Current: stdHash, Current: stdHash,
@ -1834,7 +1832,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
require.NoErrorf(t, err, "could not parse response: %s", txOut) require.NoErrorf(t, err, "could not parse response: %s", txOut)
assert.Equal(t, *block.Transactions[0], actual.Transaction) assert.Equal(t, *block.Transactions[0], actual.Transaction)
assert.Equal(t, 17, actual.Confirmations) assert.Equal(t, 22, actual.Confirmations)
assert.Equal(t, TXHash, actual.Transaction.Hash()) assert.Equal(t, TXHash, actual.Transaction.Hash())
}) })
@ -1947,12 +1945,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
require.NoError(t, json.Unmarshal(res, actual)) require.NoError(t, json.Unmarshal(res, actual))
checkNep17TransfersAux(t, e, actual, sent, rcvd) checkNep17TransfersAux(t, e, actual, sent, rcvd)
} }
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{10, 11, 12, 13}, []int{2, 3}) }) t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{14, 15, 16, 17}, []int{3, 4}) })
t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) }) t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) })
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{7, 8}, []int{1}) }) t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{11, 12}, []int{2}) })
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{10}, []int{2}) }) t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{14}, []int{3}) })
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{9, 10}, []int{2}) }) t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{13, 14}, []int{3}) })
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{11, 12}, []int{3}) }) t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{15, 16}, []int{4}) })
}) })
} }
@ -2036,6 +2034,39 @@ func doRPCCallOverHTTP(rpcCall string, url string, t *testing.T) []byte {
return bytes.TrimSpace(body) return bytes.TrimSpace(body)
} }
func checkNep11Balances(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.NEP11Balances)
require.True(t, ok)
expected := result.NEP11Balances{
Balances: []result.NEP11AssetBalance{
{
Asset: nnsHash,
Tokens: []result.NEP11TokenBalance{
{
ID: nnsToken1ID,
Amount: "1",
LastUpdated: 14,
},
},
},
{
Asset: nfsoHash,
Tokens: []result.NEP11TokenBalance{
{
ID: nfsoToken1ID,
Amount: "80",
LastUpdated: 21,
},
},
},
},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
}
require.Equal(t, testchain.PrivateKeyByID(0).Address(), res.Address)
require.ElementsMatch(t, expected.Balances, res.Balances)
}
func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.NEP17Balances) res, ok := acc.(*result.NEP17Balances)
require.True(t, ok) require.True(t, ok)
@ -2055,8 +2086,8 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "57796785740", Amount: "46748035310",
LastUpdated: 16, LastUpdated: 19,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
} }
@ -2064,8 +2095,110 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
require.ElementsMatch(t, expected.Balances, res.Balances) require.ElementsMatch(t, expected.Balances, res.Balances)
} }
func checkNep11Transfers(t *testing.T, e *executor, acc interface{}) {
checkNep11TransfersAux(t, e, acc, []int{0}, []int{0, 1, 2})
}
func checkNep11TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) {
res, ok := acc.(*result.NEP11Transfers)
require.True(t, ok)
blockReceiveNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(21)) // transfer 0.05 NFSO from priv1 back to priv0.
require.NoError(t, err)
require.Equal(t, 1, len(blockReceiveNFSO.Transactions))
txReceiveNFSO := blockReceiveNFSO.Transactions[0]
blockSendNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1.
require.NoError(t, err)
require.Equal(t, 1, len(blockSendNFSO.Transactions))
txSendNFSO := blockSendNFSO.Transactions[0]
blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token by transferring 10 GAS to NFSO contract.
require.NoError(t, err)
require.Equal(t, 1, len(blockMintNFSO.Transactions))
txMintNFSO := blockMintNFSO.Transactions[0]
blockRegisterNSRecordA, err := e.chain.GetBlock(e.chain.GetHeaderHash(14)) // register `neo.com` with A record type and priv0 owner via NS
require.NoError(t, err)
require.Equal(t, 1, len(blockRegisterNSRecordA.Transactions))
txRegisterNSRecordA := blockRegisterNSRecordA.Transactions[0]
// These are laid out here explicitly for 2 purposes:
// * to be able to reference any particular event for paging
// * to check chain events consistency
// Technically these could be retrieved from application log, but that would almost
// duplicate the Server method.
expected := result.NEP11Transfers{
Sent: []result.NEP11Transfer{
{
Timestamp: blockSendNFSO.Timestamp,
Asset: nfsoHash,
Address: testchain.PrivateKeyByID(1).Address(), // to priv1
ID: nfsoToken1ID, // NFSO ID
Amount: big.NewInt(25).String(),
Index: 19,
TxHash: txSendNFSO.Hash(),
},
},
Received: []result.NEP11Transfer{
{
Timestamp: blockReceiveNFSO.Timestamp,
Asset: nfsoHash,
ID: nfsoToken1ID,
Address: testchain.PrivateKeyByID(1).Address(), // from priv1
Amount: "5",
Index: 21,
TxHash: txReceiveNFSO.Hash(),
},
{
Timestamp: blockMintNFSO.Timestamp,
Asset: nfsoHash,
ID: nfsoToken1ID,
Address: "", // minting
Amount: "100",
Index: 18,
TxHash: txMintNFSO.Hash(),
},
{
Timestamp: blockRegisterNSRecordA.Timestamp,
Asset: nnsHash,
ID: nnsToken1ID,
Address: "", // minting
Amount: "1",
Index: 14,
TxHash: txRegisterNSRecordA.Hash(),
},
},
Address: testchain.PrivateKeyByID(0).Address(),
}
require.Equal(t, expected.Address, res.Address)
arr := make([]result.NEP11Transfer, 0, len(expected.Sent))
for i := range expected.Sent {
for _, j := range sent {
if i == j {
arr = append(arr, expected.Sent[i])
break
}
}
}
require.Equal(t, arr, res.Sent)
arr = arr[:0]
for i := range expected.Received {
for _, j := range rcvd {
if i == j {
arr = append(arr, expected.Received[i])
break
}
}
}
require.Equal(t, arr, res.Received)
}
func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) { func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) {
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []int{0, 1, 2, 3, 4, 5, 6, 7}) checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8})
} }
func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) { func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) {
@ -2074,6 +2207,21 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
rublesHash, err := util.Uint160DecodeStringLE(testContractHash) rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
require.NoError(t, err) require.NoError(t, err)
blockTransferNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1.
require.NoError(t, err)
require.Equal(t, 1, len(blockTransferNFSO.Transactions))
txTransferNFSO := blockTransferNFSO.Transactions[0]
blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token for priv0 by transferring 10 GAS to NFSO contract.
require.NoError(t, err)
require.Equal(t, 1, len(blockMintNFSO.Transactions))
txMintNFSO := blockMintNFSO.Transactions[0]
blockDeploy5, err := e.chain.GetBlock(e.chain.GetHeaderHash(17)) // deploy NeoFS Object contract (NEP11-Divisible)
require.NoError(t, err)
require.Equal(t, 1, len(blockDeploy5.Transactions))
txDeploy5 := blockDeploy5.Transactions[0]
blockPutNewTestValue, err := e.chain.GetBlock(e.chain.GetHeaderHash(16)) // invoke `put` method of `test_contract.go` with `testkey`, `newtestvalue` args blockPutNewTestValue, err := e.chain.GetBlock(e.chain.GetHeaderHash(16)) // invoke `put` method of `test_contract.go` with `testkey`, `newtestvalue` args
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(blockPutNewTestValue.Transactions)) require.Equal(t, 1, len(blockPutNewTestValue.Transactions))
@ -2155,6 +2303,39 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
// duplicate the Server method. // duplicate the Server method.
expected := result.NEP17Transfers{ expected := result.NEP17Transfers{
Sent: []result.NEP17Transfer{ Sent: []result.NEP17Transfer{
{
Timestamp: blockTransferNFSO.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: "", // burn
Amount: big.NewInt(txTransferNFSO.SystemFee + txTransferNFSO.NetworkFee).String(),
Index: 19,
TxHash: blockTransferNFSO.Hash(),
},
{
Timestamp: blockMintNFSO.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: address.Uint160ToString(nfsoHash),
Amount: "1000000000",
Index: 18,
NotifyIndex: 0,
TxHash: txMintNFSO.Hash(),
},
{
Timestamp: blockMintNFSO.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: "", // burn
Amount: big.NewInt(txMintNFSO.SystemFee + txMintNFSO.NetworkFee).String(),
Index: 18,
TxHash: blockMintNFSO.Hash(),
},
{
Timestamp: blockDeploy5.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: "", // burn
Amount: big.NewInt(txDeploy5.SystemFee + txDeploy5.NetworkFee).String(),
Index: 17,
TxHash: blockDeploy5.Hash(),
},
{ {
Timestamp: blockPutNewTestValue.Timestamp, Timestamp: blockPutNewTestValue.Timestamp,
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
@ -2288,6 +2469,15 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
}, },
}, },
Received: []result.NEP17Transfer{ Received: []result.NEP17Transfer{
{
Timestamp: blockMintNFSO.Timestamp, // GAS bounty
Asset: e.chain.UtilityTokenHash(),
Address: "",
Amount: "50000000",
Index: 18,
NotifyIndex: 0,
TxHash: blockMintNFSO.Hash(),
},
{ {
Timestamp: blockGASBounty2.Timestamp, Timestamp: blockGASBounty2.Timestamp,
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),

Binary file not shown.