github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/wallet/persist.go (about)

     1  package wallet
     2  
     3  import (
     4  	"crypto/rand"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/NebulousLabs/Sia/crypto"
     9  	"github.com/NebulousLabs/Sia/modules"
    10  	"github.com/NebulousLabs/Sia/persist"
    11  )
    12  
    13  const (
    14  	logFile            = modules.WalletDir + ".log"
    15  	settingsFileSuffix = ".json"
    16  	settingsFile       = modules.WalletDir + settingsFileSuffix
    17  
    18  	encryptionVerificationLen = 32
    19  )
    20  
    21  var (
    22  	settingsMetadata = persist.Metadata{
    23  		Header:  "Wallet Settings",
    24  		Version: "0.4.0",
    25  	}
    26  	seedMetadata = persist.Metadata{
    27  		Header:  "Wallet Seed",
    28  		Version: "0.4.0",
    29  	}
    30  )
    31  
    32  // SpendableKeyFile stores an encrypted spendable key on disk.
    33  type SpendableKeyFile struct {
    34  	UID                    UniqueID
    35  	EncryptionVerification crypto.Ciphertext
    36  	SpendableKey           crypto.Ciphertext
    37  }
    38  
    39  // WalletPersist contains all data that persists on disk during wallet
    40  // operation.
    41  type WalletPersist struct {
    42  	// EncryptionVerification is an encrypted string that, when decrypted, is
    43  	// 32 '0' bytes. The UID is used to prevent leaking information in the
    44  	// event that the same key gets used for multiple wallets.
    45  	UID                    UniqueID
    46  	EncryptionVerification crypto.Ciphertext
    47  
    48  	// The primary seed is used to generate new addresses as they are required.
    49  	// All addresses are tracked and spendable. Only modules.PublicKeysPerSeed
    50  	// keys/addresses can be created per seed, after which a new seed will need
    51  	// to be generated.
    52  	PrimarySeedFile     SeedFile
    53  	PrimarySeedProgress uint64
    54  
    55  	// AuxiliarySeedFiles is a set of seeds that the wallet can spend from, but is
    56  	// no longer using to generate addresses. The primary use case is loading
    57  	// backups in the event of lost files or coins. All auxiliary seeds are
    58  	// encrypted using the primary seed encryption password.
    59  	AuxiliarySeedFiles []SeedFile
    60  
    61  	// UnseededKeys are list of spendable keys that were not generated by a
    62  	// random seed.
    63  	UnseededKeys []SpendableKeyFile
    64  }
    65  
    66  // loadSettings reads the wallet's settings from the wallet's settings file,
    67  // overwriting the settings object in memory. loadSettings should only be
    68  // called at startup.
    69  func (w *Wallet) loadSettings() error {
    70  	return persist.LoadFile(settingsMetadata, &w.persist, filepath.Join(w.persistDir, settingsFile))
    71  }
    72  
    73  // saveSettings writes the wallet's settings to the wallet's settings file,
    74  // replacing the existing file.
    75  func (w *Wallet) saveSettings() error {
    76  	return persist.SaveFile(settingsMetadata, w.persist, filepath.Join(w.persistDir, settingsFile))
    77  }
    78  
    79  // saveSettingsSync writes the wallet's settings to the wallet's settings file,
    80  // replacing the existing file, and then syncs to disk.
    81  func (w *Wallet) saveSettingsSync() error {
    82  	return persist.SaveFileSync(settingsMetadata, w.persist, filepath.Join(w.persistDir, settingsFile))
    83  }
    84  
    85  // initSettings creates the settings object at startup. If a settings file
    86  // exists, the settings file will be loaded into memory. If the settings file
    87  // does not exist, a new.persist file will be created.
    88  func (w *Wallet) initSettings() error {
    89  	// Check if the settings file exists, if not create it.
    90  	settingsFilename := filepath.Join(w.persistDir, settingsFile)
    91  	_, err := os.Stat(settingsFilename)
    92  	if os.IsNotExist(err) {
    93  		_, err = rand.Read(w.persist.UID[:])
    94  		if err != nil {
    95  			return err
    96  		}
    97  		return w.saveSettings()
    98  	} else if err != nil {
    99  		return err
   100  	}
   101  
   102  	// Load the settings file if it does exist.
   103  	return w.loadSettings()
   104  }
   105  
   106  // initPersist loads all of the wallet's persistence files into memory,
   107  // creating them if they do not exist.
   108  func (w *Wallet) initPersist() error {
   109  	// Create a directory for the wallet without overwriting an existing
   110  	// directory.
   111  	err := os.MkdirAll(w.persistDir, 0700)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	// Start logging.
   117  	w.log, err = persist.NewFileLogger(filepath.Join(w.persistDir, logFile))
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	// Load the settings file.
   123  	err = w.initSettings()
   124  	if err != nil {
   125  		return err
   126  	}
   127  	return nil
   128  }
   129  
   130  // createBackup creates a backup file at the desired filepath.
   131  func (w *Wallet) createBackup(backupFilepath string) error {
   132  	return persist.SaveFileSync(settingsMetadata, w.persist, backupFilepath)
   133  }
   134  
   135  // CreateBackup creates a backup file at the desired filepath.
   136  func (w *Wallet) CreateBackup(backupFilepath string) error {
   137  	w.mu.Lock()
   138  	defer w.mu.Unlock()
   139  	return w.createBackup(backupFilepath)
   140  }
   141  
   142  /*
   143  // LoadBackup loads a backup file from the provided filepath. The backup file
   144  // primary seed is loaded as an auxiliary seed.
   145  func (w *Wallet) LoadBackup(masterKey, backupMasterKey crypto.TwofishKey, backupFilepath string) error {
   146  	lockID := w.mu.Lock()
   147  	defer w.mu.Unlock(lockID)
   148  
   149  	// Load all of the seed files, check for duplicates, re-encrypt them (but
   150  	// keep the UID), and add them to the WalletPersist object)
   151  	var backupPersist WalletPersist
   152  	err := persist.LoadFile(settingsMetadata, &backupPersist, backupFilepath)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	backupSeeds := append(backupPersist.AuxiliarySeedFiles, backupPersist.PrimarySeedFile)
   157  	TODO: more
   158  }
   159  */