forked from TrueCloudLab/neoneo-go
Merge pull request #1811 from nspcc-dev/feature/pubdump
Allow to dump public keys in CLI
This commit is contained in:
commit
9ca59e6312
4 changed files with 128 additions and 5 deletions
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -120,6 +121,18 @@ func NewCommands() []cli.Command {
|
||||||
decryptFlag,
|
decryptFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "dump-keys",
|
||||||
|
Usage: "dump public keys for account",
|
||||||
|
Action: dumpKeys,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
walletPathFlag,
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "address to print public keys for",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "export",
|
Name: "export",
|
||||||
Usage: "export keys for address",
|
Usage: "export keys for address",
|
||||||
|
@ -537,6 +550,56 @@ func dumpWallet(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dumpKeys(ctx *cli.Context) error {
|
||||||
|
wall, err := openWallet(ctx.String("wallet"))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
accounts := wall.Accounts
|
||||||
|
addr := ctx.String("address")
|
||||||
|
if addr != "" {
|
||||||
|
u, err := flags.ParseAddress(addr)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("invalid address: %w", err), 1)
|
||||||
|
}
|
||||||
|
acc := wall.GetAccount(u)
|
||||||
|
if acc == nil {
|
||||||
|
return cli.NewExitError("account is missing", 1)
|
||||||
|
}
|
||||||
|
accounts = []*wallet.Account{acc}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPrinted := false
|
||||||
|
for _, acc := range accounts {
|
||||||
|
pub, ok := vm.ParseSignatureContract(acc.Contract.Script)
|
||||||
|
if ok {
|
||||||
|
if hasPrinted {
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "%s (simple signature contract):\n", acc.Address)
|
||||||
|
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(pub))
|
||||||
|
hasPrinted = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n, bs, ok := vm.ParseMultiSigContract(acc.Contract.Script)
|
||||||
|
if ok {
|
||||||
|
if hasPrinted {
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "%s (%d out of %d multisig contract):\n", acc.Address, n, len(bs))
|
||||||
|
for i := range bs {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(bs[i]))
|
||||||
|
}
|
||||||
|
hasPrinted = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr != "" {
|
||||||
|
return cli.NewExitError(fmt.Errorf("Unknown script type for address %s", addr), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func createWallet(ctx *cli.Context) error {
|
func createWallet(ctx *cli.Context) error {
|
||||||
path := ctx.String("wallet")
|
path := ctx.String("wallet")
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
|
|
|
@ -337,6 +337,49 @@ func TestWalletDump(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDumpKeys(t *testing.T) {
|
||||||
|
e := newExecutor(t, false)
|
||||||
|
cmd := []string{"neo-go", "wallet", "dump-keys", "--wallet", validatorWallet}
|
||||||
|
pubRegex := "^0[23][a-hA-H0-9]{64}$"
|
||||||
|
t.Run("all", func(t *testing.T) {
|
||||||
|
e.Run(t, cmd...)
|
||||||
|
e.checkNextLine(t, "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
e.checkNextLine(t, "^\\s*$")
|
||||||
|
e.checkNextLine(t, "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY")
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
}
|
||||||
|
e.checkNextLine(t, "^\\s*$")
|
||||||
|
e.checkNextLine(t, "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK")
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
e.checkEOF(t)
|
||||||
|
})
|
||||||
|
t.Run("simple signature", func(t *testing.T) {
|
||||||
|
cmd := append(cmd, "--address", "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
|
||||||
|
e.Run(t, cmd...)
|
||||||
|
e.checkNextLine(t, "simple signature contract")
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
e.checkEOF(t)
|
||||||
|
})
|
||||||
|
t.Run("3/4 multisig", func(t *testing.T) {
|
||||||
|
cmd := append(cmd, "-a", "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY")
|
||||||
|
e.Run(t, cmd...)
|
||||||
|
e.checkNextLine(t, "3 out of 4 multisig contract")
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
}
|
||||||
|
e.checkEOF(t)
|
||||||
|
})
|
||||||
|
t.Run("1/1 multisig", func(t *testing.T) {
|
||||||
|
cmd := append(cmd, "--address", "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK")
|
||||||
|
e.Run(t, cmd...)
|
||||||
|
e.checkNextLine(t, "1 out of 1 multisig contract")
|
||||||
|
e.checkNextLine(t, pubRegex)
|
||||||
|
e.checkEOF(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Testcase is the wallet of privnet validator.
|
// Testcase is the wallet of privnet validator.
|
||||||
func TestWalletConvert(t *testing.T) {
|
func TestWalletConvert(t *testing.T) {
|
||||||
tmpDir := path.Join(os.TempDir(), "neogo.test.convert")
|
tmpDir := path.Join(os.TempDir(), "neogo.test.convert")
|
||||||
|
|
|
@ -105,24 +105,32 @@ func ParseMultiSigContract(script []byte) (int, [][]byte, bool) {
|
||||||
// IsSignatureContract checks whether the passed script is a signature check
|
// IsSignatureContract checks whether the passed script is a signature check
|
||||||
// contract.
|
// contract.
|
||||||
func IsSignatureContract(script []byte) bool {
|
func IsSignatureContract(script []byte) bool {
|
||||||
|
_, ok := ParseSignatureContract(script)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignatureContract parses simple signature contract and returns
|
||||||
|
// public key.
|
||||||
|
func ParseSignatureContract(script []byte) ([]byte, bool) {
|
||||||
if len(script) != 41 {
|
if len(script) != 41 {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := NewContext(script)
|
ctx := NewContext(script)
|
||||||
instr, param, err := ctx.Next()
|
instr, param, err := ctx.Next()
|
||||||
if err != nil || instr != opcode.PUSHDATA1 || len(param) != 33 {
|
if err != nil || instr != opcode.PUSHDATA1 || len(param) != 33 {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
pub := param
|
||||||
instr, _, err = ctx.Next()
|
instr, _, err = ctx.Next()
|
||||||
if err != nil || instr != opcode.PUSHNULL {
|
if err != nil || instr != opcode.PUSHNULL {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
instr, param, err = ctx.Next()
|
instr, param, err = ctx.Next()
|
||||||
if err != nil || instr != opcode.SYSCALL || binary.LittleEndian.Uint32(param) != verifyInteropID {
|
if err != nil || instr != opcode.SYSCALL || binary.LittleEndian.Uint32(param) != verifyInteropID {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
return true
|
return pub, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStandardContract checks whether the passed script is a signature or
|
// IsStandardContract checks whether the passed script is a signature or
|
||||||
|
|
|
@ -25,6 +25,15 @@ func testSignatureContract() []byte {
|
||||||
return prog
|
return prog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseSignatureContract(t *testing.T) {
|
||||||
|
prog := testSignatureContract()
|
||||||
|
pub := randomBytes(33)
|
||||||
|
copy(prog[2:], pub)
|
||||||
|
actual, ok := ParseSignatureContract(prog)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, pub, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsSignatureContract(t *testing.T) {
|
func TestIsSignatureContract(t *testing.T) {
|
||||||
t.Run("valid contract", func(t *testing.T) {
|
t.Run("valid contract", func(t *testing.T) {
|
||||||
prog := testSignatureContract()
|
prog := testSignatureContract()
|
||||||
|
|
Loading…
Reference in a new issue