From 36faab761dcb13c3cade1776680800a5aee15d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Thu, 18 May 2023 13:24:36 +0200 Subject: [PATCH] feat(wallet): Add sr25519-adr8 and sr25519-raw support --- wallet/file/file.go | 20 ++++++++++++---- wallet/file/sr25519.go | 53 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 wallet/file/sr25519.go diff --git a/wallet/file/file.go b/wallet/file/file.go index 4dfd1b2e..6eea1e7c 100644 --- a/wallet/file/file.go +++ b/wallet/file/file.go @@ -45,12 +45,12 @@ const ( // SupportedAlgorithmsForImport returns the algorithms supported by the given import kind. func SupportedAlgorithmsForImport(kind *wallet.ImportKind) []string { if kind == nil { - return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw} + return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Adr8, wallet.AlgorithmSr25519Raw} } switch *kind { case wallet.ImportKindMnemonic: - return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44} + return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSr25519Adr8} case wallet.ImportKindPrivateKey: return []string{wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw} default: @@ -192,7 +192,7 @@ func (af *fileAccountFactory) PrettyKind(rawCfg map[string]interface{}) string { // In case of ADR8 or BIP44 show the keypair number. var number string switch cfg.Algorithm { - case wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44: + case wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSr25519Adr8: number = fmt.Sprintf(":%d", cfg.Number) } return fmt.Sprintf("%s (%s%s)", Kind, cfg.Algorithm, number) @@ -429,7 +429,7 @@ func (af *fileAccountFactory) Import(name string, passphrase string, rawCfg map[ switch src.Kind { case wallet.ImportKindMnemonic: switch cfg.Algorithm { - case wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44: + case wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSr25519Adr8: default: return nil, fmt.Errorf("algorithm '%s' does not support import from mnemonic", cfg.Algorithm) } @@ -520,6 +520,18 @@ func newAccount(state *secretState, cfg *accountConfig) (wallet.Account, error) return nil, fmt.Errorf("failed to initialize signer: %w", err) } + return &fileAccount{ + cfg: cfg, + state: state, + signer: signer, + }, nil + case wallet.AlgorithmSr25519Adr8: + // For Sr25519 use the ADR 0008 derivation scheme. + signer, err := Sr25519FromMnemonic(state.Data, cfg.Number) + if err != nil { + return nil, fmt.Errorf("failed to initialize signer: %w", err) + } + return &fileAccount{ cfg: cfg, state: state, diff --git a/wallet/file/sr25519.go b/wallet/file/sr25519.go new file mode 100644 index 00000000..4bb27b73 --- /dev/null +++ b/wallet/file/sr25519.go @@ -0,0 +1,53 @@ +package file + +import ( + "fmt" + + "github.com/oasisprotocol/oasis-core/go/common/crypto/sakg" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/crypto/slip10" + sdkSignature "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519" + "github.com/tyler-smith/go-bip39" +) + +// Sr25519FromMnemonic derives a signer using ADR-8 from given mnemonic. +func Sr25519FromMnemonic(mnemonic string, number uint32) (sdkSignature.Signer, error) { + if number > sakg.MaxAccountKeyNumber { + return nil, fmt.Errorf( + "sakg: invalid key number: %d (maximum: %d)", + number, + sakg.MaxAccountKeyNumber, + ) + } + + if !bip39.IsMnemonicValid(mnemonic) { + return nil, fmt.Errorf("sakg: invalid mnemonic") + } + + seed := bip39.NewSeed(mnemonic, "") + + signer, chainCode, err := slip10.NewMasterKey(seed) + if err != nil { + return nil, fmt.Errorf("sakg: error deriving master key: %w", err) + } + + pathStr := fmt.Sprintf("%s/%d'", sakg.BIP32PathPrefix, number) + path, err := sakg.NewBIP32Path(pathStr) + if err != nil { + return nil, fmt.Errorf("sakg: error creating BIP-0032 path %s: %w", pathStr, err) + } + + for _, index := range path { + signer, chainCode, err = slip10.NewChildKey(signer, chainCode, index) + if err != nil { + return nil, fmt.Errorf("sakg: error deriving child key: %w", err) + } + } + + sr25519signer, err := sr25519.NewSigner(signer.(signature.UnsafeSigner).UnsafeBytes()) + if err != nil { + return nil, err + } + return sr25519signer, nil +}