cli/smartcontract: add manifest add-group
command
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
6fe2ace43b
commit
868198a36e
3 changed files with 207 additions and 4 deletions
|
@ -227,6 +227,47 @@ func TestContractDeployWithData(t *testing.T) {
|
|||
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
||||
}
|
||||
|
||||
func TestContractManifestGroups(t *testing.T) {
|
||||
e := newExecutor(t, true)
|
||||
|
||||
// For proper nef generation.
|
||||
config.Version = "0.90.0-test"
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "neogo.test.deployfail")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
os.RemoveAll(tmpDir)
|
||||
})
|
||||
|
||||
w, err := wallet.NewWalletFromFile(testWalletPath)
|
||||
require.NoError(t, err)
|
||||
defer w.Close()
|
||||
|
||||
nefName := path.Join(tmpDir, "deploy.nef")
|
||||
manifestName := path.Join(tmpDir, "deploy.manifest.json")
|
||||
e.Run(t, "neo-go", "contract", "compile",
|
||||
"--in", "testdata/deploy/main.go", // compile single file
|
||||
"--config", "testdata/deploy/neo-go.yml",
|
||||
"--out", nefName, "--manifest", manifestName)
|
||||
|
||||
cmd := []string{"neo-go", "contract", "manifest", "add-group",
|
||||
"--nef", nefName, "--manifest", manifestName}
|
||||
|
||||
e.In.WriteString("testpass\r")
|
||||
e.Run(t, append(cmd, "--wallet", testWalletPath,
|
||||
"--sender", testWalletAccount, "--account", testWalletAccount)...)
|
||||
|
||||
e.In.WriteString("testpass\r") // should override signature with the previous sender
|
||||
e.Run(t, append(cmd, "--wallet", testWalletPath,
|
||||
"--sender", validatorAddr, "--account", testWalletAccount)...)
|
||||
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "contract", "deploy",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--in", nefName, "--manifest", manifestName,
|
||||
"--wallet", validatorWallet, "--address", validatorAddr)
|
||||
}
|
||||
|
||||
func deployVerifyContract(t *testing.T, e *executor) util.Uint160 {
|
||||
return deployContract(t, e, "testdata/verify.go", "testdata/verify.yml", validatorWallet, validatorAddr, "one")
|
||||
}
|
||||
|
|
122
cli/smartcontract/manifest.go
Normal file
122
cli/smartcontract/manifest.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func manifestAddGroup(ctx *cli.Context) error {
|
||||
walletPath := ctx.String("wallet")
|
||||
if len(walletPath) == 0 {
|
||||
return cli.NewExitError(errNoWallet, 1)
|
||||
}
|
||||
|
||||
w, err := wallet.NewWalletFromFile(walletPath)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
addr, err := flags.ParseAddress(ctx.String("account"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(errors.New("address is missing"), 1)
|
||||
}
|
||||
|
||||
sender, err := flags.ParseAddress(ctx.String("sender"))
|
||||
if err != nil {
|
||||
return cli.NewExitError("invalid sender", 1)
|
||||
}
|
||||
|
||||
nf, _, err := readNEFFile(ctx.String("nef"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't read NEF file: %w", err), 1)
|
||||
}
|
||||
|
||||
mPath := ctx.String("manifest")
|
||||
m, _, err := readManifest(mPath)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
|
||||
}
|
||||
|
||||
h := state.CreateContractHash(sender, nf.Checksum, m.Name)
|
||||
|
||||
gAcc, err := getUnlockedAccount(w, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var found bool
|
||||
|
||||
sig := gAcc.PrivateKey().Sign(h.BytesBE())
|
||||
pub := gAcc.PrivateKey().PublicKey()
|
||||
for i := range m.Groups {
|
||||
if m.Groups[i].PublicKey.Equal(pub) {
|
||||
m.Groups[i].Signature = sig
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
m.Groups = append(m.Groups, manifest.Group{
|
||||
PublicKey: pub,
|
||||
Signature: sig,
|
||||
})
|
||||
}
|
||||
|
||||
rawM, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't marshal manifest: %w", err), 1)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(mPath, rawM, os.ModePerm)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't write manifest file: %w", err), 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readNEFFile(filename string) (*nef.File, []byte, error) {
|
||||
if len(filename) == 0 {
|
||||
return nil, nil, errors.New("no nef file was provided")
|
||||
}
|
||||
|
||||
f, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
nefFile, err := nef.FileFromBytes(f)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("can't parse NEF file: %w", err)
|
||||
}
|
||||
|
||||
return &nefFile, f, nil
|
||||
}
|
||||
|
||||
func readManifest(filename string) (*manifest.Manifest, []byte, error) {
|
||||
if len(filename) == 0 {
|
||||
return nil, nil, errNoManifestFile
|
||||
}
|
||||
|
||||
manifestBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
m := new(manifest.Manifest)
|
||||
err = json.Unmarshal(manifestBytes, m)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return m, manifestBytes, nil
|
||||
}
|
|
@ -370,6 +370,36 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "manifest",
|
||||
Usage: "manifest-related commands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "add-group",
|
||||
Usage: "adds group to the manifest",
|
||||
Action: manifestAddGroup,
|
||||
Flags: []cli.Flag{
|
||||
walletFlag,
|
||||
cli.StringFlag{
|
||||
Name: "sender, s",
|
||||
Usage: "deploy transaction sender",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "account, a",
|
||||
Usage: "account to sign group with",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "nef, n",
|
||||
Usage: "path to the NEF file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "manifest, m",
|
||||
Usage: "path to the manifest",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
@ -750,22 +780,32 @@ func getAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error
|
|||
} else {
|
||||
addr = wall.GetChangeAddress()
|
||||
}
|
||||
|
||||
acc, err := getUnlockedAccount(wall, addr)
|
||||
return acc, wall, err
|
||||
}
|
||||
|
||||
func getUnlockedAccount(wall *wallet.Wallet, addr util.Uint160) (*wallet.Account, error) {
|
||||
acc := wall.GetAccount(addr)
|
||||
if acc == nil {
|
||||
return nil, nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
|
||||
return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
|
||||
}
|
||||
|
||||
if acc.PrivateKey() != nil {
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
rawPass, err := input.ReadPassword(
|
||||
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
|
||||
if err != nil {
|
||||
return nil, nil, cli.NewExitError(err, 1)
|
||||
return nil, cli.NewExitError(err, 1)
|
||||
}
|
||||
pass := strings.TrimRight(string(rawPass), "\n")
|
||||
err = acc.Decrypt(pass, wall.Scrypt)
|
||||
if err != nil {
|
||||
return nil, nil, cli.NewExitError(err, 1)
|
||||
return nil, cli.NewExitError(err, 1)
|
||||
}
|
||||
return acc, wall, nil
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
// contractDeploy deploys contract.
|
||||
|
|
Loading…
Reference in a new issue