forked from TrueCloudLab/neoneo-go
Merge pull request #1590 from nspcc-dev/tests/wallet
Add CLI tests for `contract` and `wallet` commands
This commit is contained in:
commit
818dc078da
18 changed files with 586 additions and 86 deletions
|
@ -16,6 +16,61 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestContractInitAndCompile(t *testing.T) {
|
||||
tmpDir := path.Join(os.TempDir(), "neogo.inittest")
|
||||
require.NoError(t, os.Mkdir(tmpDir, os.ModePerm))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
e := newExecutor(t, false)
|
||||
defer e.Close(t)
|
||||
|
||||
t.Run("no path is provided", func(t *testing.T) {
|
||||
e.RunWithError(t, "neo-go", "contract", "init")
|
||||
})
|
||||
t.Run("invalid path", func(t *testing.T) {
|
||||
e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00")
|
||||
})
|
||||
|
||||
ctrPath := path.Join(tmpDir, "testcontract")
|
||||
e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
|
||||
|
||||
t.Run("don't rewrite existing directory", func(t *testing.T) {
|
||||
e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath)
|
||||
})
|
||||
|
||||
// For proper nef generation.
|
||||
config.Version = "0.90.0-test"
|
||||
|
||||
srcPath := path.Join(ctrPath, "main.go")
|
||||
cfgPath := path.Join(ctrPath, "neo-go.yml")
|
||||
nefPath := path.Join(tmpDir, "testcontract.nef")
|
||||
manifestPath := path.Join(tmpDir, "testcontract.manifest.json")
|
||||
cmd := []string{"neo-go", "contract", "compile"}
|
||||
t.Run("missing source", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath)
|
||||
t.Run("missing config, but require manifest", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
t.Run("provided non-existent config", func(t *testing.T) {
|
||||
cfgName := path.Join(ctrPath, "notexists.yml")
|
||||
e.RunWithError(t, append(cmd, "--config", cfgName)...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "--config", cfgPath)
|
||||
e.Run(t, cmd...)
|
||||
e.checkEOF(t)
|
||||
require.FileExists(t, nefPath)
|
||||
require.FileExists(t, manifestPath)
|
||||
|
||||
t.Run("output hex script with --verbose", func(t *testing.T) {
|
||||
e.Run(t, append(cmd, "--verbose")...)
|
||||
e.checkNextLine(t, "^[0-9a-hA-H]+$")
|
||||
})
|
||||
}
|
||||
|
||||
func TestComlileAndInvokeFunction(t *testing.T) {
|
||||
e := newExecutor(t, true)
|
||||
defer e.Close(t)
|
||||
|
@ -23,7 +78,10 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
// For proper nef generation.
|
||||
config.Version = "0.90.0-test"
|
||||
|
||||
tmpDir := os.TempDir()
|
||||
tmpDir := path.Join(os.TempDir(), "neogo.test.compileandinvoke")
|
||||
require.NoError(t, os.Mkdir(tmpDir, os.ModePerm))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
nefName := path.Join(tmpDir, "deploy.nef")
|
||||
manifestName := path.Join(tmpDir, "deploy.manifest.json")
|
||||
e.Run(t, "neo-go", "contract", "compile",
|
||||
|
@ -31,10 +89,12 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
"--config", "testdata/deploy/neo-go.yml",
|
||||
"--out", nefName, "--manifest", manifestName)
|
||||
|
||||
defer func() {
|
||||
os.Remove(nefName)
|
||||
os.Remove(manifestName)
|
||||
}()
|
||||
// Check that it is possible to invoke before deploy.
|
||||
// This doesn't make much sense, because every method has an offset
|
||||
// which is contained in the manifest. This should be either removed or refactored.
|
||||
e.Run(t, "neo-go", "contract", "testinvokescript",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--in", nefName, "--", util.Uint160{1, 2, 3}.StringLE())
|
||||
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "contract", "deploy",
|
||||
|
@ -49,10 +109,33 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
h.StringLE(), "getValue")
|
||||
cmd := []string{"neo-go", "contract", "testinvokefunction",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr}
|
||||
t.Run("missing hash", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
t.Run("invalid hash", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "notahash")...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, h.StringLE())
|
||||
t.Run("missing method", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "getValue")
|
||||
t.Run("invalid params", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "[")...)
|
||||
})
|
||||
t.Run("invalid cosigner", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--", "notahash")...)
|
||||
})
|
||||
t.Run("missing RPC address", func(t *testing.T) {
|
||||
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
|
||||
h.StringLE(), "getValue")
|
||||
})
|
||||
|
||||
e.Run(t, cmd...)
|
||||
|
||||
res := new(result.Invoke)
|
||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||
|
@ -60,6 +143,32 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
require.Len(t, res.Stack, 1)
|
||||
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
|
||||
|
||||
t.Run("real invoke", func(t *testing.T) {
|
||||
cmd := []string{"neo-go", "contract", "invokefunction",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr}
|
||||
t.Run("missing wallet", func(t *testing.T) {
|
||||
cmd := append(cmd, h.StringLE(), "getValue")
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
t.Run("non-existent wallet", func(t *testing.T) {
|
||||
cmd := append(cmd, "--wallet", path.Join(tmpDir, "not.exists"),
|
||||
h.StringLE(), "getValue")
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "--wallet", validatorWallet, "--address", validatorAddr)
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, append(cmd, h.StringLE(), "getValue")...)
|
||||
|
||||
t.Run("failind method", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.RunWithError(t, append(cmd, h.StringLE(), "fail")...)
|
||||
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, append(cmd, "--force", h.StringLE(), "fail")...)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
nefName := path.Join(tmpDir, "updated.nef")
|
||||
manifestName := path.Join(tmpDir, "updated.manifest.json")
|
||||
|
@ -101,6 +210,42 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestContractInspect(t *testing.T) {
|
||||
e := newExecutor(t, false)
|
||||
defer e.Close(t)
|
||||
|
||||
// For proper nef generation.
|
||||
config.Version = "0.90.0-test"
|
||||
const srcPath = "testdata/deploy/main.go"
|
||||
|
||||
tmpDir := path.Join(os.TempDir(), "neogo.test.contract.inspect")
|
||||
require.NoError(t, os.Mkdir(tmpDir, os.ModePerm))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
nefName := path.Join(tmpDir, "deploy.nef")
|
||||
manifestName := path.Join(tmpDir, "deploy.manifest.json")
|
||||
e.Run(t, "neo-go", "contract", "compile",
|
||||
"--in", srcPath,
|
||||
"--config", "testdata/deploy/neo-go.yml",
|
||||
"--out", nefName, "--manifest", manifestName)
|
||||
|
||||
cmd := []string{"neo-go", "contract", "inspect"}
|
||||
t.Run("missing input", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
t.Run("with raw '.go'", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--in", srcPath)...)
|
||||
e.Run(t, append(cmd, "--in", srcPath, "--compile")...)
|
||||
require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
|
||||
})
|
||||
t.Run("with nef", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--in", nefName, "--compile")...)
|
||||
e.RunWithError(t, append(cmd, "--in", path.Join(tmpDir, "not.exists"))...)
|
||||
e.Run(t, append(cmd, "--in", nefName)...)
|
||||
require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompileExamples(t *testing.T) {
|
||||
const examplePath = "../examples"
|
||||
infos, err := ioutil.ReadDir(examplePath)
|
||||
|
|
|
@ -132,7 +132,7 @@ func (e *executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Tr
|
|||
func (e *executor) getNextLine(t *testing.T) string {
|
||||
line, err := e.Out.ReadString('\n')
|
||||
require.NoError(t, err)
|
||||
return line
|
||||
return strings.TrimSuffix(line, "\n")
|
||||
}
|
||||
|
||||
func (e *executor) checkNextLine(t *testing.T, expected string) {
|
||||
|
@ -184,7 +184,9 @@ func (e *executor) Run(t *testing.T, args ...string) {
|
|||
func (e *executor) run(args ...string) error {
|
||||
e.Out.Reset()
|
||||
e.Err.Reset()
|
||||
return e.CLI.Run(args)
|
||||
err := e.CLI.Run(args)
|
||||
e.In.Reset()
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *executor) checkTxPersisted(t *testing.T, prefix ...string) (*transaction.Transaction, uint32) {
|
||||
|
|
|
@ -36,8 +36,8 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
defer os.Remove(wallet2Path)
|
||||
|
||||
addAccount := func(w string, wif string) {
|
||||
e.In.WriteString("acc\rpass\rpass\r")
|
||||
e.Run(t, "neo-go", "wallet", "init", "--wallet", w)
|
||||
e.In.WriteString("acc\rpass\rpass\r")
|
||||
e.Run(t, "neo-go", "wallet", "import-multisig",
|
||||
"--wallet", w,
|
||||
"--wif", wif,
|
||||
|
|
|
@ -295,17 +295,6 @@ func restoreDB(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// readBlock performs reading of block size and then bytes with the length equal to that size.
|
||||
func readBlock(reader *io.BinReader) ([]byte, error) {
|
||||
var size = reader.ReadU32LE()
|
||||
bytes := make([]byte, size)
|
||||
reader.ReadBytes(bytes)
|
||||
if reader.Err != nil {
|
||||
return nil, reader.Err
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func startServer(ctx *cli.Context) error {
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
|
@ -56,7 +57,7 @@ var (
|
|||
Name: "out",
|
||||
Usage: "file to put JSON transaction to",
|
||||
}
|
||||
forceFlag = cli.StringFlag{
|
||||
forceFlag = cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "force-push the transaction in case of bad VM state after test script invocation",
|
||||
}
|
||||
|
@ -350,6 +351,7 @@ func initSmartContract(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
basePath := contractName
|
||||
contractName = path.Base(contractName)
|
||||
fileName := "main.go"
|
||||
|
||||
// create base directory
|
||||
|
@ -500,9 +502,6 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err = c.InvokeFunction(script, operation, params, cosigners)
|
||||
if err != nil {
|
||||
|
@ -510,7 +509,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
|||
}
|
||||
if signAndPush && resp.State != "HALT" {
|
||||
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s\n", resp.State, resp.FaultException)
|
||||
if ctx.String("force") == "" {
|
||||
if !ctx.Bool("force") {
|
||||
return cli.NewExitError(errText+". Use --force flag to send the transaction anyway.", 1)
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...")
|
||||
|
|
5
cli/testdata/deploy/main.go
vendored
5
cli/testdata/deploy/main.go
vendored
|
@ -17,6 +17,11 @@ func _deploy(isUpdate bool) {
|
|||
storage.Put(ctx, key, value)
|
||||
}
|
||||
|
||||
// Fail just fails.
|
||||
func Fail() {
|
||||
panic("as expected")
|
||||
}
|
||||
|
||||
// Update updates contract with the new one.
|
||||
func Update(script, manifest []byte) {
|
||||
contract.Update(script, manifest)
|
||||
|
|
1
cli/testdata/wallets/testwallet_NEO2.json
vendored
Normal file
1
cli/testdata/wallets/testwallet_NEO2.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"wallet1","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}
|
55
cli/testdata/wallets/testwallet_NEO3.json
vendored
Normal file
55
cli/testdata/wallets/testwallet_NEO3.json
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc",
|
||||
"key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux",
|
||||
"label": "",
|
||||
"contract": {
|
||||
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parameter0",
|
||||
"type": "Signature"
|
||||
}
|
||||
],
|
||||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
|
||||
"key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux",
|
||||
"label": "",
|
||||
"contract": {
|
||||
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parameter0",
|
||||
"type": "Signature"
|
||||
},
|
||||
{
|
||||
"name": "parameter1",
|
||||
"type": "Signature"
|
||||
},
|
||||
{
|
||||
"name": "parameter2",
|
||||
"type": "Signature"
|
||||
}
|
||||
],
|
||||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
"n": 16384,
|
||||
"r": 8,
|
||||
"p": 8
|
||||
},
|
||||
"extra": {
|
||||
"Tokens": null
|
||||
}
|
||||
}
|
162
cli/wallet/legacy.go
Normal file
162
cli/wallet/legacy.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
package wallet
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
type (
|
||||
walletV2 struct {
|
||||
Version string `json:"version"`
|
||||
Accounts []accountV2 `json:"accounts"`
|
||||
Scrypt keys.ScryptParams `json:"scrypt"`
|
||||
Extra wallet.Extra `json:"extra"`
|
||||
}
|
||||
accountV2 struct {
|
||||
Address string `json:"address"`
|
||||
EncryptedWIF string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Contract *struct {
|
||||
Script string `json:"script"`
|
||||
Parameters []wallet.ContractParam `json:"parameters"`
|
||||
Deployed bool `json:"deployed"`
|
||||
} `json:"contract"`
|
||||
Locked bool `json:"lock"`
|
||||
Default bool `json:"isdefault"`
|
||||
}
|
||||
)
|
||||
|
||||
// newWalletV2FromFile reads NEO2 wallet from file.
|
||||
// This should be used read-only, no operations are supported on returned wallet.
|
||||
func newWalletV2FromFile(path string) (*walletV2, error) {
|
||||
file, err := os.OpenFile(path, os.O_RDWR, os.ModeAppend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
wall := new(walletV2)
|
||||
return wall, json.NewDecoder(file).Decode(wall)
|
||||
}
|
||||
|
||||
const simpleSigLen = 35
|
||||
|
||||
func (a *accountV2) convert(pass string) (*wallet.Account, error) {
|
||||
address.Prefix = address.NEO2Prefix
|
||||
priv, err := keys.NEP2Decrypt(a.EncryptedWIF, pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address.Prefix = address.NEO3Prefix
|
||||
newAcc := wallet.NewAccountFromPrivateKey(priv)
|
||||
if a.Contract != nil {
|
||||
script, err := hex.DecodeString(a.Contract.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If it is simple signature script, newAcc does already have it.
|
||||
if len(script) != simpleSigLen {
|
||||
nsigs, pubs, ok := parseMultisigContract(script)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid multisig contract")
|
||||
}
|
||||
script, err := smartcontract.CreateMultiSigRedeemScript(nsigs, pubs)
|
||||
if err != nil {
|
||||
return nil, errors.New("can't create new multisig contract")
|
||||
}
|
||||
newAcc.Contract.Script = script
|
||||
newAcc.Contract.Parameters = a.Contract.Parameters
|
||||
newAcc.Contract.Deployed = a.Contract.Deployed
|
||||
}
|
||||
}
|
||||
newAcc.Address = address.Uint160ToString(newAcc.Contract.ScriptHash())
|
||||
newAcc.Default = a.Default
|
||||
newAcc.Label = a.Label
|
||||
newAcc.Locked = a.Locked
|
||||
return newAcc, newAcc.Encrypt(pass)
|
||||
}
|
||||
|
||||
const (
|
||||
opPush1 = 0x51
|
||||
opPush16 = 0x60
|
||||
opPushBytes1 = 0x01
|
||||
opPushBytes2 = 0x02
|
||||
opPushBytes33 = 0x21
|
||||
opCheckMultisig = 0xAE
|
||||
opRet = 0x66
|
||||
)
|
||||
|
||||
func getNumOfThingsFromInstr(script []byte) (int, int, bool) {
|
||||
var op = script[0]
|
||||
switch {
|
||||
case opPush1 <= op && op <= opPush16:
|
||||
return int(op-opPush1) + 1, 1, true
|
||||
case op == opPushBytes1 && len(script) >= 2:
|
||||
return int(script[1]), 2, true
|
||||
case op == opPushBytes2 && len(script) >= 3:
|
||||
return int(binary.LittleEndian.Uint16(script[1:])), 3, true
|
||||
default:
|
||||
return 0, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
const minMultisigLen = 37
|
||||
|
||||
// parseMultisigContract accepts multisig verification script from NEO2
|
||||
// and returns list of public keys in the same order as in script..
|
||||
func parseMultisigContract(script []byte) (int, keys.PublicKeys, bool) {
|
||||
// It should contain at least 1 public key.
|
||||
if len(script) < minMultisigLen {
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
nsigs, offset, ok := getNumOfThingsFromInstr(script)
|
||||
if !ok {
|
||||
return 0, nil, false
|
||||
}
|
||||
var pubs [][]byte
|
||||
var nkeys int
|
||||
for offset < len(script) && script[offset] == opPushBytes33 {
|
||||
if len(script[offset:]) < 34 {
|
||||
return 0, nil, false
|
||||
}
|
||||
pubs = append(pubs, script[offset+1:offset+34])
|
||||
nkeys++
|
||||
offset += 34
|
||||
}
|
||||
if nkeys < nsigs || offset >= len(script) {
|
||||
return 0, nil, false
|
||||
}
|
||||
nkeys2, off, ok := getNumOfThingsFromInstr(script[offset:])
|
||||
if !ok || nkeys2 != nkeys {
|
||||
return 0, nil, false
|
||||
}
|
||||
end := script[offset+off:]
|
||||
switch {
|
||||
case len(end) == 1 && end[0] == opCheckMultisig:
|
||||
case len(end) == 2 && end[0] == opCheckMultisig && end[1] == opRet:
|
||||
default:
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
ret := make(keys.PublicKeys, len(pubs))
|
||||
for i := range pubs {
|
||||
pub, err := keys.NewPublicKeyFromBytes(pubs[i], elliptic.P256())
|
||||
if err != nil {
|
||||
return 0, nil, false
|
||||
}
|
||||
ret[i] = pub
|
||||
|
||||
}
|
||||
return nsigs, ret, true
|
||||
}
|
111
cli/wallet/legacy_test.go
Normal file
111
cli/wallet/legacy_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package wallet
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testParseMultisigContract(t *testing.T, s []byte, nsigs int, keys ...*keys.PublicKey) {
|
||||
ns, ks, ok := parseMultisigContract(s)
|
||||
if len(keys) == 0 {
|
||||
require.False(t, ok)
|
||||
return
|
||||
}
|
||||
require.True(t, ok)
|
||||
require.Equal(t, nsigs, ns)
|
||||
require.Equal(t, len(keys), len(ks))
|
||||
for i := range keys {
|
||||
require.Equal(t, keys[i], ks[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMultisigContract(t *testing.T) {
|
||||
t.Run("single multisig", func(t *testing.T) {
|
||||
s := fromHex(t, "512102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc251ae")
|
||||
pub := pubFromHex(t, "02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2")
|
||||
t.Run("good, no ret", func(t *testing.T) {
|
||||
testParseMultisigContract(t, s, 1, pub)
|
||||
})
|
||||
t.Run("good, with ret", func(t *testing.T) {
|
||||
s := append(s, opRet)
|
||||
testParseMultisigContract(t, s, 1, pub)
|
||||
})
|
||||
t.Run("bad, no check multisig", func(t *testing.T) {
|
||||
sBad := make([]byte, len(s))
|
||||
copy(sBad, s)
|
||||
sBad[len(sBad)-1] ^= 0xFF
|
||||
testParseMultisigContract(t, sBad, 0)
|
||||
})
|
||||
t.Run("bad, invalid number of keys", func(t *testing.T) {
|
||||
sBad := make([]byte, len(s))
|
||||
copy(sBad, s)
|
||||
sBad[len(sBad)-2] = opPush1 + 1
|
||||
testParseMultisigContract(t, sBad, 0)
|
||||
})
|
||||
t.Run("bad, invalid first instruction", func(t *testing.T) {
|
||||
sBad := make([]byte, len(s))
|
||||
copy(sBad, s)
|
||||
sBad[0] = 0xFF
|
||||
testParseMultisigContract(t, sBad, 0)
|
||||
})
|
||||
t.Run("bad, invalid public key", func(t *testing.T) {
|
||||
sBad := make([]byte, len(s))
|
||||
copy(sBad, s)
|
||||
sBad[2] = 0xFF
|
||||
testParseMultisigContract(t, sBad, 0)
|
||||
})
|
||||
t.Run("bad, many sigs", func(t *testing.T) {
|
||||
sBad := make([]byte, len(s))
|
||||
copy(sBad, s)
|
||||
sBad[0] = opPush1 + 1
|
||||
testParseMultisigContract(t, sBad, 0)
|
||||
})
|
||||
t.Run("empty, no panic", func(t *testing.T) {
|
||||
testParseMultisigContract(t, []byte{}, 0)
|
||||
})
|
||||
})
|
||||
t.Run("3/4 multisig", func(t *testing.T) {
|
||||
// From privnet consensus wallet.
|
||||
s := fromHex(t, "532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae")
|
||||
ks := keys.PublicKeys{
|
||||
pubFromHex(t, "02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e"),
|
||||
pubFromHex(t, "02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62"),
|
||||
pubFromHex(t, "02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2"),
|
||||
pubFromHex(t, "03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699"),
|
||||
}
|
||||
t.Run("good", func(t *testing.T) {
|
||||
testParseMultisigContract(t, s, 3, ks...)
|
||||
})
|
||||
t.Run("good, with pushbytes1", func(t *testing.T) {
|
||||
s := append([]byte{opPushBytes1, 3}, s[1:]...)
|
||||
testParseMultisigContract(t, s, 3, ks...)
|
||||
})
|
||||
t.Run("good, with pushbytes2", func(t *testing.T) {
|
||||
s := append([]byte{opPushBytes2, 3, 0}, s[1:]...)
|
||||
testParseMultisigContract(t, s, 3, ks...)
|
||||
})
|
||||
t.Run("bad, no panic on prefix", func(t *testing.T) {
|
||||
for i := minMultisigLen; i < len(s)-1; i++ {
|
||||
testParseMultisigContract(t, s[:i], 0)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func fromHex(t *testing.T, s string) []byte {
|
||||
bs, err := hex.DecodeString(s)
|
||||
require.NoError(t, err)
|
||||
return bs
|
||||
}
|
||||
|
||||
func pubFromHex(t *testing.T, s string) *keys.PublicKey {
|
||||
bs := fromHex(t, s)
|
||||
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
||||
require.NoError(t, err)
|
||||
return pub
|
||||
}
|
|
@ -158,10 +158,6 @@ func getNEP17Balance(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
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")
|
||||
|
||||
|
@ -366,10 +362,6 @@ func multiTransferNEP17(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
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 {
|
||||
return cli.NewExitError("empty recipients list", 1)
|
||||
|
@ -433,10 +425,6 @@ func transferNEP17(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
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)
|
||||
to := toFlag.Uint160()
|
||||
|
|
|
@ -247,40 +247,25 @@ func claimGas(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
func convertWallet(ctx *cli.Context) error {
|
||||
wall, err := openWallet(ctx.String("wallet"))
|
||||
wall, err := newWalletV2FromFile(ctx.String("wallet"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
defer wall.Close()
|
||||
|
||||
newWallet, err := wallet.NewWallet(ctx.String("out"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, -1)
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
defer newWallet.Close()
|
||||
|
||||
for _, acc := range wall.Accounts {
|
||||
address.Prefix = address.NEO2Prefix
|
||||
|
||||
pass, err := input.ReadPassword(ctx.App.Writer, fmt.Sprintf("Enter passphrase for account %s (label '%s') > ", acc.Address, acc.Label))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, -1)
|
||||
} else if err := acc.Decrypt(pass); err != nil {
|
||||
return cli.NewExitError("invalid passphrase", -1)
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
address.Prefix = address.NEO3Prefix
|
||||
newAcc, err := wallet.NewAccountFromWIF(acc.PrivateKey().WIF())
|
||||
newAcc, err := acc.convert(pass)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't convert account: %w", err), -1)
|
||||
}
|
||||
newAcc.Address = address.Uint160ToString(acc.Contract.ScriptHash())
|
||||
newAcc.Contract = acc.Contract
|
||||
newAcc.Default = acc.Default
|
||||
newAcc.Label = acc.Label
|
||||
newAcc.Locked = acc.Locked
|
||||
if err := newAcc.Encrypt(pass); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't encrypt converted account: %w", err), -1)
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
newWallet.AddAccount(newAcc)
|
||||
}
|
||||
|
|
|
@ -286,10 +286,73 @@ func TestWalletDump(t *testing.T) {
|
|||
e := newExecutor(t, false)
|
||||
defer e.Close(t)
|
||||
|
||||
e.Run(t, "neo-go", "wallet", "dump", "--wallet", "testdata/testwallet.json")
|
||||
cmd := []string{"neo-go", "wallet", "dump", "--wallet", "testdata/testwallet.json"}
|
||||
e.Run(t, cmd...)
|
||||
rawStr := strings.TrimSpace(e.Out.String())
|
||||
w := new(wallet.Wallet)
|
||||
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
||||
require.Equal(t, 1, len(w.Accounts))
|
||||
require.Equal(t, "NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM", w.Accounts[0].Address)
|
||||
|
||||
t.Run("with decrypt", func(t *testing.T) {
|
||||
cmd = append(cmd, "--decrypt")
|
||||
t.Run("invalid password", func(t *testing.T) {
|
||||
e.In.WriteString("invalidpass\r")
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
e.In.WriteString("testpass\r")
|
||||
e.Run(t, cmd...)
|
||||
rawStr := strings.TrimSpace(e.Out.String())
|
||||
w := new(wallet.Wallet)
|
||||
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
||||
require.Equal(t, 1, len(w.Accounts))
|
||||
require.Equal(t, "NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM", w.Accounts[0].Address)
|
||||
})
|
||||
}
|
||||
|
||||
// Testcase is the wallet of privnet validator.
|
||||
func TestWalletConvert(t *testing.T) {
|
||||
tmpDir := path.Join(os.TempDir(), "neogo.test.convert")
|
||||
require.NoError(t, os.Mkdir(tmpDir, os.ModePerm))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
e := newExecutor(t, false)
|
||||
defer e.Close(t)
|
||||
|
||||
outPath := path.Join(tmpDir, "wallet.json")
|
||||
cmd := []string{"neo-go", "wallet", "convert"}
|
||||
t.Run("missing wallet", func(t *testing.T) {
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "--wallet", "testdata/wallets/testwallet_NEO2.json", "--out", outPath)
|
||||
t.Run("invalid password", func(t *testing.T) {
|
||||
// missing password
|
||||
e.RunWithError(t, cmd...)
|
||||
// invalid password
|
||||
e.In.WriteString("two\r")
|
||||
e.RunWithError(t, cmd...)
|
||||
})
|
||||
|
||||
// 2 accounts.
|
||||
e.In.WriteString("one\r")
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "wallet", "convert",
|
||||
"--wallet", "testdata/wallets/testwallet_NEO2.json",
|
||||
"--out", outPath)
|
||||
|
||||
actual, err := wallet.NewWalletFromFile(outPath)
|
||||
require.NoError(t, err)
|
||||
expected, err := wallet.NewWalletFromFile("testdata/wallets/testwallet_NEO3.json")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(actual.Accounts), len(expected.Accounts))
|
||||
for _, exp := range expected.Accounts {
|
||||
addr, err := address.StringToUint160(exp.Address)
|
||||
require.NoError(t, err)
|
||||
|
||||
act := actual.GetAccount(addr)
|
||||
require.NotNil(t, act)
|
||||
require.Equal(t, exp, act)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -879,8 +879,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
for _, p := range n.Args[1:] {
|
||||
params = append(params, c.scTypeFromExpr(p))
|
||||
}
|
||||
name := constant.StringVal(tv.Value)
|
||||
c.emittedEvents[name] = append(c.emittedEvents[name], params)
|
||||
// Sometimes event name is stored in a var.
|
||||
// Skip in this case.
|
||||
if tv.Value != nil {
|
||||
name := constant.StringVal(tv.Value)
|
||||
c.emittedEvents[name] = append(c.emittedEvents[name], params)
|
||||
}
|
||||
}
|
||||
c.convertSyscall(n, f.pkg.Name(), f.name)
|
||||
default:
|
||||
|
|
|
@ -195,8 +195,7 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
priv0 := testchain.PrivateKeyByID(0)
|
||||
priv1 := testchain.PrivateKeyByID(1)
|
||||
priv0ScriptHash := priv0.GetScriptHash()
|
||||
acc0, err := wallet.NewAccountFromWIF(priv0.WIF())
|
||||
require.NoError(t, err)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||
|
||||
// Prepare some transaction for future submission.
|
||||
txSendRaw := newNEP17Transfer(bc.contracts.NEO.Hash, priv0ScriptHash, priv1.GetScriptHash(), int64(util.Fixed8FromInt64(1000)))
|
||||
|
@ -268,8 +267,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
|||
b.Header().EncodeBinary(buf.BinWriter)
|
||||
t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
|
||||
|
||||
acc0, err := wallet.NewAccountFromWIF(priv0.WIF())
|
||||
require.NoError(t, err)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||
|
||||
// Push some contract into the chain.
|
||||
txDeploy, cHash := newDeployTx(t, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
|
||||
|
|
|
@ -132,16 +132,14 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
t.Run("Contract", func(t *testing.T) {
|
||||
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
|
||||
priv := testchain.PrivateKeyByID(0)
|
||||
acc1, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc1 := wallet.NewAccountFromPrivateKey(priv)
|
||||
acc1.Contract.Deployed = true
|
||||
acc1.Contract.Script, _ = hex.DecodeString(verifyContractAVM)
|
||||
h, _ := util.Uint160DecodeStringLE(verifyContractHash)
|
||||
tx.ValidUntilBlock = chain.BlockHeight() + 10
|
||||
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
acc0, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv)
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: acc0.PrivateKey().GetScriptHash(),
|
||||
|
@ -173,8 +171,7 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1))
|
||||
})
|
||||
t.Run("InvalidContract", func(t *testing.T) {
|
||||
acc0, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv)
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: acc0.PrivateKey().GetScriptHash(),
|
||||
|
@ -200,8 +197,7 @@ func TestSignAndPushInvocationTx(t *testing.T) {
|
|||
require.NoError(t, c.Init())
|
||||
|
||||
priv := testchain.PrivateKey(0)
|
||||
acc, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc, 30, 0, []transaction.Signer{{
|
||||
Account: priv.GetScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
|
@ -239,8 +235,7 @@ func TestCreateTxFromScript(t *testing.T) {
|
|||
require.NoError(t, c.Init())
|
||||
|
||||
priv := testchain.PrivateKey(0)
|
||||
acc, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||
t.Run("NoSystemFee", func(t *testing.T) {
|
||||
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10)
|
||||
require.NoError(t, err)
|
||||
|
@ -269,8 +264,7 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
|||
require.NoError(t, c.Init())
|
||||
|
||||
priv := testchain.PrivateKeyByID(0)
|
||||
acc, err := wallet.NewAccountFromWIF(priv.WIF())
|
||||
require.NoError(t, err)
|
||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||
|
||||
gasContractHash, err := c.GetNativeContractHash("gas")
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -996,8 +996,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
})
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
acc0, err := wallet.NewAccountFromWIF(priv0.WIF())
|
||||
require.NoError(t, err)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||
|
||||
addNetworkFee := func(tx *transaction.Transaction) {
|
||||
size := io.GetVarSize(tx)
|
||||
|
|
|
@ -90,7 +90,7 @@ func NewAccount() (*Account, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newAccountFromPrivateKey(priv), nil
|
||||
return NewAccountFromPrivateKey(priv), nil
|
||||
}
|
||||
|
||||
// SignTx signs transaction t and updates it's Witnesses.
|
||||
|
@ -172,7 +172,7 @@ func NewAccountFromWIF(wif string) (*Account, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newAccountFromPrivateKey(privKey), nil
|
||||
return NewAccountFromPrivateKey(privKey), nil
|
||||
}
|
||||
|
||||
// NewAccountFromEncryptedWIF creates a new Account from the given encrypted WIF.
|
||||
|
@ -182,7 +182,7 @@ func NewAccountFromEncryptedWIF(wif string, pass string) (*Account, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
a := newAccountFromPrivateKey(priv)
|
||||
a := NewAccountFromPrivateKey(priv)
|
||||
a.EncryptedWIF = wif
|
||||
|
||||
return a, nil
|
||||
|
@ -216,8 +216,8 @@ func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// newAccountFromPrivateKey creates a wallet from the given PrivateKey.
|
||||
func newAccountFromPrivateKey(p *keys.PrivateKey) *Account {
|
||||
// NewAccountFromPrivateKey creates a wallet from the given PrivateKey.
|
||||
func NewAccountFromPrivateKey(p *keys.PrivateKey) *Account {
|
||||
pubKey := p.PublicKey()
|
||||
pubAddr := p.Address()
|
||||
wif := p.WIF()
|
||||
|
|
Loading…
Reference in a new issue