solana-go/vault/vault.go

174 lines
4.4 KiB
Go

// Copyright 2020 dfuse Platform Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vault
import (
"fmt"
"os"
"github.com/gagliardetto/solana-go"
)
// Vault represents a `solana-go` wallet. It contains the encrypted
// material to load a KeyBag, which is the signing provider for
// signing transactions using the `solana-go` library.
type Vault struct {
Kind string `json:"kind"`
Version int `json:"version"`
Comment string `json:"comment"`
SecretBoxWrap string `json:"secretbox_wrap"`
SecretBoxCiphertext string `json:"secretbox_ciphertext"`
KeyBag []solana.PrivateKey `json:"-"`
}
// NewVaultFromWalletFile returns a new Vault instance from the
// provided filename of an eos wallet.
func NewVaultFromWalletFile(filename string) (*Vault, error) {
v := NewVault()
fl, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fl.Close()
err = json.NewDecoder(fl).Decode(&v)
if err != nil {
return nil, err
}
return v, nil
}
// // NewVaultFromKeysFile creates a new Vault from the keys in the
// // provided keys file.
// // keysFile should be formatted with a single private key per line
// func NewVaultFromKeysFile(keysFile string) (*Vault, error) {
// v := NewVault()
// if err := v.KeyBag.ImportFromFile(keysFile); err != nil {
// return nil, err
// }
// return v, nil
// }
// NewVaultFromSingleKey creates a new Vault from the provided
// private key.
func NewVaultFromSingleKey(privKey string) (*Vault, error) {
v := NewVault()
key, err := solana.PrivateKeyFromBase58(privKey)
if err != nil {
return nil, fmt.Errorf("import private key: %s", err)
}
v.KeyBag = append(v.KeyBag, key)
return v, nil
}
// NewVault returns an empty vault, unsaved and with no keys.
func NewVault() *Vault {
return &Vault{
Kind: "solana-vault-wallet",
Version: 1,
}
}
// NewKeyPair creates a new keypair, saves the private key in the
// local wallet and returns the public key. It does NOT save the
// wallet, you better do that soon after.
func (v *Vault) NewKeyPair() (pub solana.PublicKey, err error) {
privKey, err := solana.NewRandomPrivateKey()
if err != nil {
return solana.PublicKey{}, err
}
v.KeyBag = append(v.KeyBag, privKey)
return privKey.PublicKey(), nil
}
// AddPrivateKey appends the provided private key into the Vault's KeyBag
func (v *Vault) AddPrivateKey(privateKey solana.PrivateKey) solana.PublicKey {
v.KeyBag = append(v.KeyBag, privateKey)
return privateKey.PublicKey()
}
// PrintPublicKeys prints a PublicKey corresponding to each PrivateKey in the Vault's
// KeyBag.
func (v *Vault) PrintPublicKeys() {
fmt.Printf("Public keys contained within (%d in total):\n", len(v.KeyBag))
for _, key := range v.KeyBag {
fmt.Println("-", key.PublicKey().String())
}
}
func (v *Vault) PrintPrivateKeys() {
fmt.Printf("Private keys contained within (%d in total):\n", len(v.KeyBag))
for _, key := range v.KeyBag {
fmt.Printf("- %s (corresponds to %s)\n", key, key.PublicKey())
}
}
// WriteToFile writes the Vault to disk. You need to encrypt before
// writing to file, otherwise you might lose much :)
func (v *Vault) WriteToFile(filename string) error {
cnt, err := json.MarshalIndent(v, "", " ")
if err != nil {
return err
}
fl, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
_, err = fl.Write(cnt)
if err != nil {
fl.Close()
return err
}
return fl.Close()
}
func (v *Vault) Open(boxer SecretBoxer) error {
data, err := boxer.Open(v.SecretBoxCiphertext)
if err != nil {
return fmt.Errorf("opening boxer: %w", err)
}
err = json.Unmarshal(data, &v.KeyBag)
if err != nil {
return fmt.Errorf("unmarshal: %w", err)
}
return nil
}
func (v *Vault) Seal(boxer SecretBoxer) error {
payload, err := json.Marshal(v.KeyBag)
if err != nil {
return err
}
v.SecretBoxWrap = boxer.WrapType()
cipherText, err := boxer.Seal(payload)
if err != nil {
return err
}
v.SecretBoxCiphertext = cipherText
return nil
}