github.com/gagliardetto/solana-go@v1.11.0/vault/vault.go (about)

     1  // Copyright 2020 dfuse Platform Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vault
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  
    21  	"github.com/gagliardetto/solana-go"
    22  )
    23  
    24  // Vault represents a `solana-go` wallet.  It contains the encrypted
    25  // material to load a KeyBag, which is the signing provider for
    26  // signing transactions using the `solana-go` library.
    27  type Vault struct {
    28  	Kind    string `json:"kind"`
    29  	Version int    `json:"version"`
    30  	Comment string `json:"comment"`
    31  
    32  	SecretBoxWrap       string `json:"secretbox_wrap"`
    33  	SecretBoxCiphertext string `json:"secretbox_ciphertext"`
    34  
    35  	KeyBag []solana.PrivateKey `json:"-"`
    36  }
    37  
    38  // NewVaultFromWalletFile returns a new Vault instance from the
    39  // provided filename of an eos wallet.
    40  func NewVaultFromWalletFile(filename string) (*Vault, error) {
    41  	v := NewVault()
    42  	fl, err := os.Open(filename)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	defer fl.Close()
    47  
    48  	err = json.NewDecoder(fl).Decode(&v)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return v, nil
    54  }
    55  
    56  // // NewVaultFromKeysFile creates a new Vault from the keys in the
    57  // // provided keys file.
    58  // // keysFile should be formatted with a single private key per line
    59  // func NewVaultFromKeysFile(keysFile string) (*Vault, error) {
    60  // 	v := NewVault()
    61  // 	if err := v.KeyBag.ImportFromFile(keysFile); err != nil {
    62  // 		return nil, err
    63  // 	}
    64  // 	return v, nil
    65  // }
    66  
    67  // NewVaultFromSingleKey creates a new Vault from the provided
    68  // private key.
    69  func NewVaultFromSingleKey(privKey string) (*Vault, error) {
    70  	v := NewVault()
    71  	key, err := solana.PrivateKeyFromBase58(privKey)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("import private key: %s", err)
    74  	}
    75  	v.KeyBag = append(v.KeyBag, key)
    76  	return v, nil
    77  }
    78  
    79  // NewVault returns an empty vault, unsaved and with no keys.
    80  func NewVault() *Vault {
    81  	return &Vault{
    82  		Kind:    "solana-vault-wallet",
    83  		Version: 1,
    84  	}
    85  }
    86  
    87  // NewKeyPair creates a new keypair, saves the private key in the
    88  // local wallet and returns the public key. It does NOT save the
    89  // wallet, you better do that soon after.
    90  func (v *Vault) NewKeyPair() (pub solana.PublicKey, err error) {
    91  	privKey, err := solana.NewRandomPrivateKey()
    92  	if err != nil {
    93  		return solana.PublicKey{}, err
    94  	}
    95  
    96  	v.KeyBag = append(v.KeyBag, privKey)
    97  
    98  	return privKey.PublicKey(), nil
    99  }
   100  
   101  // AddPrivateKey appends the provided private key into the Vault's KeyBag
   102  func (v *Vault) AddPrivateKey(privateKey solana.PrivateKey) solana.PublicKey {
   103  	v.KeyBag = append(v.KeyBag, privateKey)
   104  	return privateKey.PublicKey()
   105  }
   106  
   107  // PrintPublicKeys prints a PublicKey corresponding to each PrivateKey in the Vault's
   108  // KeyBag.
   109  func (v *Vault) PrintPublicKeys() {
   110  	fmt.Printf("Public keys contained within (%d in total):\n", len(v.KeyBag))
   111  	for _, key := range v.KeyBag {
   112  		fmt.Println("-", key.PublicKey().String())
   113  	}
   114  }
   115  
   116  func (v *Vault) PrintPrivateKeys() {
   117  	fmt.Printf("Private keys contained within (%d in total):\n", len(v.KeyBag))
   118  	for _, key := range v.KeyBag {
   119  		fmt.Printf("- %s (corresponds to %s)\n", key, key.PublicKey())
   120  	}
   121  }
   122  
   123  // WriteToFile writes the Vault to disk. You need to encrypt before
   124  // writing to file, otherwise you might lose much :)
   125  func (v *Vault) WriteToFile(filename string) error {
   126  	cnt, err := json.MarshalIndent(v, "", "  ")
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	fl, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	_, err = fl.Write(cnt)
   137  	if err != nil {
   138  		fl.Close()
   139  		return err
   140  	}
   141  
   142  	return fl.Close()
   143  }
   144  
   145  func (v *Vault) Open(boxer SecretBoxer) error {
   146  	data, err := boxer.Open(v.SecretBoxCiphertext)
   147  	if err != nil {
   148  		return fmt.Errorf("opening boxer: %w", err)
   149  	}
   150  
   151  	err = json.Unmarshal(data, &v.KeyBag)
   152  	if err != nil {
   153  		return fmt.Errorf("unmarshal: %w", err)
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func (v *Vault) Seal(boxer SecretBoxer) error {
   160  	payload, err := json.Marshal(v.KeyBag)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	v.SecretBoxWrap = boxer.WrapType()
   166  	cipherText, err := boxer.Seal(payload)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	v.SecretBoxCiphertext = cipherText
   172  	return nil
   173  }