github.com/ZuluSpl0it/Sia@v1.3.7/modules/wallet/persist.go (about) 1 package wallet 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 9 "github.com/NebulousLabs/Sia/crypto" 10 "github.com/NebulousLabs/Sia/encoding" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/persist" 13 "github.com/NebulousLabs/Sia/types" 14 "github.com/NebulousLabs/errors" 15 "github.com/NebulousLabs/fastrand" 16 17 "github.com/coreos/bbolt" 18 ) 19 20 const ( 21 compatFile = modules.WalletDir + ".json" 22 dbFile = modules.WalletDir + ".db" 23 logFile = modules.WalletDir + ".log" 24 ) 25 26 var ( 27 dbMetadata = persist.Metadata{ 28 Header: "Wallet Database", 29 Version: "1.1.0", 30 } 31 ) 32 33 // spendableKeyFile stores an encrypted spendable key on disk. 34 type spendableKeyFile struct { 35 UID uniqueID 36 EncryptionVerification crypto.Ciphertext 37 SpendableKey crypto.Ciphertext 38 } 39 40 // openDB loads the set database and populates it with the necessary buckets. 41 func (w *Wallet) openDB(filename string) (err error) { 42 w.db, err = persist.OpenDatabase(dbMetadata, filename) 43 if err != nil { 44 return err 45 } 46 // initialize the database 47 err = w.db.Update(func(tx *bolt.Tx) error { 48 // check whether we need to init bucketAddrTransactions 49 buildAddrTxns := tx.Bucket(bucketAddrTransactions) == nil 50 // ensure that all buckets exist 51 for _, b := range dbBuckets { 52 _, err := tx.CreateBucketIfNotExists(b) 53 if err != nil { 54 return fmt.Errorf("could not create bucket %v: %v", string(b), err) 55 } 56 } 57 // if the wallet does not have a UID, create one 58 if tx.Bucket(bucketWallet).Get(keyUID) == nil { 59 uid := make([]byte, len(uniqueID{})) 60 fastrand.Read(uid[:]) 61 tx.Bucket(bucketWallet).Put(keyUID, uid) 62 } 63 // if fields in bucketWallet are nil, set them to zero to prevent unmarshal errors 64 wb := tx.Bucket(bucketWallet) 65 if wb.Get(keyConsensusHeight) == nil { 66 wb.Put(keyConsensusHeight, encoding.Marshal(uint64(0))) 67 } 68 if wb.Get(keyAuxiliarySeedFiles) == nil { 69 wb.Put(keyAuxiliarySeedFiles, encoding.Marshal([]seedFile{})) 70 } 71 if wb.Get(keySpendableKeyFiles) == nil { 72 wb.Put(keySpendableKeyFiles, encoding.Marshal([]spendableKeyFile{})) 73 } 74 if wb.Get(keySiafundPool) == nil { 75 wb.Put(keySiafundPool, encoding.Marshal(types.ZeroCurrency)) 76 } 77 78 // build the bucketAddrTransactions bucket if necessary 79 if buildAddrTxns { 80 it := dbProcessedTransactionsIterator(tx) 81 for it.next() { 82 index, pt := it.key(), it.value() 83 if err := dbAddProcessedTransactionAddrs(tx, pt, index); err != nil { 84 return err 85 } 86 } 87 } 88 89 // check whether wallet is encrypted 90 w.encrypted = tx.Bucket(bucketWallet).Get(keyEncryptionVerification) != nil 91 return nil 92 }) 93 return err 94 } 95 96 // initPersist loads all of the wallet's persistence files into memory, 97 // creating them if they do not exist. 98 func (w *Wallet) initPersist() error { 99 // Create a directory for the wallet without overwriting an existing 100 // directory. 101 err := os.MkdirAll(w.persistDir, 0700) 102 if err != nil { 103 return err 104 } 105 106 // Start logging. 107 w.log, err = persist.NewFileLogger(filepath.Join(w.persistDir, logFile)) 108 if err != nil { 109 return err 110 } 111 112 // Open the database. 113 dbFilename := filepath.Join(w.persistDir, dbFile) 114 compatFilename := filepath.Join(w.persistDir, compatFile) 115 _, dbErr := os.Stat(dbFilename) 116 _, compatErr := os.Stat(compatFilename) 117 if dbErr != nil && compatErr == nil { 118 // database does not exist, but old persist does; convert it 119 err = w.convertPersistFrom112To120(dbFilename, compatFilename) 120 } else { 121 // either database exists or neither exists; open/create the database 122 err = w.openDB(filepath.Join(w.persistDir, dbFile)) 123 } 124 if err != nil { 125 return err 126 } 127 err = w.tg.AfterStop(func() error { 128 var err error 129 if w.dbRollback { 130 // rollback txn if necessry. 131 err = errors.New("database unable to sync - rollback requested") 132 err = errors.Compose(err, w.dbTx.Rollback()) 133 } else { 134 // else commit the transaction. 135 err = w.dbTx.Commit() 136 } 137 if err != nil { 138 w.log.Severe("ERROR: failed to apply database update:", err) 139 return errors.AddContext(err, "unable to commit dbTx in syncDB") 140 } 141 return w.db.Close() 142 }) 143 if err != nil { 144 return err 145 } 146 go w.threadedDBUpdate() 147 return nil 148 } 149 150 // createBackup copies the wallet database to dst. 151 func (w *Wallet) createBackup(dst io.Writer) error { 152 _, err := w.dbTx.WriteTo(dst) 153 return err 154 } 155 156 // CreateBackup creates a backup file at the desired filepath. 157 func (w *Wallet) CreateBackup(backupFilepath string) error { 158 if err := w.tg.Add(); err != nil { 159 return err 160 } 161 defer w.tg.Done() 162 w.mu.Lock() 163 defer w.mu.Unlock() 164 f, err := os.Create(backupFilepath) 165 if err != nil { 166 return err 167 } 168 defer f.Close() 169 return w.createBackup(f) 170 } 171 172 // compat112Persist is the structure of the wallet.json file used in v1.1.2 173 type compat112Persist struct { 174 UID uniqueID 175 EncryptionVerification crypto.Ciphertext 176 PrimarySeedFile seedFile 177 PrimarySeedProgress uint64 178 AuxiliarySeedFiles []seedFile 179 UnseededKeys []spendableKeyFile 180 } 181 182 // compat112Meta is the metadata of the wallet.json file used in v1.1.2 183 var compat112Meta = persist.Metadata{ 184 Header: "Wallet Settings", 185 Version: "0.4.0", 186 } 187 188 // convertPersistFrom112To120 converts an old (pre-v1.2.0) wallet.json file to 189 // a wallet.db database. 190 func (w *Wallet) convertPersistFrom112To120(dbFilename, compatFilename string) error { 191 var data compat112Persist 192 err := persist.LoadJSON(compat112Meta, &data, compatFilename) 193 if err != nil { 194 return err 195 } 196 197 w.db, err = persist.OpenDatabase(dbMetadata, dbFilename) 198 if err != nil { 199 return err 200 } 201 // initialize the database 202 err = w.db.Update(func(tx *bolt.Tx) error { 203 for _, b := range dbBuckets { 204 _, err := tx.CreateBucket(b) 205 if err != nil { 206 return fmt.Errorf("could not create bucket %v: %v", string(b), err) 207 } 208 } 209 // set UID, verification, seeds, and seed progress 210 tx.Bucket(bucketWallet).Put(keyUID, data.UID[:]) 211 tx.Bucket(bucketWallet).Put(keyEncryptionVerification, data.EncryptionVerification) 212 tx.Bucket(bucketWallet).Put(keyPrimarySeedFile, encoding.Marshal(data.PrimarySeedFile)) 213 tx.Bucket(bucketWallet).Put(keyAuxiliarySeedFiles, encoding.Marshal(data.AuxiliarySeedFiles)) 214 tx.Bucket(bucketWallet).Put(keySpendableKeyFiles, encoding.Marshal(data.UnseededKeys)) 215 // old wallets had a "preload depth" of 25 216 dbPutPrimarySeedProgress(tx, data.PrimarySeedProgress+25) 217 218 // set consensus height and CCID to zero so that a full rescan is 219 // triggered 220 dbPutConsensusHeight(tx, 0) 221 dbPutConsensusChangeID(tx, modules.ConsensusChangeBeginning) 222 return nil 223 }) 224 w.encrypted = true 225 return err 226 } 227 228 /* 229 // LoadBackup loads a backup file from the provided filepath. The backup file 230 // primary seed is loaded as an auxiliary seed. 231 func (w *Wallet) LoadBackup(masterKey, backupMasterKey crypto.TwofishKey, backupFilepath string) error { 232 if err := w.tg.Add(); err != nil { 233 return err 234 } 235 defer w.tg.Done() 236 237 lockID := w.mu.Lock() 238 defer w.mu.Unlock(lockID) 239 240 // Load all of the seed files, check for duplicates, re-encrypt them (but 241 // keep the UID), and add them to the walletPersist object) 242 var backupPersist walletPersist 243 err := persist.LoadFile(settingsMetadata, &backupPersist, backupFilepath) 244 if err != nil { 245 return err 246 } 247 backupSeeds := append(backupPersist.AuxiliarySeedFiles, backupPersist.PrimarySeedFile) 248 TODO: more 249 } 250 */