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 }