github.com/ZuluSpl0it/Sia@v1.3.7/modules/wallet/unseeded.go (about) 1 package wallet 2 3 import ( 4 "errors" 5 6 "github.com/NebulousLabs/Sia/crypto" 7 "github.com/NebulousLabs/Sia/encoding" 8 "github.com/NebulousLabs/Sia/modules" 9 "github.com/NebulousLabs/Sia/types" 10 "github.com/NebulousLabs/fastrand" 11 ) 12 13 const ( 14 // SiagFileExtension is the file extension to be used for siag files 15 SiagFileExtension = ".siakey" 16 17 // SiagFileHeader is the header for all siag files. Do not change. Because siag was created 18 // early in development, compatibility with siag requires manually handling 19 // the headers and version instead of using the persist package. 20 SiagFileHeader = "siag" 21 22 // SiagFileVersion is the version number to be used for siag files 23 SiagFileVersion = "1.0" 24 ) 25 26 var ( 27 errAllDuplicates = errors.New("old wallet has no new seeds") 28 errDuplicateSpendableKey = errors.New("key has already been loaded into the wallet") 29 30 // ErrInconsistentKeys is the error when keyfiles provided are for different addresses 31 ErrInconsistentKeys = errors.New("keyfiles provided that are for different addresses") 32 // ErrInsufficientKeys is the error when there's not enough keys provided to spend the siafunds 33 ErrInsufficientKeys = errors.New("not enough keys provided to spend the siafunds") 34 // ErrNoKeyfile is the error when no keyfile has been presented 35 ErrNoKeyfile = errors.New("no keyfile has been presented") 36 // ErrUnknownHeader is the error when file contains wrong header 37 ErrUnknownHeader = errors.New("file contains the wrong header") 38 // ErrUnknownVersion is the error when the file has an unknown version number 39 ErrUnknownVersion = errors.New("file has an unknown version number") 40 ) 41 42 // A siagKeyPair is the struct representation of the bytes that get saved to 43 // disk by siag when a new keyfile is created. 44 type siagKeyPair struct { 45 Header string 46 Version string 47 Index int // should be uint64 - too late now 48 SecretKey crypto.SecretKey 49 UnlockConditions types.UnlockConditions 50 } 51 52 // savedKey033x is the persist structure that was used to save and load private 53 // keys in versions v0.3.3.x for siad. 54 type savedKey033x struct { 55 SecretKey crypto.SecretKey 56 UnlockConditions types.UnlockConditions 57 Visible bool 58 } 59 60 // decryptSpendableKeyFile decrypts a spendableKeyFile, returning a 61 // spendableKey. 62 func decryptSpendableKeyFile(masterKey crypto.TwofishKey, uk spendableKeyFile) (sk spendableKey, err error) { 63 // Verify that the decryption key is correct. 64 decryptionKey := uidEncryptionKey(masterKey, uk.UID) 65 err = verifyEncryption(decryptionKey, uk.EncryptionVerification) 66 if err != nil { 67 return 68 } 69 70 // Decrypt the spendable key and add it to the wallet. 71 encodedKey, err := decryptionKey.DecryptBytes(uk.SpendableKey) 72 if err != nil { 73 return 74 } 75 err = encoding.Unmarshal(encodedKey, &sk) 76 return 77 } 78 79 // integrateSpendableKey loads a spendableKey into the wallet. 80 func (w *Wallet) integrateSpendableKey(masterKey crypto.TwofishKey, sk spendableKey) { 81 w.keys[sk.UnlockConditions.UnlockHash()] = sk 82 } 83 84 // loadSpendableKey loads a spendable key into the wallet database. 85 func (w *Wallet) loadSpendableKey(masterKey crypto.TwofishKey, sk spendableKey) error { 86 // Duplication is detected by looking at the set of unlock conditions. If 87 // the wallet is locked, correct deduplication is uncertain. 88 if !w.unlocked { 89 return modules.ErrLockedWallet 90 } 91 92 // Check for duplicates. 93 _, exists := w.keys[sk.UnlockConditions.UnlockHash()] 94 if exists { 95 return errDuplicateSpendableKey 96 } 97 98 // TODO: Check that the key is actually spendable. 99 100 // Create a UID and encryption verification. 101 var skf spendableKeyFile 102 fastrand.Read(skf.UID[:]) 103 encryptionKey := uidEncryptionKey(masterKey, skf.UID) 104 skf.EncryptionVerification = encryptionKey.EncryptBytes(verificationPlaintext) 105 106 // Encrypt and save the key. 107 skf.SpendableKey = encryptionKey.EncryptBytes(encoding.Marshal(sk)) 108 109 err := checkMasterKey(w.dbTx, masterKey) 110 if err != nil { 111 return err 112 } 113 var current []spendableKeyFile 114 err = encoding.Unmarshal(w.dbTx.Bucket(bucketWallet).Get(keySpendableKeyFiles), ¤t) 115 if err != nil { 116 return err 117 } 118 return w.dbTx.Bucket(bucketWallet).Put(keySpendableKeyFiles, encoding.Marshal(append(current, skf))) 119 120 // w.keys[sk.UnlockConditions.UnlockHash()] = sk -> aids with duplicate 121 // detection, but causes db inconsistency. Rescanning is probably the 122 // solution. 123 } 124 125 // loadSiagKeys loads a set of siag keyfiles into the wallet, so that the 126 // wallet may spend the siafunds. 127 func (w *Wallet) loadSiagKeys(masterKey crypto.TwofishKey, keyfiles []string) error { 128 // Load the keyfiles from disk. 129 if len(keyfiles) < 1 { 130 return ErrNoKeyfile 131 } 132 skps := make([]siagKeyPair, len(keyfiles)) 133 for i, keyfile := range keyfiles { 134 err := encoding.ReadFile(keyfile, &skps[i]) 135 if err != nil { 136 return err 137 } 138 139 if skps[i].Header != SiagFileHeader { 140 return ErrUnknownHeader 141 } 142 if skps[i].Version != SiagFileVersion { 143 return ErrUnknownVersion 144 } 145 } 146 147 // Check that all of the loaded files have the same address, and that there 148 // are enough to create the transaction. 149 baseUnlockHash := skps[0].UnlockConditions.UnlockHash() 150 for _, skp := range skps { 151 if skp.UnlockConditions.UnlockHash() != baseUnlockHash { 152 return ErrInconsistentKeys 153 } 154 } 155 if uint64(len(skps)) < skps[0].UnlockConditions.SignaturesRequired { 156 return ErrInsufficientKeys 157 } 158 // Drop all unneeded keys. 159 skps = skps[0:skps[0].UnlockConditions.SignaturesRequired] 160 161 // Merge the keys into a single spendableKey and save it to the wallet. 162 var sk spendableKey 163 sk.UnlockConditions = skps[0].UnlockConditions 164 for _, skp := range skps { 165 sk.SecretKeys = append(sk.SecretKeys, skp.SecretKey) 166 } 167 err := w.loadSpendableKey(masterKey, sk) 168 if err != nil { 169 return err 170 } 171 w.integrateSpendableKey(masterKey, sk) 172 return nil 173 } 174 175 // LoadSiagKeys loads a set of siag-generated keys into the wallet. 176 func (w *Wallet) LoadSiagKeys(masterKey crypto.TwofishKey, keyfiles []string) error { 177 if err := w.tg.Add(); err != nil { 178 return err 179 } 180 defer w.tg.Done() 181 182 // load the keys and reset the consensus change ID and height in preparation for rescan 183 err := func() error { 184 w.mu.Lock() 185 defer w.mu.Unlock() 186 err := w.loadSiagKeys(masterKey, keyfiles) 187 if err != nil { 188 return err 189 } 190 191 if err = w.dbTx.DeleteBucket(bucketProcessedTransactions); err != nil { 192 return err 193 } 194 if _, err = w.dbTx.CreateBucket(bucketProcessedTransactions); err != nil { 195 return err 196 } 197 w.unconfirmedProcessedTransactions = nil 198 err = dbPutConsensusChangeID(w.dbTx, modules.ConsensusChangeBeginning) 199 if err != nil { 200 return err 201 } 202 return dbPutConsensusHeight(w.dbTx, 0) 203 }() 204 if err != nil { 205 return err 206 } 207 208 // rescan the blockchain 209 w.cs.Unsubscribe(w) 210 w.tpool.Unsubscribe(w) 211 212 done := make(chan struct{}) 213 go w.rescanMessage(done) 214 defer close(done) 215 216 err = w.cs.ConsensusSetSubscribe(w, modules.ConsensusChangeBeginning, w.tg.StopChan()) 217 if err != nil { 218 return err 219 } 220 w.tpool.TransactionPoolSubscribe(w) 221 return nil 222 } 223 224 // Load033xWallet loads a v0.3.3.x wallet as an unseeded key, such that the 225 // funds become spendable to the current wallet. 226 func (w *Wallet) Load033xWallet(masterKey crypto.TwofishKey, filepath033x string) error { 227 if err := w.tg.Add(); err != nil { 228 return err 229 } 230 defer w.tg.Done() 231 232 // load the keys and reset the consensus change ID and height in preparation for rescan 233 err := func() error { 234 w.mu.Lock() 235 defer w.mu.Unlock() 236 237 var savedKeys []savedKey033x 238 err := encoding.ReadFile(filepath033x, &savedKeys) 239 if err != nil { 240 return err 241 } 242 var seedsLoaded int 243 for _, savedKey := range savedKeys { 244 spendKey := spendableKey{ 245 UnlockConditions: savedKey.UnlockConditions, 246 SecretKeys: []crypto.SecretKey{savedKey.SecretKey}, 247 } 248 err = w.loadSpendableKey(masterKey, spendKey) 249 if err != nil && err != errDuplicateSpendableKey { 250 return err 251 } 252 if err == nil { 253 seedsLoaded++ 254 } 255 w.integrateSpendableKey(masterKey, spendKey) 256 } 257 if seedsLoaded == 0 { 258 return errAllDuplicates 259 } 260 261 if err = w.dbTx.DeleteBucket(bucketProcessedTransactions); err != nil { 262 return err 263 } 264 if _, err = w.dbTx.CreateBucket(bucketProcessedTransactions); err != nil { 265 return err 266 } 267 w.unconfirmedProcessedTransactions = nil 268 err = dbPutConsensusChangeID(w.dbTx, modules.ConsensusChangeBeginning) 269 if err != nil { 270 return err 271 } 272 return dbPutConsensusHeight(w.dbTx, 0) 273 }() 274 if err != nil { 275 return err 276 } 277 278 // rescan the blockchain 279 w.cs.Unsubscribe(w) 280 w.tpool.Unsubscribe(w) 281 282 done := make(chan struct{}) 283 go w.rescanMessage(done) 284 defer close(done) 285 286 err = w.cs.ConsensusSetSubscribe(w, modules.ConsensusChangeBeginning, w.tg.StopChan()) 287 if err != nil { 288 return err 289 } 290 w.tpool.TransactionPoolSubscribe(w) 291 292 return nil 293 }