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), &current)
   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  }