diff --git a/cmd/wallet.go b/cmd/wallet.go index a19d22b..795e146 100644 --- a/cmd/wallet.go +++ b/cmd/wallet.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcutil/base58" "github.com/hashicorp/go-secure-stdlib/password" "github.com/jedib0t/go-pretty/v6/table" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spf13/cobra" "github.com/spacemeshos/smcli/common" @@ -36,6 +37,9 @@ var ( // useLedger indicates that the Ledger device should be used. useLedger bool + + // hrp is the human-readable network identifier used in Spacemesh network addresses. + hrp string ) // walletCmd represents the wallet command. @@ -196,10 +200,9 @@ only child keys).`, // full key is 64 bytes which is 128 chars in hex, need to print at least this much maxWidth = 150 } - // TODO: add spacemesh address format (bech32) - // https://github.com/spacemeshos/smcli/issues/38 if printPrivate { t.AppendHeader(table.Row{ + "address", "pubkey", "privkey", "path", @@ -207,18 +210,19 @@ only child keys).`, "created", }) t.SetColumnConfigs([]table.ColumnConfig{ - {Number: 1, WidthMax: maxWidth, WidthMaxEnforcer: widthEnforcer}, {Number: 2, WidthMax: maxWidth, WidthMaxEnforcer: widthEnforcer}, + {Number: 3, WidthMax: maxWidth, WidthMaxEnforcer: widthEnforcer}, }) } else { t.AppendHeader(table.Row{ + "address", "pubkey", "path", "name", "created", }) t.SetColumnConfigs([]table.ColumnConfig{ - {Number: 1, WidthMax: maxWidth, WidthMaxEnforcer: widthEnforcer}, + {Number: 2, WidthMax: maxWidth, WidthMaxEnforcer: widthEnforcer}, }) } @@ -241,6 +245,7 @@ only child keys).`, if master != nil { if printPrivate { t.AppendRow(table.Row{ + "N/A", encoder(master.Public), privKeyEncoder(master.Private), master.Path.String(), @@ -249,6 +254,7 @@ only child keys).`, }) } else { t.AppendRow(table.Row{ + "N/A", encoder(master.Public), master.Path.String(), master.DisplayName, @@ -262,6 +268,7 @@ only child keys).`, for _, a := range w.Secrets.Accounts { if printPrivate { t.AppendRow(table.Row{ + wallet.PubkeyToAddress(a.Public, hrp), encoder(a.Public), privKeyEncoder(a.Private), a.Path.String(), @@ -270,6 +277,7 @@ only child keys).`, }) } else { t.AppendRow(table.Row{ + wallet.PubkeyToAddress(a.Public, hrp), encoder(a.Public), a.Path.String(), a.DisplayName, @@ -289,6 +297,7 @@ func init() { readCmd.Flags().BoolVarP(&printFull, "full", "f", false, "Print full keys (no abbreviation)") readCmd.Flags().BoolVar(&printBase58, "base58", false, "Print keys in base58 (rather than hex)") readCmd.Flags().BoolVar(&printParent, "parent", false, "Print parent key (not only child keys)") + readCmd.Flags().StringVar(&hrp, "hrp", types.NetworkHRP(), "Set human-readable address prefix") readCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "enable debug mode") createCmd.Flags().BoolVarP(&useLedger, "ledger", "l", false, "Create a wallet using a Ledger device") } diff --git a/wallet/wallet.go b/wallet/wallet.go index a91d45a..5849d21 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -7,6 +7,9 @@ import ( "fmt" "strings" + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/genvm/core" + walletTemplate "github.com/spacemeshos/go-spacemesh/genvm/templates/wallet" "github.com/tyler-smith/go-bip39" "github.com/spacemeshos/smcli/common" @@ -170,3 +173,12 @@ func accountsFromMaster(masterKeypair *EDKeyPair, masterSeed []byte, n int) (acc func (w *Wallet) Mnemonic() string { return w.Secrets.Mnemonic } + +func PubkeyToAddress(pubkey []byte, hrp string) string { + types.SetNetworkHRP(hrp) + key := [ed25519.PublicKeySize]byte{} + copy(key[:], pubkey) + walletArgs := &walletTemplate.SpawnArguments{PublicKey: key} + walletAddress := core.ComputePrincipal(walletTemplate.TemplateAddress, walletArgs) + return walletAddress.String() +} diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index 5c46a98..663c82a 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/stretchr/testify/require" "github.com/tyler-smith/go-bip39" ) @@ -88,6 +89,15 @@ func TestWalletFromGivenMnemonic(t *testing.T) { // Sanity check that the keypair works with the standard ed25519 library sig := ed25519.Sign(ed25519.PrivateKey(w.Secrets.Accounts[0].Private), msg) require.True(t, ed25519.Verify(ed25519.PublicKey(w.Secrets.Accounts[0].Public), msg, sig)) + + // Test conversion to a Spacemesh wallet address + expAddress := "sm1qqqqqqz9rf583slhn38g6q6a562ctltv9fv5w8q2gdz9k" + address := PubkeyToAddress(w.Secrets.Accounts[0].Public, types.NetworkHRP()) + require.Equal(t, expAddress, address) + + expAddressTestnet := "stest1qqqqqqz9rf583slhn38g6q6a562ctltv9fv5w8qha56t0" + addressTestnet := PubkeyToAddress(w.Secrets.Accounts[0].Public, "stest") + require.Equal(t, expAddressTestnet, addressTestnet) } func TestKeysInWalletMaintainExpectedPath(t *testing.T) {