Merge pull request #2100 from nspcc-dev/cli-compiler-groups
cli/smartcontract: provide manifest groups on deploy
This commit is contained in:
commit
2329e25d1e
6 changed files with 256 additions and 47 deletions
|
@ -227,6 +227,47 @@ func TestContractDeployWithData(t *testing.T) {
|
||||||
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
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 {
|
func deployVerifyContract(t *testing.T, e *executor) util.Uint160 {
|
||||||
return deployContract(t, e, "testdata/verify.go", "testdata/verify.yml", validatorWallet, validatorAddr, "one")
|
return deployContract(t, e, "testdata/verify.go", "testdata/verify.yml", validatorWallet, validatorAddr, "one")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ const (
|
||||||
validatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP"
|
validatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP"
|
||||||
multisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq"
|
multisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq"
|
||||||
|
|
||||||
|
testWalletPath = "testdata/testwallet.json"
|
||||||
|
testWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG"
|
||||||
|
|
||||||
validatorWallet = "testdata/wallet1_solo.json"
|
validatorWallet = "testdata/wallet1_solo.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -560,27 +590,33 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||||
return exitErr
|
return exitErr
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = invokeWithArgs(ctx, signAndPush, script, operation, params, cosigners)
|
var (
|
||||||
|
acc *wallet.Account
|
||||||
|
w *wallet.Wallet
|
||||||
|
)
|
||||||
|
if signAndPush {
|
||||||
|
acc, w, err = getAccFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = invokeWithArgs(ctx, acc, w, script, operation, params, cosigners)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeWithArgs(ctx *cli.Context, signAndPush bool, script util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Signer) (util.Uint160, error) {
|
func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Signer) (util.Uint160, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
gas, sysgas fixedn.Fixed8
|
gas, sysgas fixedn.Fixed8
|
||||||
cosignersAccounts []client.SignerAccount
|
cosignersAccounts []client.SignerAccount
|
||||||
resp *result.Invoke
|
resp *result.Invoke
|
||||||
acc *wallet.Account
|
|
||||||
wall *wallet.Wallet
|
|
||||||
sender util.Uint160
|
sender util.Uint160
|
||||||
|
signAndPush = acc != nil
|
||||||
)
|
)
|
||||||
if signAndPush {
|
if signAndPush {
|
||||||
gas = flags.Fixed8FromContext(ctx, "gas")
|
gas = flags.Fixed8FromContext(ctx, "gas")
|
||||||
sysgas = flags.Fixed8FromContext(ctx, "sysgas")
|
sysgas = flags.Fixed8FromContext(ctx, "sysgas")
|
||||||
acc, wall, err = getAccFromContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return sender, err
|
|
||||||
}
|
|
||||||
sender, err = address.StringToUint160(acc.Address)
|
sender, err = address.StringToUint160(acc.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sender, err
|
return sender, err
|
||||||
|
@ -744,54 +780,45 @@ func getAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error
|
||||||
} else {
|
} else {
|
||||||
addr = wall.GetChangeAddress()
|
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)
|
acc := wall.GetAccount(addr)
|
||||||
if acc == nil {
|
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(
|
rawPass, err := input.ReadPassword(
|
||||||
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
|
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cli.NewExitError(err, 1)
|
return nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
pass := strings.TrimRight(string(rawPass), "\n")
|
pass := strings.TrimRight(string(rawPass), "\n")
|
||||||
err = acc.Decrypt(pass, wall.Scrypt)
|
err = acc.Decrypt(pass, wall.Scrypt)
|
||||||
if err != nil {
|
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.
|
// contractDeploy deploys contract.
|
||||||
func contractDeploy(ctx *cli.Context) error {
|
func contractDeploy(ctx *cli.Context) error {
|
||||||
in := ctx.String("in")
|
nefFile, f, err := readNEFFile(ctx.String("in"))
|
||||||
if len(in) == 0 {
|
|
||||||
return cli.NewExitError(errNoInput, 1)
|
|
||||||
}
|
|
||||||
manifestFile := ctx.String("manifest")
|
|
||||||
if len(manifestFile) == 0 {
|
|
||||||
return cli.NewExitError(errNoManifestFile, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := ioutil.ReadFile(in)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
// Check the file.
|
|
||||||
nefFile, err := nef.FileFromBytes(f)
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestBytes, err := ioutil.ReadFile(manifestFile)
|
m, manifestBytes, err := readManifest(ctx.String("manifest"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
|
||||||
}
|
}
|
||||||
m := &manifest.Manifest{}
|
|
||||||
err = json.Unmarshal(manifestBytes, m)
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("failed to restore manifest file: %w", err), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
appCallParams := []smartcontract.Parameter{
|
appCallParams := []smartcontract.Parameter{
|
||||||
{
|
{
|
||||||
|
@ -826,7 +853,18 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
|
||||||
}
|
}
|
||||||
sender, extErr := invokeWithArgs(ctx, true, mgmtHash, "deploy", appCallParams, nil)
|
|
||||||
|
acc, w, err := getAccFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cosigners := []transaction.Signer{{
|
||||||
|
Account: acc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
}}
|
||||||
|
|
||||||
|
sender, extErr := invokeWithArgs(ctx, acc, w, mgmtHash, "deploy", appCallParams, cosigners)
|
||||||
if extErr != nil {
|
if extErr != nil {
|
||||||
return extErr
|
return extErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,24 +228,19 @@ func TestWalletExport(t *testing.T) {
|
||||||
func TestClaimGas(t *testing.T) {
|
func TestClaimGas(t *testing.T) {
|
||||||
e := newExecutor(t, true)
|
e := newExecutor(t, true)
|
||||||
|
|
||||||
const walletPath = "testdata/testwallet.json"
|
|
||||||
w, err := wallet.NewWalletFromFile(walletPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(w.Close)
|
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"neo-go", "wallet", "nep17", "multitransfer",
|
"neo-go", "wallet", "nep17", "multitransfer",
|
||||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"NEO:" + w.Accounts[0].Address + ":1000",
|
"NEO:" + testWalletAccount + ":1000",
|
||||||
"GAS:" + w.Accounts[0].Address + ":1000", // for tx send
|
"GAS:" + testWalletAccount + ":1000", // for tx send
|
||||||
}
|
}
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, args...)
|
e.Run(t, args...)
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
h, err := address.StringToUint160(w.Accounts[0].Address)
|
h, err := address.StringToUint160(testWalletAccount)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
balanceBefore := e.Chain.GetUtilityTokenBalance(h)
|
balanceBefore := e.Chain.GetUtilityTokenBalance(h)
|
||||||
|
@ -257,8 +252,8 @@ func TestClaimGas(t *testing.T) {
|
||||||
e.In.WriteString("testpass\r")
|
e.In.WriteString("testpass\r")
|
||||||
e.Run(t, "neo-go", "wallet", "claim",
|
e.Run(t, "neo-go", "wallet", "claim",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", walletPath,
|
"--wallet", testWalletPath,
|
||||||
"--address", w.Accounts[0].Address)
|
"--address", testWalletAccount)
|
||||||
tx, height := e.checkTxPersisted(t)
|
tx, height := e.checkTxPersisted(t)
|
||||||
balanceBefore.Sub(balanceBefore, big.NewInt(tx.NetworkFee+tx.SystemFee))
|
balanceBefore.Sub(balanceBefore, big.NewInt(tx.NetworkFee+tx.SystemFee))
|
||||||
balanceBefore.Add(balanceBefore, cl)
|
balanceBefore.Add(balanceBefore, cl)
|
||||||
|
@ -337,13 +332,13 @@ func TestImportDeployed(t *testing.T) {
|
||||||
func TestWalletDump(t *testing.T) {
|
func TestWalletDump(t *testing.T) {
|
||||||
e := newExecutor(t, false)
|
e := newExecutor(t, false)
|
||||||
|
|
||||||
cmd := []string{"neo-go", "wallet", "dump", "--wallet", "testdata/testwallet.json"}
|
cmd := []string{"neo-go", "wallet", "dump", "--wallet", testWalletPath}
|
||||||
e.Run(t, cmd...)
|
e.Run(t, cmd...)
|
||||||
rawStr := strings.TrimSpace(e.Out.String())
|
rawStr := strings.TrimSpace(e.Out.String())
|
||||||
w := new(wallet.Wallet)
|
w := new(wallet.Wallet)
|
||||||
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
||||||
require.Equal(t, 1, len(w.Accounts))
|
require.Equal(t, 1, len(w.Accounts))
|
||||||
require.Equal(t, "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG", w.Accounts[0].Address)
|
require.Equal(t, testWalletAccount, w.Accounts[0].Address)
|
||||||
|
|
||||||
t.Run("with decrypt", func(t *testing.T) {
|
t.Run("with decrypt", func(t *testing.T) {
|
||||||
cmd = append(cmd, "--decrypt")
|
cmd = append(cmd, "--decrypt")
|
||||||
|
@ -358,7 +353,7 @@ func TestWalletDump(t *testing.T) {
|
||||||
w := new(wallet.Wallet)
|
w := new(wallet.Wallet)
|
||||||
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
require.NoError(t, json.Unmarshal([]byte(rawStr), w))
|
||||||
require.Equal(t, 1, len(w.Accounts))
|
require.Equal(t, 1, len(w.Accounts))
|
||||||
require.Equal(t, "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG", w.Accounts[0].Address)
|
require.Equal(t, testWalletAccount, w.Accounts[0].Address)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,6 +276,16 @@ Using either constant or literal for contract hash and method will allow compile
|
||||||
to perform more extensive analysis.
|
to perform more extensive analysis.
|
||||||
This check can be disabled with `--no-permissions` flag.
|
This check can be disabled with `--no-permissions` flag.
|
||||||
|
|
||||||
|
#### Manifest file
|
||||||
|
Any contract can be included in a group identified by a public key which is used in [permissions](#Permissions).
|
||||||
|
This is achieved with `manifest add-group` command.
|
||||||
|
```
|
||||||
|
./bin/neo-go contract manifest add-group -n contract.nef -m contract.manifest.json --sender <sender> --wallet /path/to/wallet.json --account <account>
|
||||||
|
```
|
||||||
|
It accepts contract `.nef` and manifest files emitted by `compile` command as well as
|
||||||
|
sender and signer accounts. `--sender` is the account who will send deploy transaction later (not necessarily in wallet).
|
||||||
|
`--account` is the wallet account which signs contract hash using group private key.
|
||||||
|
|
||||||
#### Neo Express support
|
#### Neo Express support
|
||||||
|
|
||||||
It's possible to deploy contracts written in Go using [Neo
|
It's possible to deploy contracts written in Go using [Neo
|
||||||
|
|
Loading…
Reference in a new issue