github.com/ZuluSpl0it/Sia@v1.3.7/modules/wallet/transactionbuilder.go (about)

     1  package wallet
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"sort"
     7  
     8  	"github.com/NebulousLabs/Sia/crypto"
     9  	"github.com/NebulousLabs/Sia/encoding"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/types"
    12  
    13  	"github.com/coreos/bbolt"
    14  )
    15  
    16  var (
    17  	// errBuilderAlreadySigned indicates that the transaction builder has
    18  	// already added at least one successful signature to the transaction,
    19  	// meaning that future calls to Sign will result in an invalid transaction.
    20  	errBuilderAlreadySigned = errors.New("sign has already been called on this transaction builder, multiple calls can cause issues")
    21  
    22  	// errDustOutput indicates an output is not spendable because it is dust.
    23  	errDustOutput = errors.New("output is too small")
    24  
    25  	// errOutputTimelock indicates an output's timelock is still active.
    26  	errOutputTimelock = errors.New("wallet consensus set height is lower than the output timelock")
    27  
    28  	// errSpendHeightTooHigh indicates an output's spend height is greater than
    29  	// the allowed height.
    30  	errSpendHeightTooHigh = errors.New("output spend height exceeds the allowed height")
    31  )
    32  
    33  // transactionBuilder allows transactions to be manually constructed, including
    34  // the ability to fund transactions with siacoins and siafunds from the wallet.
    35  type transactionBuilder struct {
    36  	// 'signed' indicates that at least one transaction signature has been
    37  	// added to the wallet, meaning that future calls to 'Sign' will fail.
    38  	parents     []types.Transaction
    39  	signed      bool
    40  	transaction types.Transaction
    41  
    42  	newParents            []int
    43  	siacoinInputs         []int
    44  	siafundInputs         []int
    45  	transactionSignatures []int
    46  
    47  	wallet *Wallet
    48  }
    49  
    50  // addSignatures will sign a transaction using a spendable key, with support
    51  // for multisig spendable keys. Because of the restricted input, the function
    52  // is compatible with both siacoin inputs and siafund inputs.
    53  func addSignatures(txn *types.Transaction, cf types.CoveredFields, uc types.UnlockConditions, parentID crypto.Hash, spendKey spendableKey) (newSigIndices []int) {
    54  	// Try to find the matching secret key for each public key - some public
    55  	// keys may not have a match. Some secret keys may be used multiple times,
    56  	// which is why public keys are used as the outer loop.
    57  	totalSignatures := uint64(0)
    58  	for i, siaPubKey := range uc.PublicKeys {
    59  		// Search for the matching secret key to the public key.
    60  		for j := range spendKey.SecretKeys {
    61  			pubKey := spendKey.SecretKeys[j].PublicKey()
    62  			if !bytes.Equal(siaPubKey.Key, pubKey[:]) {
    63  				continue
    64  			}
    65  
    66  			// Found the right secret key, add a signature.
    67  			sig := types.TransactionSignature{
    68  				ParentID:       parentID,
    69  				CoveredFields:  cf,
    70  				PublicKeyIndex: uint64(i),
    71  			}
    72  			newSigIndices = append(newSigIndices, len(txn.TransactionSignatures))
    73  			txn.TransactionSignatures = append(txn.TransactionSignatures, sig)
    74  			sigIndex := len(txn.TransactionSignatures) - 1
    75  			sigHash := txn.SigHash(sigIndex)
    76  			encodedSig := crypto.SignHash(sigHash, spendKey.SecretKeys[j])
    77  			txn.TransactionSignatures[sigIndex].Signature = encodedSig[:]
    78  
    79  			// Count that the signature has been added, and break out of the
    80  			// secret key loop.
    81  			totalSignatures++
    82  			break
    83  		}
    84  
    85  		// If there are enough signatures to satisfy the unlock conditions,
    86  		// break out of the outer loop.
    87  		if totalSignatures == uc.SignaturesRequired {
    88  			break
    89  		}
    90  	}
    91  	return newSigIndices
    92  }
    93  
    94  // checkOutput is a helper function used to determine if an output is usable.
    95  func (w *Wallet) checkOutput(tx *bolt.Tx, currentHeight types.BlockHeight, id types.SiacoinOutputID, output types.SiacoinOutput, dustThreshold types.Currency) error {
    96  	// Check that an output is not dust
    97  	if output.Value.Cmp(dustThreshold) < 0 {
    98  		return errDustOutput
    99  	}
   100  	// Check that this output has not recently been spent by the wallet.
   101  	spendHeight, err := dbGetSpentOutput(tx, types.OutputID(id))
   102  	if err == nil {
   103  		if spendHeight+RespendTimeout > currentHeight {
   104  			return errSpendHeightTooHigh
   105  		}
   106  	}
   107  	outputUnlockConditions := w.keys[output.UnlockHash].UnlockConditions
   108  	if currentHeight < outputUnlockConditions.Timelock {
   109  		return errOutputTimelock
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // FundSiacoins will add a siacoin input of exactly 'amount' to the
   116  // transaction. A parent transaction may be needed to achieve an input with the
   117  // correct value. The siacoin input will not be signed until 'Sign' is called
   118  // on the transaction builder.
   119  func (tb *transactionBuilder) FundSiacoins(amount types.Currency) error {
   120  	// dustThreshold has to be obtained separate from the lock
   121  	dustThreshold, err := tb.wallet.DustThreshold()
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	tb.wallet.mu.Lock()
   127  	defer tb.wallet.mu.Unlock()
   128  
   129  	consensusHeight, err := dbGetConsensusHeight(tb.wallet.dbTx)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// Collect a value-sorted set of siacoin outputs.
   135  	var so sortedOutputs
   136  	err = dbForEachSiacoinOutput(tb.wallet.dbTx, func(scoid types.SiacoinOutputID, sco types.SiacoinOutput) {
   137  		so.ids = append(so.ids, scoid)
   138  		so.outputs = append(so.outputs, sco)
   139  	})
   140  	if err != nil {
   141  		return err
   142  	}
   143  	// Add all of the unconfirmed outputs as well.
   144  	for _, upt := range tb.wallet.unconfirmedProcessedTransactions {
   145  		for i, sco := range upt.Transaction.SiacoinOutputs {
   146  			// Determine if the output belongs to the wallet.
   147  			_, exists := tb.wallet.keys[sco.UnlockHash]
   148  			if !exists {
   149  				continue
   150  			}
   151  			so.ids = append(so.ids, upt.Transaction.SiacoinOutputID(uint64(i)))
   152  			so.outputs = append(so.outputs, sco)
   153  		}
   154  	}
   155  	sort.Sort(sort.Reverse(so))
   156  
   157  	// Create and fund a parent transaction that will add the correct amount of
   158  	// siacoins to the transaction.
   159  	var fund types.Currency
   160  	// potentialFund tracks the balance of the wallet including outputs that
   161  	// have been spent in other unconfirmed transactions recently. This is to
   162  	// provide the user with a more useful error message in the event that they
   163  	// are overspending.
   164  	var potentialFund types.Currency
   165  	parentTxn := types.Transaction{}
   166  	var spentScoids []types.SiacoinOutputID
   167  	for i := range so.ids {
   168  		scoid := so.ids[i]
   169  		sco := so.outputs[i]
   170  		// Check that the output can be spent.
   171  		if err := tb.wallet.checkOutput(tb.wallet.dbTx, consensusHeight, scoid, sco, dustThreshold); err != nil {
   172  			if err == errSpendHeightTooHigh {
   173  				potentialFund = potentialFund.Add(sco.Value)
   174  			}
   175  			continue
   176  		}
   177  
   178  		// Add a siacoin input for this output.
   179  		sci := types.SiacoinInput{
   180  			ParentID:         scoid,
   181  			UnlockConditions: tb.wallet.keys[sco.UnlockHash].UnlockConditions,
   182  		}
   183  		parentTxn.SiacoinInputs = append(parentTxn.SiacoinInputs, sci)
   184  		spentScoids = append(spentScoids, scoid)
   185  
   186  		// Add the output to the total fund
   187  		fund = fund.Add(sco.Value)
   188  		potentialFund = potentialFund.Add(sco.Value)
   189  		if fund.Cmp(amount) >= 0 {
   190  			break
   191  		}
   192  	}
   193  	if potentialFund.Cmp(amount) >= 0 && fund.Cmp(amount) < 0 {
   194  		return modules.ErrIncompleteTransactions
   195  	}
   196  	if fund.Cmp(amount) < 0 {
   197  		return modules.ErrLowBalance
   198  	}
   199  
   200  	// Create and add the output that will be used to fund the standard
   201  	// transaction.
   202  	parentUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	exactOutput := types.SiacoinOutput{
   208  		Value:      amount,
   209  		UnlockHash: parentUnlockConditions.UnlockHash(),
   210  	}
   211  	parentTxn.SiacoinOutputs = append(parentTxn.SiacoinOutputs, exactOutput)
   212  
   213  	// Create a refund output if needed.
   214  	if !amount.Equals(fund) {
   215  		refundUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   216  		if err != nil {
   217  			return err
   218  		}
   219  		refundOutput := types.SiacoinOutput{
   220  			Value:      fund.Sub(amount),
   221  			UnlockHash: refundUnlockConditions.UnlockHash(),
   222  		}
   223  		parentTxn.SiacoinOutputs = append(parentTxn.SiacoinOutputs, refundOutput)
   224  	}
   225  
   226  	// Sign all of the inputs to the parent transaction.
   227  	for _, sci := range parentTxn.SiacoinInputs {
   228  		addSignatures(&parentTxn, types.FullCoveredFields, sci.UnlockConditions, crypto.Hash(sci.ParentID), tb.wallet.keys[sci.UnlockConditions.UnlockHash()])
   229  	}
   230  	// Mark the parent output as spent. Must be done after the transaction is
   231  	// finished because otherwise the txid and output id will change.
   232  	err = dbPutSpentOutput(tb.wallet.dbTx, types.OutputID(parentTxn.SiacoinOutputID(0)), consensusHeight)
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	// Add the exact output.
   238  	newInput := types.SiacoinInput{
   239  		ParentID:         parentTxn.SiacoinOutputID(0),
   240  		UnlockConditions: parentUnlockConditions,
   241  	}
   242  	tb.newParents = append(tb.newParents, len(tb.parents))
   243  	tb.parents = append(tb.parents, parentTxn)
   244  	tb.siacoinInputs = append(tb.siacoinInputs, len(tb.transaction.SiacoinInputs))
   245  	tb.transaction.SiacoinInputs = append(tb.transaction.SiacoinInputs, newInput)
   246  
   247  	// Mark all outputs that were spent as spent.
   248  	for _, scoid := range spentScoids {
   249  		err = dbPutSpentOutput(tb.wallet.dbTx, types.OutputID(scoid), consensusHeight)
   250  		if err != nil {
   251  			return err
   252  		}
   253  	}
   254  	return nil
   255  }
   256  
   257  // FundSiafunds will add a siafund input of exactly 'amount' to the
   258  // transaction. A parent transaction may be needed to achieve an input with the
   259  // correct value. The siafund input will not be signed until 'Sign' is called
   260  // on the transaction builder.
   261  func (tb *transactionBuilder) FundSiafunds(amount types.Currency) error {
   262  	tb.wallet.mu.Lock()
   263  	defer tb.wallet.mu.Unlock()
   264  
   265  	consensusHeight, err := dbGetConsensusHeight(tb.wallet.dbTx)
   266  	if err != nil {
   267  		return err
   268  	}
   269  
   270  	// Create and fund a parent transaction that will add the correct amount of
   271  	// siafunds to the transaction.
   272  	var fund types.Currency
   273  	var potentialFund types.Currency
   274  	parentTxn := types.Transaction{}
   275  	var spentSfoids []types.SiafundOutputID
   276  	c := tb.wallet.dbTx.Bucket(bucketSiafundOutputs).Cursor()
   277  	for idBytes, sfoBytes := c.First(); idBytes != nil; idBytes, sfoBytes = c.Next() {
   278  		var sfoid types.SiafundOutputID
   279  		var sfo types.SiafundOutput
   280  		if err := encoding.Unmarshal(idBytes, &sfoid); err != nil {
   281  			return err
   282  		} else if err := encoding.Unmarshal(sfoBytes, &sfo); err != nil {
   283  			return err
   284  		}
   285  
   286  		// Check that this output has not recently been spent by the wallet.
   287  		spendHeight, err := dbGetSpentOutput(tb.wallet.dbTx, types.OutputID(sfoid))
   288  		if err != nil {
   289  			// mimic map behavior: no entry means zero value
   290  			spendHeight = 0
   291  		}
   292  		// Prevent an underflow error.
   293  		allowedHeight := consensusHeight - RespendTimeout
   294  		if consensusHeight < RespendTimeout {
   295  			allowedHeight = 0
   296  		}
   297  		if spendHeight > allowedHeight {
   298  			potentialFund = potentialFund.Add(sfo.Value)
   299  			continue
   300  		}
   301  		outputUnlockConditions := tb.wallet.keys[sfo.UnlockHash].UnlockConditions
   302  		if consensusHeight < outputUnlockConditions.Timelock {
   303  			continue
   304  		}
   305  
   306  		// Add a siafund input for this output.
   307  		parentClaimUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   308  		if err != nil {
   309  			return err
   310  		}
   311  		sfi := types.SiafundInput{
   312  			ParentID:         sfoid,
   313  			UnlockConditions: outputUnlockConditions,
   314  			ClaimUnlockHash:  parentClaimUnlockConditions.UnlockHash(),
   315  		}
   316  		parentTxn.SiafundInputs = append(parentTxn.SiafundInputs, sfi)
   317  		spentSfoids = append(spentSfoids, sfoid)
   318  
   319  		// Add the output to the total fund
   320  		fund = fund.Add(sfo.Value)
   321  		potentialFund = potentialFund.Add(sfo.Value)
   322  		if fund.Cmp(amount) >= 0 {
   323  			break
   324  		}
   325  	}
   326  	if potentialFund.Cmp(amount) >= 0 && fund.Cmp(amount) < 0 {
   327  		return modules.ErrIncompleteTransactions
   328  	}
   329  	if fund.Cmp(amount) < 0 {
   330  		return modules.ErrLowBalance
   331  	}
   332  
   333  	// Create and add the output that will be used to fund the standard
   334  	// transaction.
   335  	parentUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	exactOutput := types.SiafundOutput{
   340  		Value:      amount,
   341  		UnlockHash: parentUnlockConditions.UnlockHash(),
   342  	}
   343  	parentTxn.SiafundOutputs = append(parentTxn.SiafundOutputs, exactOutput)
   344  
   345  	// Create a refund output if needed.
   346  	if !amount.Equals(fund) {
   347  		refundUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   348  		if err != nil {
   349  			return err
   350  		}
   351  		refundOutput := types.SiafundOutput{
   352  			Value:      fund.Sub(amount),
   353  			UnlockHash: refundUnlockConditions.UnlockHash(),
   354  		}
   355  		parentTxn.SiafundOutputs = append(parentTxn.SiafundOutputs, refundOutput)
   356  	}
   357  
   358  	// Sign all of the inputs to the parent transaction.
   359  	for _, sfi := range parentTxn.SiafundInputs {
   360  		addSignatures(&parentTxn, types.FullCoveredFields, sfi.UnlockConditions, crypto.Hash(sfi.ParentID), tb.wallet.keys[sfi.UnlockConditions.UnlockHash()])
   361  	}
   362  
   363  	// Add the exact output.
   364  	claimUnlockConditions, err := tb.wallet.nextPrimarySeedAddress(tb.wallet.dbTx)
   365  	if err != nil {
   366  		return err
   367  	}
   368  	newInput := types.SiafundInput{
   369  		ParentID:         parentTxn.SiafundOutputID(0),
   370  		UnlockConditions: parentUnlockConditions,
   371  		ClaimUnlockHash:  claimUnlockConditions.UnlockHash(),
   372  	}
   373  	tb.newParents = append(tb.newParents, len(tb.parents))
   374  	tb.parents = append(tb.parents, parentTxn)
   375  	tb.siafundInputs = append(tb.siafundInputs, len(tb.transaction.SiafundInputs))
   376  	tb.transaction.SiafundInputs = append(tb.transaction.SiafundInputs, newInput)
   377  
   378  	// Mark all outputs that were spent as spent.
   379  	for _, sfoid := range spentSfoids {
   380  		err = dbPutSpentOutput(tb.wallet.dbTx, types.OutputID(sfoid), consensusHeight)
   381  		if err != nil {
   382  			return err
   383  		}
   384  	}
   385  	return nil
   386  }
   387  
   388  // UnconfirmedParents returns the unconfirmed parents of the transaction set
   389  // that is being constructed by the transaction builder.
   390  func (tb *transactionBuilder) UnconfirmedParents() (parents []types.Transaction, err error) {
   391  	// Currently we don't need to call UnconfirmedParents after the transaction
   392  	// was signed so we don't allow doing that. If for some reason our
   393  	// requirements change, we can remove this check. The only downside is,
   394  	// that it might lead to transactions being returned that are not actually
   395  	// parents in case the signed transaction already has child transactions.
   396  	if tb.signed {
   397  		return nil, errBuilderAlreadySigned
   398  	}
   399  	addedParents := make(map[types.TransactionID]struct{})
   400  	for _, p := range tb.parents {
   401  		for _, sci := range p.SiacoinInputs {
   402  			tSet := tb.wallet.tpool.TransactionSet(crypto.Hash(sci.ParentID))
   403  			for _, txn := range tSet {
   404  				// Add the transaction to the parents.
   405  				txnID := txn.ID()
   406  				if _, exists := addedParents[txnID]; exists {
   407  					continue
   408  				}
   409  				addedParents[txnID] = struct{}{}
   410  				parents = append(parents, txn)
   411  
   412  				// When we found the transaction that contains the output that
   413  				// is spent by sci we stop to avoid adding child transactions.
   414  				for i := range txn.SiacoinOutputs {
   415  					if txn.SiacoinOutputID(uint64(i)) == sci.ParentID {
   416  						break
   417  					}
   418  				}
   419  			}
   420  		}
   421  	}
   422  	return
   423  }
   424  
   425  // AddParents adds a set of parents to the transaction.
   426  func (tb *transactionBuilder) AddParents(newParents []types.Transaction) {
   427  	tb.parents = append(tb.parents, newParents...)
   428  }
   429  
   430  // AddMinerFee adds a miner fee to the transaction, returning the index of the
   431  // miner fee within the transaction.
   432  func (tb *transactionBuilder) AddMinerFee(fee types.Currency) uint64 {
   433  	tb.transaction.MinerFees = append(tb.transaction.MinerFees, fee)
   434  	return uint64(len(tb.transaction.MinerFees) - 1)
   435  }
   436  
   437  // AddSiacoinInput adds a siacoin input to the transaction, returning the index
   438  // of the siacoin input within the transaction. When 'Sign' gets called, this
   439  // input will be left unsigned.
   440  func (tb *transactionBuilder) AddSiacoinInput(input types.SiacoinInput) uint64 {
   441  	tb.transaction.SiacoinInputs = append(tb.transaction.SiacoinInputs, input)
   442  	return uint64(len(tb.transaction.SiacoinInputs) - 1)
   443  }
   444  
   445  // AddSiacoinOutput adds a siacoin output to the transaction, returning the
   446  // index of the siacoin output within the transaction.
   447  func (tb *transactionBuilder) AddSiacoinOutput(output types.SiacoinOutput) uint64 {
   448  	tb.transaction.SiacoinOutputs = append(tb.transaction.SiacoinOutputs, output)
   449  	return uint64(len(tb.transaction.SiacoinOutputs) - 1)
   450  }
   451  
   452  // AddFileContract adds a file contract to the transaction, returning the index
   453  // of the file contract within the transaction.
   454  func (tb *transactionBuilder) AddFileContract(fc types.FileContract) uint64 {
   455  	tb.transaction.FileContracts = append(tb.transaction.FileContracts, fc)
   456  	return uint64(len(tb.transaction.FileContracts) - 1)
   457  }
   458  
   459  // AddFileContractRevision adds a file contract revision to the transaction,
   460  // returning the index of the file contract revision within the transaction.
   461  // When 'Sign' gets called, this revision will be left unsigned.
   462  func (tb *transactionBuilder) AddFileContractRevision(fcr types.FileContractRevision) uint64 {
   463  	tb.transaction.FileContractRevisions = append(tb.transaction.FileContractRevisions, fcr)
   464  	return uint64(len(tb.transaction.FileContractRevisions) - 1)
   465  }
   466  
   467  // AddStorageProof adds a storage proof to the transaction, returning the index
   468  // of the storage proof within the transaction.
   469  func (tb *transactionBuilder) AddStorageProof(sp types.StorageProof) uint64 {
   470  	tb.transaction.StorageProofs = append(tb.transaction.StorageProofs, sp)
   471  	return uint64(len(tb.transaction.StorageProofs) - 1)
   472  }
   473  
   474  // AddSiafundInput adds a siafund input to the transaction, returning the index
   475  // of the siafund input within the transaction. When 'Sign' is called, this
   476  // input will be left unsigned.
   477  func (tb *transactionBuilder) AddSiafundInput(input types.SiafundInput) uint64 {
   478  	tb.transaction.SiafundInputs = append(tb.transaction.SiafundInputs, input)
   479  	return uint64(len(tb.transaction.SiafundInputs) - 1)
   480  }
   481  
   482  // AddSiafundOutput adds a siafund output to the transaction, returning the
   483  // index of the siafund output within the transaction.
   484  func (tb *transactionBuilder) AddSiafundOutput(output types.SiafundOutput) uint64 {
   485  	tb.transaction.SiafundOutputs = append(tb.transaction.SiafundOutputs, output)
   486  	return uint64(len(tb.transaction.SiafundOutputs) - 1)
   487  }
   488  
   489  // AddArbitraryData adds arbitrary data to the transaction, returning the index
   490  // of the data within the transaction.
   491  func (tb *transactionBuilder) AddArbitraryData(arb []byte) uint64 {
   492  	tb.transaction.ArbitraryData = append(tb.transaction.ArbitraryData, arb)
   493  	return uint64(len(tb.transaction.ArbitraryData) - 1)
   494  }
   495  
   496  // AddTransactionSignature adds a transaction signature to the transaction,
   497  // returning the index of the signature within the transaction. The signature
   498  // should already be valid, and shouldn't sign any of the inputs that were
   499  // added by calling 'FundSiacoins' or 'FundSiafunds'.
   500  func (tb *transactionBuilder) AddTransactionSignature(sig types.TransactionSignature) uint64 {
   501  	tb.transaction.TransactionSignatures = append(tb.transaction.TransactionSignatures, sig)
   502  	return uint64(len(tb.transaction.TransactionSignatures) - 1)
   503  }
   504  
   505  // Drop discards all of the outputs in a transaction, returning them to the
   506  // pool so that other transactions may use them. 'Drop' should only be called
   507  // if a transaction is both unsigned and will not be used any further.
   508  func (tb *transactionBuilder) Drop() {
   509  	tb.wallet.mu.Lock()
   510  	defer tb.wallet.mu.Unlock()
   511  
   512  	// Iterate through all parents and the transaction itself and restore all
   513  	// outputs to the list of available outputs.
   514  	txns := append(tb.parents, tb.transaction)
   515  	for _, txn := range txns {
   516  		for _, sci := range txn.SiacoinInputs {
   517  			dbDeleteSpentOutput(tb.wallet.dbTx, types.OutputID(sci.ParentID))
   518  		}
   519  	}
   520  
   521  	tb.parents = nil
   522  	tb.signed = false
   523  	tb.transaction = types.Transaction{}
   524  
   525  	tb.newParents = nil
   526  	tb.siacoinInputs = nil
   527  	tb.siafundInputs = nil
   528  	tb.transactionSignatures = nil
   529  }
   530  
   531  // Sign will sign any inputs added by 'FundSiacoins' or 'FundSiafunds' and
   532  // return a transaction set that contains all parents prepended to the
   533  // transaction. If more fields need to be added, a new transaction builder will
   534  // need to be created.
   535  //
   536  // If the whole transaction flag is set to true, then the whole transaction
   537  // flag will be set in the covered fields object. If the whole transaction flag
   538  // is set to false, then the covered fields object will cover all fields that
   539  // have already been added to the transaction, but will also leave room for
   540  // more fields to be added.
   541  //
   542  // Sign should not be called more than once. If, for some reason, there is an
   543  // error while calling Sign, the builder should be dropped.
   544  func (tb *transactionBuilder) Sign(wholeTransaction bool) ([]types.Transaction, error) {
   545  	if tb.signed {
   546  		return nil, errBuilderAlreadySigned
   547  	}
   548  
   549  	// Create the coveredfields struct.
   550  	var coveredFields types.CoveredFields
   551  	if wholeTransaction {
   552  		coveredFields = types.CoveredFields{WholeTransaction: true}
   553  	} else {
   554  		for i := range tb.transaction.MinerFees {
   555  			coveredFields.MinerFees = append(coveredFields.MinerFees, uint64(i))
   556  		}
   557  		for i := range tb.transaction.SiacoinInputs {
   558  			coveredFields.SiacoinInputs = append(coveredFields.SiacoinInputs, uint64(i))
   559  		}
   560  		for i := range tb.transaction.SiacoinOutputs {
   561  			coveredFields.SiacoinOutputs = append(coveredFields.SiacoinOutputs, uint64(i))
   562  		}
   563  		for i := range tb.transaction.FileContracts {
   564  			coveredFields.FileContracts = append(coveredFields.FileContracts, uint64(i))
   565  		}
   566  		for i := range tb.transaction.FileContractRevisions {
   567  			coveredFields.FileContractRevisions = append(coveredFields.FileContractRevisions, uint64(i))
   568  		}
   569  		for i := range tb.transaction.StorageProofs {
   570  			coveredFields.StorageProofs = append(coveredFields.StorageProofs, uint64(i))
   571  		}
   572  		for i := range tb.transaction.SiafundInputs {
   573  			coveredFields.SiafundInputs = append(coveredFields.SiafundInputs, uint64(i))
   574  		}
   575  		for i := range tb.transaction.SiafundOutputs {
   576  			coveredFields.SiafundOutputs = append(coveredFields.SiafundOutputs, uint64(i))
   577  		}
   578  		for i := range tb.transaction.ArbitraryData {
   579  			coveredFields.ArbitraryData = append(coveredFields.ArbitraryData, uint64(i))
   580  		}
   581  	}
   582  	// TransactionSignatures don't get covered by the 'WholeTransaction' flag,
   583  	// and must be covered manually.
   584  	for i := range tb.transaction.TransactionSignatures {
   585  		coveredFields.TransactionSignatures = append(coveredFields.TransactionSignatures, uint64(i))
   586  	}
   587  
   588  	// For each siacoin input in the transaction that we added, provide a
   589  	// signature.
   590  	tb.wallet.mu.RLock()
   591  	defer tb.wallet.mu.RUnlock()
   592  	for _, inputIndex := range tb.siacoinInputs {
   593  		input := tb.transaction.SiacoinInputs[inputIndex]
   594  		key, ok := tb.wallet.keys[input.UnlockConditions.UnlockHash()]
   595  		if !ok {
   596  			return nil, errors.New("transaction builder added an input that it cannot sign")
   597  		}
   598  		newSigIndices := addSignatures(&tb.transaction, coveredFields, input.UnlockConditions, crypto.Hash(input.ParentID), key)
   599  		tb.transactionSignatures = append(tb.transactionSignatures, newSigIndices...)
   600  		tb.signed = true // Signed is set to true after one successful signature to indicate that future signings can cause issues.
   601  	}
   602  	for _, inputIndex := range tb.siafundInputs {
   603  		input := tb.transaction.SiafundInputs[inputIndex]
   604  		key, ok := tb.wallet.keys[input.UnlockConditions.UnlockHash()]
   605  		if !ok {
   606  			return nil, errors.New("transaction builder added an input that it cannot sign")
   607  		}
   608  		newSigIndices := addSignatures(&tb.transaction, coveredFields, input.UnlockConditions, crypto.Hash(input.ParentID), key)
   609  		tb.transactionSignatures = append(tb.transactionSignatures, newSigIndices...)
   610  		tb.signed = true // Signed is set to true after one successful signature to indicate that future signings can cause issues.
   611  	}
   612  
   613  	// Get the transaction set and delete the transaction from the registry.
   614  	txnSet := append(tb.parents, tb.transaction)
   615  	return txnSet, nil
   616  }
   617  
   618  // ViewTransaction returns a transaction-in-progress along with all of its
   619  // parents, specified by id. An error is returned if the id is invalid.  Note
   620  // that ids become invalid for a transaction after 'SignTransaction' has been
   621  // called because the transaction gets deleted.
   622  func (tb *transactionBuilder) View() (types.Transaction, []types.Transaction) {
   623  	return tb.transaction, tb.parents
   624  }
   625  
   626  // ViewAdded returns all of the siacoin inputs, siafund inputs, and parent
   627  // transactions that have been automatically added by the builder.
   628  func (tb *transactionBuilder) ViewAdded() (newParents, siacoinInputs, siafundInputs, transactionSignatures []int) {
   629  	return tb.newParents, tb.siacoinInputs, tb.siafundInputs, tb.transactionSignatures
   630  }
   631  
   632  // registerTransaction takes a transaction and its parents and returns a
   633  // wallet.TransactionBuilder which can be used to expand the transaction. The
   634  // most typical call is 'RegisterTransaction(types.Transaction{}, nil)', which
   635  // registers a new transaction without parents.
   636  func (w *Wallet) registerTransaction(t types.Transaction, parents []types.Transaction) *transactionBuilder {
   637  	// Create a deep copy of the transaction and parents by encoding them. A
   638  	// deep copy ensures that there are no pointer or slice related errors -
   639  	// the builder will be working directly on the transaction, and the
   640  	// transaction may be in use elsewhere (in this case, the host is using the
   641  	// transaction.
   642  	pBytes := encoding.Marshal(parents)
   643  	var pCopy []types.Transaction
   644  	err := encoding.Unmarshal(pBytes, &pCopy)
   645  	if err != nil {
   646  		panic(err)
   647  	}
   648  	tBytes := encoding.Marshal(t)
   649  	var tCopy types.Transaction
   650  	err = encoding.Unmarshal(tBytes, &tCopy)
   651  	if err != nil {
   652  		panic(err)
   653  	}
   654  	return &transactionBuilder{
   655  		parents:     pCopy,
   656  		transaction: tCopy,
   657  
   658  		wallet: w,
   659  	}
   660  }
   661  
   662  // RegisterTransaction takes a transaction and its parents and returns a
   663  // modules.TransactionBuilder which can be used to expand the transaction. The
   664  // most typical call is 'RegisterTransaction(types.Transaction{}, nil)', which
   665  // registers a new transaction without parents.
   666  func (w *Wallet) RegisterTransaction(t types.Transaction, parents []types.Transaction) (modules.TransactionBuilder, error) {
   667  	if err := w.tg.Add(); err != nil {
   668  		return nil, err
   669  	}
   670  	defer w.tg.Done()
   671  	w.mu.Lock()
   672  	defer w.mu.Unlock()
   673  	return w.registerTransaction(t, parents), nil
   674  }
   675  
   676  // StartTransaction is a convenience function that calls
   677  // RegisterTransaction(types.Transaction{}, nil).
   678  func (w *Wallet) StartTransaction() (modules.TransactionBuilder, error) {
   679  	if err := w.tg.Add(); err != nil {
   680  		return nil, err
   681  	}
   682  	defer w.tg.Done()
   683  	return w.RegisterTransaction(types.Transaction{}, nil)
   684  }