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  }