github.com/ZuluSpl0it/Sia@v1.3.7/modules/wallet.go (about) 1 package modules 2 3 import ( 4 "bytes" 5 "errors" 6 7 "github.com/NebulousLabs/entropy-mnemonics" 8 9 "github.com/NebulousLabs/Sia/crypto" 10 "github.com/NebulousLabs/Sia/types" 11 ) 12 13 const ( 14 // PublicKeysPerSeed define the number of public keys that get pregenerated 15 // for a seed at startup when searching for balances in the blockchain. 16 PublicKeysPerSeed = 2500 17 18 // SeedChecksumSize is the number of bytes that are used to checksum 19 // addresses to prevent accidental spending. 20 SeedChecksumSize = 6 21 22 // WalletDir is the directory that contains the wallet persistence. 23 WalletDir = "wallet" 24 ) 25 26 var ( 27 // ErrBadEncryptionKey is returned if the incorrect encryption key to a 28 // file is provided. 29 ErrBadEncryptionKey = errors.New("provided encryption key is incorrect") 30 31 // ErrIncompleteTransactions is returned if the wallet has incomplete 32 // transactions being built that are using all of the current outputs, and 33 // therefore the wallet is unable to spend money despite it not technically 34 // being 'unconfirmed' yet. 35 ErrIncompleteTransactions = errors.New("wallet has coins spent in incomplete transactions - not enough remaining coins") 36 37 // ErrLockedWallet is returned when an action cannot be performed due to 38 // the wallet being locked. 39 ErrLockedWallet = errors.New("wallet must be unlocked before it can be used") 40 41 // ErrLowBalance is returned if the wallet does not have enough funds to 42 // complete the desired action. 43 ErrLowBalance = errors.New("insufficient balance") 44 45 // ErrWalletShutdown is returned when a method can't continue execution due 46 // to the wallet shutting down. 47 ErrWalletShutdown = errors.New("wallet is shutting down") 48 ) 49 50 type ( 51 // Seed is cryptographic entropy that is used to derive spendable wallet 52 // addresses. 53 Seed [crypto.EntropySize]byte 54 55 // WalletTransactionID is a unique identifier for a wallet transaction. 56 WalletTransactionID crypto.Hash 57 58 // A ProcessedInput represents funding to a transaction. The input is 59 // coming from an address and going to the outputs. The fund types are 60 // 'SiacoinInput', 'SiafundInput'. 61 ProcessedInput struct { 62 ParentID types.OutputID `json:"parentid"` 63 FundType types.Specifier `json:"fundtype"` 64 WalletAddress bool `json:"walletaddress"` 65 RelatedAddress types.UnlockHash `json:"relatedaddress"` 66 Value types.Currency `json:"value"` 67 } 68 69 // A ProcessedOutput is a siacoin output that appears in a transaction. 70 // Some outputs mature immediately, some are delayed, and some may never 71 // mature at all (in the event of storage proofs). 72 // 73 // Fund type can either be 'SiacoinOutput', 'SiafundOutput', 'ClaimOutput', 74 // 'MinerPayout', or 'MinerFee'. All outputs except the miner fee create 75 // outputs accessible to an address. Miner fees are not spendable, and 76 // instead contribute to the block subsidy. 77 // 78 // MaturityHeight indicates at what block height the output becomes 79 // available. SiacoinInputs and SiafundInputs become available immediately. 80 // ClaimInputs and MinerPayouts become available after 144 confirmations. 81 ProcessedOutput struct { 82 ID types.OutputID `json:"id"` 83 FundType types.Specifier `json:"fundtype"` 84 MaturityHeight types.BlockHeight `json:"maturityheight"` 85 WalletAddress bool `json:"walletaddress"` 86 RelatedAddress types.UnlockHash `json:"relatedaddress"` 87 Value types.Currency `json:"value"` 88 } 89 90 // A ProcessedTransaction is a transaction that has been processed into 91 // explicit inputs and outputs and tagged with some header data such as 92 // confirmation height + timestamp. 93 // 94 // Because of the block subsidy, a block is considered as a transaction. 95 // Since there is technically no transaction id for the block subsidy, the 96 // block id is used instead. 97 ProcessedTransaction struct { 98 Transaction types.Transaction `json:"transaction"` 99 TransactionID types.TransactionID `json:"transactionid"` 100 ConfirmationHeight types.BlockHeight `json:"confirmationheight"` 101 ConfirmationTimestamp types.Timestamp `json:"confirmationtimestamp"` 102 103 Inputs []ProcessedInput `json:"inputs"` 104 Outputs []ProcessedOutput `json:"outputs"` 105 } 106 107 // TransactionBuilder is used to construct custom transactions. A transaction 108 // builder is initialized via 'RegisterTransaction' and then can be modified by 109 // adding funds or other fields. The transaction is completed by calling 110 // 'Sign', which will sign all inputs added via the 'FundSiacoins' or 111 // 'FundSiafunds' call. All modifications are additive. 112 // 113 // Parents of the transaction are kept in the transaction builder. A parent is 114 // any unconfirmed transaction that is required for the child to be valid. 115 // 116 // Transaction builders are not thread safe. 117 TransactionBuilder interface { 118 // FundSiacoins will add a siacoin input of exactly 'amount' to the 119 // transaction. A parent transaction may be needed to achieve an input 120 // with the correct value. The siacoin input will not be signed until 121 // 'Sign' is called on the transaction builder. The expectation is that 122 // the transaction will be completed and broadcast within a few hours. 123 // Longer risks double-spends, as the wallet will assume that the 124 // transaction failed. 125 FundSiacoins(amount types.Currency) error 126 127 // FundSiafunds will add a siafund input of exactly 'amount' to the 128 // transaction. A parent transaction may be needed to achieve an input 129 // with the correct value. The siafund input will not be signed until 130 // 'Sign' is called on the transaction builder. Any siacoins that are 131 // released by spending the siafund outputs will be sent to another 132 // address owned by the wallet. The expectation is that the transaction 133 // will be completed and broadcast within a few hours. Longer risks 134 // double-spends, because the wallet will assume the transaction 135 // failed. 136 FundSiafunds(amount types.Currency) error 137 138 // AddParents adds a set of parents to the transaction. 139 AddParents([]types.Transaction) 140 141 // AddMinerFee adds a miner fee to the transaction, returning the index 142 // of the miner fee within the transaction. 143 AddMinerFee(fee types.Currency) uint64 144 145 // AddSiacoinInput adds a siacoin input to the transaction, returning 146 // the index of the siacoin input within the transaction. When 'Sign' 147 // gets called, this input will be left unsigned. 148 AddSiacoinInput(types.SiacoinInput) uint64 149 150 // AddSiacoinOutput adds a siacoin output to the transaction, returning 151 // the index of the siacoin output within the transaction. 152 AddSiacoinOutput(types.SiacoinOutput) uint64 153 154 // AddFileContract adds a file contract to the transaction, returning 155 // the index of the file contract within the transaction. 156 AddFileContract(types.FileContract) uint64 157 158 // AddFileContractRevision adds a file contract revision to the 159 // transaction, returning the index of the file contract revision 160 // within the transaction. When 'Sign' gets called, this revision will 161 // be left unsigned. 162 AddFileContractRevision(types.FileContractRevision) uint64 163 164 // AddStorageProof adds a storage proof to the transaction, returning 165 // the index of the storage proof within the transaction. 166 AddStorageProof(types.StorageProof) uint64 167 168 // AddSiafundInput adds a siafund input to the transaction, returning 169 // the index of the siafund input within the transaction. When 'Sign' 170 // is called, this input will be left unsigned. 171 AddSiafundInput(types.SiafundInput) uint64 172 173 // AddSiafundOutput adds a siafund output to the transaction, returning 174 // the index of the siafund output within the transaction. 175 AddSiafundOutput(types.SiafundOutput) uint64 176 177 // AddArbitraryData adds arbitrary data to the transaction, returning 178 // the index of the data within the transaction. 179 AddArbitraryData(arb []byte) uint64 180 181 // AddTransactionSignature adds a transaction signature to the 182 // transaction, returning the index of the signature within the 183 // transaction. The signature should already be valid, and shouldn't 184 // sign any of the inputs that were added by calling 'FundSiacoins' or 185 // 'FundSiafunds'. 186 AddTransactionSignature(types.TransactionSignature) uint64 187 188 // Sign will sign any inputs added by 'FundSiacoins' or 'FundSiafunds' 189 // and return a transaction set that contains all parents prepended to 190 // the transaction. If more fields need to be added, a new transaction 191 // builder will need to be created. 192 // 193 // If the whole transaction flag is set to true, then the whole 194 // transaction flag will be set in the covered fields object. If the 195 // whole transaction flag is set to false, then the covered fields 196 // object will cover all fields that have already been added to the 197 // transaction, but will also leave room for more fields to be added. 198 // 199 // An error will be returned if there are multiple calls to 'Sign', 200 // sometimes even if the first call to Sign has failed. Sign should 201 // only ever be called once, and if the first signing fails, the 202 // transaction should be dropped. 203 Sign(wholeTransaction bool) ([]types.Transaction, error) 204 205 // UnconfirmedParents returns any unconfirmed parents the transaction set that 206 // is being built by the transaction builder could have. 207 UnconfirmedParents() ([]types.Transaction, error) 208 209 // View returns the incomplete transaction along with all of its 210 // parents. 211 View() (txn types.Transaction, parents []types.Transaction) 212 213 // ViewAdded returns all of the siacoin inputs, siafund inputs, and 214 // parent transactions that have been automatically added by the 215 // builder. Items are returned by index. 216 ViewAdded() (newParents, siacoinInputs, siafundInputs, transactionSignatures []int) 217 218 // Drop indicates that a transaction is no longer useful and will not be 219 // broadcast, and that all of the outputs can be reclaimed. 'Drop' 220 // should only be used before signatures are added. 221 Drop() 222 } 223 224 // EncryptionManager can encrypt, lock, unlock, and indicate the current 225 // status of the EncryptionManager. 226 EncryptionManager interface { 227 // Encrypt will encrypt the wallet using the input key. Upon 228 // encryption, a primary seed will be created for the wallet (no seed 229 // exists prior to this point). If the key is blank, then the hash of 230 // the seed that is generated will be used as the key. 231 // 232 // Encrypt can only be called once throughout the life of the wallet 233 // and will return an error on subsequent calls (even after restarting 234 // the wallet). To reset the wallet, the wallet files must be moved to 235 // a different directory or deleted. 236 Encrypt(masterKey crypto.TwofishKey) (Seed, error) 237 238 // Reset will reset the wallet, clearing the database and returning it to 239 // the unencrypted state. Reset can only be called on a wallet that has 240 // already been encrypted. 241 Reset() error 242 243 // Encrypted returns whether or not the wallet has been encrypted yet. 244 // After being encrypted for the first time, the wallet can only be 245 // unlocked using the encryption password. 246 Encrypted() (bool, error) 247 248 // InitFromSeed functions like Encrypt, but using a specified seed. 249 // Unlike Encrypt, the blockchain will be scanned to determine the 250 // seed's progress. For this reason, InitFromSeed should not be called 251 // until the blockchain is fully synced. 252 InitFromSeed(masterKey crypto.TwofishKey, seed Seed) error 253 254 // Lock deletes all keys in memory and prevents the wallet from being 255 // used to spend coins or extract keys until 'Unlock' is called. 256 Lock() error 257 258 // Unlock must be called before the wallet is usable. All wallets and 259 // wallet seeds are encrypted by default, and the wallet will not know 260 // which addresses to watch for on the blockchain until unlock has been 261 // called. 262 // 263 // All items in the wallet are encrypted using different keys which are 264 // derived from the master key. 265 Unlock(masterKey crypto.TwofishKey) error 266 267 // ChangeKey changes the wallet's materKey from masterKey to newKey, 268 // re-encrypting the wallet with the provided key. 269 ChangeKey(masterKey crypto.TwofishKey, newKey crypto.TwofishKey) error 270 271 // Unlocked returns true if the wallet is currently unlocked, false 272 // otherwise. 273 Unlocked() (bool, error) 274 } 275 276 // KeyManager manages wallet keys, including the use of seeds, creating and 277 // loading backups, and providing a layer of compatibility for older wallet 278 // files. 279 KeyManager interface { 280 // AllAddresses returns all addresses that the wallet is able to spend 281 // from, including unseeded addresses. Addresses are returned sorted in 282 // byte-order. 283 AllAddresses() ([]types.UnlockHash, error) 284 285 // AllSeeds returns all of the seeds that are being tracked by the 286 // wallet, including the primary seed. Only the primary seed is used to 287 // generate new addresses, but the wallet can spend funds sent to 288 // public keys generated by any of the seeds returned. 289 AllSeeds() ([]Seed, error) 290 291 // CreateBackup will create a backup of the wallet at the provided 292 // filepath. The backup will have all seeds and keys. 293 CreateBackup(string) error 294 295 // LoadBackup will load a backup of the wallet from the provided 296 // address. The backup wallet will be added as an auxiliary seed, not 297 // as a primary seed. 298 // LoadBackup(masterKey, backupMasterKey crypto.TwofishKey, string) error 299 300 // Load033xWallet will load a version 0.3.3.x wallet from disk and add all of 301 // the keys in the wallet as unseeded keys. 302 Load033xWallet(crypto.TwofishKey, string) error 303 304 // LoadSeed will recreate a wallet file using the recovery phrase. 305 // LoadSeed only needs to be called if the original seed file or 306 // encryption password was lost. The master key is used to encrypt the 307 // recovery seed before saving it to disk. 308 LoadSeed(crypto.TwofishKey, Seed) error 309 310 // LoadSiagKeys will take a set of filepaths that point to a siag key 311 // and will have the siag keys loaded into the wallet so that they will 312 // become spendable. 313 LoadSiagKeys(crypto.TwofishKey, []string) error 314 315 // NextAddress returns a new coin addresses generated from the 316 // primary seed. 317 NextAddress() (types.UnlockConditions, error) 318 319 // NextAddresses returns n new coin addresses generated from the primary 320 // seed. 321 NextAddresses(uint64) ([]types.UnlockConditions, error) 322 323 // PrimarySeed returns the unencrypted primary seed of the wallet, 324 // along with a uint64 indicating how many addresses may be safely 325 // generated from the seed. 326 PrimarySeed() (Seed, uint64, error) 327 328 // SweepSeed scans the blockchain for outputs generated from seed and 329 // creates a transaction that transfers them to the wallet. Note that 330 // this incurs a transaction fee. It returns the total value of the 331 // outputs, minus the fee. If only siafunds were found, the fee is 332 // deducted from the wallet. 333 SweepSeed(seed Seed) (coins, funds types.Currency, err error) 334 } 335 336 // Wallet stores and manages siacoins and siafunds. The wallet file is 337 // encrypted using a user-specified password. Common addresses are all 338 // derived from a single address seed. 339 Wallet interface { 340 EncryptionManager 341 KeyManager 342 343 // Close permits clean shutdown during testing and serving. 344 Close() error 345 346 // ConfirmedBalance returns the confirmed balance of the wallet, minus 347 // any outgoing transactions. ConfirmedBalance will include unconfirmed 348 // refund transactions. 349 ConfirmedBalance() (siacoinBalance types.Currency, siafundBalance types.Currency, siacoinClaimBalance types.Currency, err error) 350 351 // UnconfirmedBalance returns the unconfirmed balance of the wallet. 352 // Outgoing funds and incoming funds are reported separately. Refund 353 // outputs are included, meaning that sending a single coin to 354 // someone could result in 'outgoing: 12, incoming: 11'. Siafunds are 355 // not considered in the unconfirmed balance. 356 UnconfirmedBalance() (outgoingSiacoins types.Currency, incomingSiacoins types.Currency, err error) 357 358 // Height returns the wallet's internal processed consensus height 359 Height() (types.BlockHeight, error) 360 361 // AddressTransactions returns all of the transactions that are related 362 // to a given address. 363 AddressTransactions(types.UnlockHash) ([]ProcessedTransaction, error) 364 365 // AddressUnconfirmedHistory returns all of the unconfirmed 366 // transactions related to a given address. 367 AddressUnconfirmedTransactions(types.UnlockHash) ([]ProcessedTransaction, error) 368 369 // Transaction returns the transaction with the given id. The bool 370 // indicates whether the transaction is in the wallet database. The 371 // wallet only stores transactions that are related to the wallet. 372 Transaction(types.TransactionID) (ProcessedTransaction, bool, error) 373 374 // Transactions returns all of the transactions that were confirmed at 375 // heights [startHeight, endHeight]. Unconfirmed transactions are not 376 // included. 377 Transactions(startHeight types.BlockHeight, endHeight types.BlockHeight) ([]ProcessedTransaction, error) 378 379 // UnconfirmedTransactions returns all unconfirmed transactions 380 // relative to the wallet. 381 UnconfirmedTransactions() ([]ProcessedTransaction, error) 382 383 // RegisterTransaction takes a transaction and its parents and returns 384 // a TransactionBuilder which can be used to expand the transaction. 385 RegisterTransaction(t types.Transaction, parents []types.Transaction) (TransactionBuilder, error) 386 387 // Rescanning reports whether the wallet is currently rescanning the 388 // blockchain. 389 Rescanning() (bool, error) 390 391 // Settings returns the Wallet's current settings. 392 Settings() (WalletSettings, error) 393 394 // SetSettings sets the Wallet's settings. 395 SetSettings(WalletSettings) error 396 397 // StartTransaction is a convenience method that calls 398 // RegisterTransaction(types.Transaction{}, nil) 399 StartTransaction() (TransactionBuilder, error) 400 401 // SendSiacoins is a tool for sending siacoins from the wallet to an 402 // address. Sending money usually results in multiple transactions. The 403 // transactions are automatically given to the transaction pool, and 404 // are also returned to the caller. 405 SendSiacoins(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) 406 407 // SendSiacoinsMulti sends coins to multiple addresses. 408 SendSiacoinsMulti(outputs []types.SiacoinOutput) ([]types.Transaction, error) 409 410 // SendSiafunds is a tool for sending siafunds from the wallet to an 411 // address. Sending money usually results in multiple transactions. The 412 // transactions are automatically given to the transaction pool, and 413 // are also returned to the caller. 414 SendSiafunds(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) 415 416 // DustThreshold returns the quantity per byte below which a Currency is 417 // considered to be Dust. 418 DustThreshold() (types.Currency, error) 419 } 420 421 // WalletSettings control the behavior of the Wallet. 422 WalletSettings struct { 423 NoDefrag bool `json:"noDefrag"` 424 } 425 ) 426 427 // CalculateWalletTransactionID is a helper function for determining the id of 428 // a wallet transaction. 429 func CalculateWalletTransactionID(tid types.TransactionID, oid types.OutputID) WalletTransactionID { 430 return WalletTransactionID(crypto.HashAll(tid, oid)) 431 } 432 433 // SeedToString converts a wallet seed to a human friendly string. 434 func SeedToString(seed Seed, did mnemonics.DictionaryID) (string, error) { 435 fullChecksum := crypto.HashObject(seed) 436 checksumSeed := append(seed[:], fullChecksum[:SeedChecksumSize]...) 437 phrase, err := mnemonics.ToPhrase(checksumSeed, did) 438 if err != nil { 439 return "", err 440 } 441 return phrase.String(), nil 442 } 443 444 // StringToSeed converts a string to a wallet seed. 445 func StringToSeed(str string, did mnemonics.DictionaryID) (Seed, error) { 446 // Decode the string into the checksummed byte slice. 447 checksumSeedBytes, err := mnemonics.FromString(str, did) 448 if err != nil { 449 return Seed{}, err 450 } 451 452 // Copy the seed from the checksummed slice. 453 var seed Seed 454 copy(seed[:], checksumSeedBytes) 455 fullChecksum := crypto.HashObject(seed) 456 if len(checksumSeedBytes) != crypto.EntropySize+SeedChecksumSize || !bytes.Equal(fullChecksum[:SeedChecksumSize], checksumSeedBytes[crypto.EntropySize:]) { 457 return Seed{}, errors.New("seed failed checksum verification") 458 } 459 return seed, nil 460 }