author.go raw

   1  // Package txauthor provides transaction creation code for wallets.
   2  package txauthor
   3  
   4  import (
   5  	"errors"
   6  	"github.com/p9c/p9/pkg/amt"
   7  	"github.com/p9c/p9/pkg/chaincfg"
   8  	
   9  	"github.com/p9c/p9/pkg/txrules"
  10  	"github.com/p9c/p9/pkg/txscript"
  11  	"github.com/p9c/p9/pkg/txsizes"
  12  	h "github.com/p9c/p9/pkg/util/helpers"
  13  	"github.com/p9c/p9/pkg/wire"
  14  )
  15  
  16  type (
  17  	// InputSource provides transaction inputs referencing spendable outputs to construct a transaction outputting some
  18  	// target amount. If the target amount can not be satisified, this can be signaled by returning a total amount less
  19  	// than the target or by returning a more detailed error implementing InputSourceError.
  20  	InputSource func(target amt.Amount) (
  21  		total amt.Amount, inputs []*wire.TxIn,
  22  		inputValues []amt.Amount, scripts [][]byte, e error,
  23  	)
  24  	// InputSourceError describes the failure to provide enough input value from unspent transaction outputs to meet a
  25  	// target amount. A typed error is used so input sources can provide their own implementations describing the reason
  26  	// for the error, for example, due to spendable policies or locked coins rather than the wallet not having enough
  27  	// available input value.
  28  	InputSourceError interface {
  29  		error
  30  		InputSourceError()
  31  	}
  32  	// Default implementation of InputSourceError.
  33  	insufficientFundsError struct{}
  34  	// AuthoredTx holds the state of a newly-created transaction and the change output (if one was added).
  35  	AuthoredTx struct {
  36  		Tx              *wire.MsgTx
  37  		PrevScripts     [][]byte
  38  		PrevInputValues []amt.Amount
  39  		TotalInput      amt.Amount
  40  		ChangeIndex     int // negative if no change
  41  	}
  42  	// ChangeSource provides P2PKH change output scripts for transaction creation.
  43  	ChangeSource func() ([]byte, error)
  44  	// SecretsSource provides private keys and redeem scripts necessary for constructing transaction input signatures.
  45  	// Secrets are looked up by the corresponding Address for the previous output script. Addresses for lookup are
  46  	// created using the source's blockchain parameters and means a single SecretsSource can only manage secrets for a
  47  	// single chain.
  48  	//
  49  	// TODO: Rewrite this interface to look up private keys and redeem scripts for pubkeys, pubkey hashes, script
  50  	//  hashes, etc. as separate interface methods.
  51  	//
  52  	// This would remove the ChainParams requirement of the interface and could avoid unnecessary conversions from
  53  	// previous output scripts to Addresses. This can not be done without modifications to the txscript package.
  54  	SecretsSource interface {
  55  		txscript.KeyDB
  56  		txscript.ScriptDB
  57  		ChainParams() *chaincfg.Params
  58  	}
  59  )
  60  
  61  func (insufficientFundsError) InputSourceError() {
  62  }
  63  func (insufficientFundsError) Error() string {
  64  	return "insufficient funds available to construct transaction"
  65  }
  66  
  67  // NewUnsignedTransaction creates an unsigned transaction paying to one or more non-change outputs. An appropriate
  68  // transaction fee is included based on the transaction size.
  69  //
  70  // Transaction inputs are chosen from repeated calls to fetchInputs with increasing targets amounts.
  71  //
  72  // If any remaining output value can be returned to the wallet via a change output without violating mempool dust rules,
  73  // a P2WPKH change output is appended to the transaction outputs. Since the change output may not be necessary,
  74  // fetchChange is called zero or one times to generate this script. This function must return a P2WPKH script or
  75  // smaller, otherwise fee estimation will be incorrect.
  76  //
  77  // If successful, the transaction, total input value spent, and all previous output scripts are returned. If the input
  78  // source was unable to provide enough input value to pay for every output any any necessary fees, an InputSourceError
  79  // is returned.
  80  //
  81  // BUGS: Fee estimation may be off when redeeming non-compressed P2PKH outputs.
  82  func NewUnsignedTransaction(
  83  	outputs []*wire.TxOut, relayFeePerKb amt.Amount,
  84  	fetchInputs InputSource, fetchChange ChangeSource,
  85  ) (*AuthoredTx, error) {
  86  	targetAmount := h.SumOutputValues(outputs)
  87  	estimatedSize := txsizes.EstimateVirtualSize(1, 0, 0, outputs, true)
  88  	targetFee := txrules.FeeForSerializeSize(relayFeePerKb, estimatedSize)
  89  	for {
  90  		inputAmount, inputs, inputValues, scripts, e := fetchInputs(targetAmount + targetFee)
  91  		if e != nil {
  92  			return nil, e
  93  		}
  94  		if inputAmount < targetAmount+targetFee {
  95  			return nil, insufficientFundsError{}
  96  		}
  97  		// We count the types of inputs, which we'll use to estimate the vsize of the transaction.
  98  		var nested, p2wpkh, p2pkh int
  99  		for _, /*pkScript*/ _ = range scripts {
 100  			switch {
 101  			// // If this is a p2sh output, we assume this is a nested P2WKH.
 102  			// case txscript.IsPayToScriptHash(pkScript):
 103  			// 	nested++
 104  			// case txscript.IsPayToWitnessPubKeyHash(pkScript):
 105  			// 	p2wpkh++
 106  			default:
 107  				p2pkh++
 108  			}
 109  		}
 110  		maxSignedSize := txsizes.EstimateVirtualSize(
 111  			p2pkh, p2wpkh,
 112  			nested, outputs, true,
 113  		)
 114  		maxRequiredFee := txrules.FeeForSerializeSize(relayFeePerKb, maxSignedSize)
 115  		remainingAmount := inputAmount - targetAmount
 116  		if remainingAmount < maxRequiredFee {
 117  			targetFee = maxRequiredFee
 118  			continue
 119  		}
 120  		unsignedTransaction := &wire.MsgTx{
 121  			Version:  wire.TxVersion,
 122  			TxIn:     inputs,
 123  			TxOut:    outputs,
 124  			LockTime: 0,
 125  		}
 126  		changeIndex := -1
 127  		changeAmount := inputAmount - targetAmount - maxRequiredFee
 128  		if changeAmount != 0 && !txrules.IsDustAmount(
 129  			changeAmount,
 130  			txsizes.P2PKHPkScriptSize, relayFeePerKb,
 131  		) {
 132  			changeScript, e := fetchChange()
 133  			if e != nil {
 134  				return nil, e
 135  			}
 136  			if len(changeScript) > txsizes.P2PKHPkScriptSize {
 137  				return nil, errors.New(
 138  					"fee estimation requires change " +
 139  						"scripts no larger than P2WPKH output scripts",
 140  				)
 141  			}
 142  			change := wire.NewTxOut(int64(changeAmount), changeScript)
 143  			l := len(outputs)
 144  			unsignedTransaction.TxOut = append(outputs[:l:l], change)
 145  			changeIndex = l
 146  		}
 147  		return &AuthoredTx{
 148  			Tx:              unsignedTransaction,
 149  			PrevScripts:     scripts,
 150  			PrevInputValues: inputValues,
 151  			TotalInput:      inputAmount,
 152  			ChangeIndex:     changeIndex,
 153  		}, nil
 154  	}
 155  }
 156  
 157  // RandomizeOutputPosition randomizes the position of a transaction's output by swapping it with a random output. The
 158  // new index is returned. This should be done before signing.
 159  func RandomizeOutputPosition(outputs []*wire.TxOut, index int) int {
 160  	r := cprng.Int31n(int32(len(outputs)))
 161  	outputs[r], outputs[index] = outputs[index], outputs[r]
 162  	return int(r)
 163  }
 164  
 165  // RandomizeChangePosition randomizes the position of an authored transaction's change output. This should be done
 166  // before signing.
 167  func (tx *AuthoredTx) RandomizeChangePosition() {
 168  	tx.ChangeIndex = RandomizeOutputPosition(tx.Tx.TxOut, tx.ChangeIndex)
 169  }
 170  
 171  // AddAllInputScripts modifies transaction a transaction by adding inputs
 172  // scripts for each input. Previous output scripts being redeemed by each input
 173  // are passed in prevPkScripts and the slice length must match the number of
 174  // inputs. Private keys and redeem scripts are looked up using a SecretsSource
 175  // based on the previous output script.
 176  func AddAllInputScripts(
 177  	tx *wire.MsgTx, prevPkScripts [][]byte, inputValues []amt.Amount,
 178  	secrets SecretsSource,
 179  ) (e error) {
 180  	inputs := tx.TxIn
 181  	// hashCache := txscript.NewTxSigHashes(tx)
 182  	chainParams := secrets.ChainParams()
 183  	if len(inputs) != len(prevPkScripts) {
 184  		return errors.New(
 185  			"tx.TxIn and prevPkScripts slices must " +
 186  				"have equal length",
 187  		)
 188  	}
 189  	for i := range inputs {
 190  		pkScript := prevPkScripts[i]
 191  		switch {
 192  		// // If this is a p2sh output, who's script hash pre-image is a witness program,
 193  		// // then we'll need to use a modified signing function which generates both the
 194  		// // sigScript, and the witness script.
 195  		// case txscript.IsPayToScriptHash(pkScript):
 196  		// 	e := spendNestedWitnessPubKeyHash(inputs[i], pkScript,
 197  		// 		int64(inputValues[i]), chainParams, secrets,
 198  		// 		tx, hashCache, i)
 199  		// 	if e != nil  {
 200  		// 				// 		return e
 201  		// 	}
 202  		// case txscript.IsPayToWitnessPubKeyHash(pkScript):
 203  		// 	e := spendWitnessKeyHash(inputs[i], pkScript,
 204  		// 		int64(inputValues[i]), chainParams, secrets,
 205  		// 		tx, hashCache, i)
 206  		// 	if e != nil  {
 207  		// 				// 		return e
 208  		// 	}
 209  		default:
 210  			sigScript := inputs[i].SignatureScript
 211  			var script []byte
 212  			script, e = txscript.SignTxOutput(
 213  				chainParams, tx, i,
 214  				pkScript, txscript.SigHashAll, secrets, secrets,
 215  				sigScript,
 216  			)
 217  			if e != nil {
 218  				return e
 219  			}
 220  			inputs[i].SignatureScript = script
 221  		}
 222  	}
 223  	return nil
 224  }
 225  
 226  // spendWitnessKeyHash generates, and sets a valid witness for spending the
 227  // // passed pkScript with the specified input amount. The input amount *must*
 228  // // correspond to the output value of the previous pkScript, or else verification
 229  // // will fail since the new sighash digest algorithm defined in BIP0143 includes
 230  // // the input value in the sighash.
 231  // func spendWitnessKeyHash(txIn *wire.TxIn, pkScript []byte,
 232  // 	inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource,
 233  // 	tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) (e error) {
 234  // 	// First obtain the key pair associated with this p2wkh address.
 235  // 	_, addrs, _, e = txscript.ExtractPkScriptAddrs(pkScript,
 236  // 		chainParams)
 237  // 	if e != nil  {
 238  // 		// 		return e
 239  // 	}
 240  // 	privKey, compressed, e := secrets.GetKey(addrs[0])
 241  // 	if e != nil  {
 242  // 		// 		return e
 243  // 	}
 244  // 	pubKey := privKey.PubKey()
 245  // 	// Once we have the key pair, generate a p2wkh address type, respecting the compression type of the generated key.
 246  // 	var pubKeyHash []byte
 247  // 	if compressed {
 248  // 		pubKeyHash = btcaddr.Hash160(pubKey.SerializeCompressed())
 249  // 	} else {
 250  // 		pubKeyHash = btcaddr.Hash160(pubKey.SerializeUncompressed())
 251  // 	}
 252  // 	p2wkhAddr, e := util.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams)
 253  // 	if e != nil  {
 254  // 		// 		return e
 255  // 	}
 256  // 	// With the concrete address type, we can now generate the corresponding witness
 257  // 	// program to be used to generate a valid witness which will allow us to spend
 258  // 	// this output.
 259  // 	witnessProgram, e := txscript.PayToAddrScript(p2wkhAddr)
 260  // 	if e != nil  {
 261  // 		// 		return e
 262  // 	}
 263  // 	witnessScript, e := txscript.WitnessSignature(tx, hashCache, idx,
 264  // 		inputValue, witnessProgram, txscript.SigHashAll, privKey, true)
 265  // 	if e != nil  {
 266  // 		// 		return e
 267  // 	}
 268  // 	txIn.Witness = witnessScript
 269  // 	return nil
 270  // }
 271  
 272  // spendNestedWitnessPubKey generates both a sigScript, and valid witness for
 273  // // spending the passed pkScript with the specified input amount. The generated
 274  // // sigScript is the version 0 p2wkh witness program corresponding to the queried
 275  // // key. The witness stack is identical to that of one which spends a regular
 276  // // p2wkh output. The input amount *must* correspond to the output value of the
 277  // // previous pkScript, or else verification will fail since the new sighash
 278  // // digest algorithm defined in BIP0143 includes the input value in the sighash.
 279  // func spendNestedWitnessPubKeyHash(txIn *wire.TxIn, pkScript []byte,
 280  // 	inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource,
 281  // 	tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) (e error) {
 282  // 	// First we need to obtain the key pair related to this p2sh output.
 283  // 	_, addrs, _, e = txscript.ExtractPkScriptAddrs(pkScript,
 284  // 		chainParams)
 285  // 	if e != nil  {
 286  // 		// 		return e
 287  // 	}
 288  // 	privKey, compressed, e := secrets.GetKey(addrs[0])
 289  // 	if e != nil  {
 290  // 		// 		return e
 291  // 	}
 292  // 	pubKey := privKey.PubKey()
 293  // 	var pubKeyHash []byte
 294  // 	if compressed {
 295  // 		pubKeyHash = btcaddr.Hash160(pubKey.SerializeCompressed())
 296  // 	} else {
 297  // 		pubKeyHash = btcaddr.Hash160(pubKey.SerializeUncompressed())
 298  // 	}
 299  // 	// Next, we'll generate a valid sigScript that'll allow us to spend the p2sh
 300  // 	// output. The sigScript will contain only a single push of the p2wkh witness
 301  // 	// program corresponding to the matching public key of this address.
 302  // 	p2wkhAddr, e := util.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams)
 303  // 	if e != nil  {
 304  // 		// 		return e
 305  // 	}
 306  // 	witnessProgram, e := txscript.PayToAddrScript(p2wkhAddr)
 307  // 	if e != nil  {
 308  // 		// 		return e
 309  // 	}
 310  // 	bldr := txscript.NewScriptBuilder()
 311  // 	bldr.AddData(witnessProgram)
 312  // 	sigScript, e := bldr.Script()
 313  // 	if e != nil  {
 314  // 		// 		return e
 315  // 	}
 316  // 	txIn.SignatureScript = sigScript
 317  // 	// With the sigScript in place, we'll next generate the proper witness that'll
 318  // 	// allow us to spend the p2wkh output.
 319  // 	witnessScript, e := txscript.WitnessSignature(tx, hashCache, idx,
 320  // 		inputValue, witnessProgram, txscript.SigHashAll, privKey, compressed)
 321  // 	if e != nil  {
 322  // 		// 		return e
 323  // 	}
 324  // 	txIn.Witness = witnessScript
 325  // 	return nil
 326  // }
 327  
 328  // AddAllInputScripts modifies an authored transaction by adding inputs scripts for each input of an authored
 329  // transaction. Private keys and redeem scripts are looked up using a SecretsSource based on the previous output script.
 330  func (tx *AuthoredTx) AddAllInputScripts(secrets SecretsSource) (e error) {
 331  	return AddAllInputScripts(tx.Tx, tx.PrevScripts, tx.PrevInputValues, secrets)
 332  }
 333