Skip to content

Commit

Permalink
wallet: make --wif optional in import-multisig command
Browse files Browse the repository at this point in the history
We have a full list of public keys in the import-multisig command, so if
 we have a key in the wallet that corresponds to one of these keys
 (simple sig), just reuse it for the account automatically

Closes #3266

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
  • Loading branch information
AliceInHunterland committed Jan 26, 2024
1 parent 36b8921 commit 57aa05a
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 4 deletions.
52 changes: 49 additions & 3 deletions cli/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,15 @@ func NewCommands() []cli.Command {
{
Name: "import-multisig",
Usage: "import multisig contract",
UsageText: "import-multisig -w wallet [--wallet-config path] --wif <wif> [--name <account_name>] --min <n>" +
UsageText: "import-multisig -w wallet [--wallet-config path] [--wif <wif>] [--name <account_name>] --min <m>" +
" [<pubkey1> [<pubkey2> [...]]]",
Description: `Imports a standard multisignature contract with "m out of n" signatures required where "m" is
specified by --min flag and "n" is the length of provided set of public keys. If
--wif flag is provided, it's used to create an account with the given name (or
without a name if --name flag is not provided). Otherwise, the command tries to
find an account with one of the given public keys and convert it to multisig. If
no suitable account is found and no --wif flag is specified, an error is returned.
`,
Action: importMultisig,
Flags: []cli.Flag{
walletPathFlag,
Expand Down Expand Up @@ -521,6 +528,12 @@ loop:
}

func importMultisig(ctx *cli.Context) error {
var (
label *string
acc *wallet.Account
accPub *keys.PublicKey
)

wall, pass, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
Expand All @@ -542,12 +555,45 @@ func importMultisig(ctx *cli.Context) error {
}
}

var label *string
if ctx.IsSet("name") {
l := ctx.String("name")
label = &l
}
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)

loop:
for _, pub := range pubs {
for _, wallAcc := range wall.Accounts {
if wallAcc.ScriptHash().Equals(pub.GetScriptHash()) {
if acc != nil {
// Multiple matching accounts found, fallback to WIF-based conversion.
acc = nil
break loop
}
acc = new(wallet.Account)
*acc = *wallAcc
accPub = pub
}
}
}

if acc != nil {
err = acc.ConvertMultisigEncrypted(accPub, m, pubs)
if err != nil {
return cli.NewExitError(err, 1)
}
if label != nil {
acc.Label = *label
}
if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1)
}
return nil
}

if !ctx.IsSet("wif") {
return cli.NewExitError(errors.New("none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided"), 1)
}
acc, err = newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil {
return cli.NewExitError(err, 1)
}
Expand Down
52 changes: 52 additions & 0 deletions cli/wallet/wallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,58 @@ func TestWalletInit(t *testing.T) {
hex.EncodeToString(pubs[2].Bytes()),
hex.EncodeToString(pubs[3].Bytes()))...)
})

privs, pubs = testcli.GenerateKeys(t, 3)
script, err = smartcontract.CreateMultiSigRedeemScript(2, pubs)
require.NoError(t, err)
// Create a wallet and import a standard account
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
e.In.WriteString("standardacc\rstdpass\rstdpass\r")
e.Run(t, "neo-go", "wallet", "import",
"--wallet", walletPath,
"--wif", privs[0].WIF())
w, err = wallet.NewWalletFromFile(walletPath)
require.NoError(t, err)
actual = w.GetAccount(privs[0].GetScriptHash())
require.NotNil(t, actual)
require.NotEqual(t, actual.Contract.Script, script)

// Test when a public key of an already imported account is present
t.Run("existing account public key, no WIF", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "import-multisig",
"--wallet", walletPath,
"--min", "2",
hex.EncodeToString(pubs[0].Bytes()), // Public key of the already imported account
hex.EncodeToString(pubs[1].Bytes()),
hex.EncodeToString(pubs[2].Bytes()))

w, err := wallet.NewWalletFromFile(walletPath)
require.NoError(t, err)
actual := w.GetAccount(hash.Hash160(script))
require.NotNil(t, actual)
require.Equal(t, actual.Contract.Script, script)
require.NoError(t, actual.Decrypt("stdpass", w.Scrypt))
require.NotEqual(t, actual.Address, w.GetAccount(privs[0].GetScriptHash()).Address)
})

// Test when no public key of an already imported account is present, and no WIF is provided
t.Run("no existing account public key, no WIF", func(t *testing.T) {
_, pubsNew := testcli.GenerateKeys(t, 3)
scriptNew, err := smartcontract.CreateMultiSigRedeemScript(2, pubsNew)
require.NoError(t, err)

e.RunWithError(t, "neo-go", "wallet", "import-multisig",
"--wallet", walletPath,
"--min", "2",
hex.EncodeToString(pubsNew[0].Bytes()),
hex.EncodeToString(pubsNew[1].Bytes()),
hex.EncodeToString(pubsNew[2].Bytes()))

w, err := wallet.NewWalletFromFile(walletPath)
require.NoError(t, err)
actual := w.GetAccount(hash.Hash160(scriptNew))
require.Nil(t, actual)
})
})
})
}
Expand Down
9 changes: 8 additions & 1 deletion pkg/wallet/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,15 @@ func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
if a.privateKey == nil {
return errors.New("account key is not available (need to decrypt?)")
}
var found bool
accKey := a.privateKey.PublicKey()
return a.ConvertMultisigEncrypted(accKey, m, pubs)
}

// ConvertMultisigEncrypted sets a's contract to an encrypted multisig contract
// with m sufficient signatures. The encrypted private key is not modified and
// remains the same.
func (a *Account) ConvertMultisigEncrypted(accKey *keys.PublicKey, m int, pubs []*keys.PublicKey) error {
var found bool
for i := range pubs {
if accKey.Equal(pubs[i]) {
found = true
Expand Down

0 comments on commit 57aa05a

Please sign in to comment.