Repository: decred/dcrd
Pull request #2642 (2021-04-30 08:50:25)

txscript: Split signing code to sign subpackage.
The current code for handling signing standard scripts resides in `txscript` and depends on parsing and creating standard scripts.  This poses a problem for future work which intends to split the standard script handling from the consensus critical code since that code will also need to depend on `txscript` and therefore would result in a circular dependency.

In order to pave the way for splitting the standard script handling without running into the aforementioned issue, this moves all of the signing code in the `txscript` package to a new subpackage named `sign`.

As an aside, the signing code really never fit very well in the `txscript` package anyway and it only exists there because it was not possible to parse scripts outside of the package back when the original code was implemented.  However, that limitation no longer exists.

It should also be noted that this only does the minimum work necessary to move the code and does not update it otherwise since future work plans to replace it with a much more robust architecture that properly handles things such as different script versions and non-standard scripts which the current code does not handle.


Patch:
diff --git a/blockchain/chaingen/generator.go b/blockchain/chaingen/generator.go
index 02c40df5..bd9369fc 100644
--- a/blockchain/chaingen/generator.go
+++ b/blockchain/chaingen/generator.go
@@ -19,6 +19,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -620,7 +621,7 @@ func (g *Generator) CreateTreasuryTSpend(privKey []byte, payouts []AddressAmount
 	})
 
 	// Calculate TSpend signature without SigHashType.
-	sigscript, err := txscript.TSpendSignatureScript(msgTx, privKey)
+	sigscript, err := sign.TSpendSignatureScript(msgTx, privKey)
 	if err != nil {
 		panic(err)
 	}
diff --git a/txscript/README.md b/txscript/README.md
index cfe45245..512a5936 100644
--- a/txscript/README.md
+++ b/txscript/README.md
@@ -32,9 +32,6 @@ the standard go tooling for working with modules to incorporate it.
 * [Extracting Details from Standard Scripts](https://pkg.go.dev/github.com/decred/dcrd/txscript/v4#example-ExtractPkScriptAddrs)
   Demonstrates extracting information from a standard public key script.
 
-* [Manually Signing a Transaction Output](https://pkg.go.dev/github.com/decred/dcrd/txscript/v4#example-SignTxOutput)
-  Demonstrates manually creating and signing a redeem transaction.
-
 * [Counting Opcodes in Scripts](https://pkg.go.dev/github.com/decred/dcrd/txscript/v4#example-ScriptTokenizer)
   Demonstrates creating a script tokenizer instance and using it to count the
   number of opcodes a script contains.
diff --git a/txscript/sign.go b/txscript/sign/sign.go
similarity index 80%
rename from txscript/sign.go
rename to txscript/sign/sign.go
index b9bf0120..1490f494 100644
--- a/txscript/sign.go
+++ b/txscript/sign/sign.go
@@ -3,7 +3,7 @@
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
-package txscript
+package sign
 
 import (
 	"errors"
@@ -14,6 +14,7 @@ import (
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr"
+	"github.com/decred/dcrd/txscript/v4"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -25,9 +26,10 @@ import (
 // does not accept a script version, the results are undefined for other script
 // versions.
 func RawTxInSignature(tx *wire.MsgTx, idx int, subScript []byte,
-	hashType SigHashType, key []byte, sigType dcrec.SignatureType) ([]byte, error) {
+	hashType txscript.SigHashType, key []byte,
+	sigType dcrec.SignatureType) ([]byte, error) {
 
-	hash, err := CalcSignatureHash(subScript, hashType, tx, idx, nil)
+	hash, err := txscript.CalcSignatureHash(subScript, hashType, tx, idx, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -70,8 +72,9 @@ func RawTxInSignature(tx *wire.MsgTx, idx int, subScript []byte,
 // as the idx'th input. privKey is serialized in the respective format for the
 // ECDSA type. This format must match the same format used to generate the payment
 // address, or the script validation will fail.
-func SignatureScript(tx *wire.MsgTx, idx int, subscript []byte, hashType SigHashType,
-	privKey []byte, sigType dcrec.SignatureType, compress bool) ([]byte, error) {
+func SignatureScript(tx *wire.MsgTx, idx int, subscript []byte,
+	hashType txscript.SigHashType, privKey []byte,
+	sigType dcrec.SignatureType, compress bool) ([]byte, error) {
 
 	sig, err := RawTxInSignature(tx, idx, subscript, hashType, privKey, sigType)
 	if err != nil {
@@ -97,19 +100,20 @@ func SignatureScript(tx *wire.MsgTx, idx int, subscript []byte, hashType SigHash
 		return nil, fmt.Errorf("unsupported signature type '%v'", sigType)
 	}
 
-	return NewScriptBuilder().AddData(sig).AddData(pkData).Script()
+	return txscript.NewScriptBuilder().AddData(sig).AddData(pkData).Script()
 }
 
 // p2pkSignatureScript constructs a pay-to-pubkey signature script.
 func p2pkSignatureScript(tx *wire.MsgTx, idx int, subScript []byte,
-	hashType SigHashType, privKey []byte, sigType dcrec.SignatureType) ([]byte, error) {
+	hashType txscript.SigHashType, privKey []byte,
+	sigType dcrec.SignatureType) ([]byte, error) {
 
 	sig, err := RawTxInSignature(tx, idx, subScript, hashType, privKey, sigType)
 	if err != nil {
 		return nil, err
 	}
 
-	return NewScriptBuilder().AddData(sig).Script()
+	return txscript.NewScriptBuilder().AddData(sig).Script()
 }
 
 // signMultiSig signs as many of the outputs in the provided multisig script as
@@ -117,11 +121,12 @@ func p2pkSignatureScript(tx *wire.MsgTx, idx int, subScript []byte,
 // fulfills the contract (i.e. nrequired signatures are provided).  Since it is
 // arguably legal to not be able to sign any of the outputs, no error is
 // returned.
-func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType,
-	addresses []stdaddr.Address, nRequired int, kdb KeyDB) ([]byte, bool) {
+func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte,
+	hashType txscript.SigHashType, addresses []stdaddr.Address,
+	nRequired int, kdb KeyDB) ([]byte, bool) {
 
 	// No need to add dummy in Decred.
-	builder := NewScriptBuilder()
+	builder := txscript.NewScriptBuilder()
 	signed := 0
 	for _, addr := range addresses {
 		key, sigType, _, err := kdb.GetKey(addr)
@@ -148,13 +153,14 @@ func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashTyp
 // handleStakeOutSign is a convenience function for reducing code clutter in
 // sign. It handles the signing of stake outputs.
 func handleStakeOutSign(tx *wire.MsgTx, idx int, subScript []byte,
-	hashType SigHashType, kdb KeyDB, sdb ScriptDB,
-	addresses []stdaddr.Address, class ScriptClass, subClass ScriptClass,
-	nrequired int) ([]byte, ScriptClass, []stdaddr.Address, int, error) {
+	hashType txscript.SigHashType, kdb KeyDB, sdb ScriptDB,
+	addresses []stdaddr.Address, class txscript.ScriptClass,
+	subClass txscript.ScriptClass,
+	nrequired int) ([]byte, txscript.ScriptClass, []stdaddr.Address, int, error) {
 
 	// look up key for address
 	switch subClass {
-	case PubKeyHashTy:
+	case txscript.PubKeyHashTy:
 		key, sigType, compressed, err := kdb.GetKey(addresses[0])
 		if err != nil {
 			return nil, class, nil, 0, err
@@ -165,7 +171,7 @@ func handleStakeOutSign(tx *wire.MsgTx, idx int, subScript []byte,
 			return nil, class, nil, 0, err
 		}
 		return txscript, class, addresses, nrequired, nil
-	case ScriptHashTy:
+	case txscript.ScriptHashTy:
 		script, err := sdb.GetScript(addresses[0])
 		if err != nil {
 			return nil, class, nil, 0, err
@@ -182,23 +188,25 @@ func handleStakeOutSign(tx *wire.MsgTx, idx int, subScript []byte,
 // its input index, a database of keys, a database of scripts, and information
 // about the type of signature and returns a signature, script class, the
 // addresses involved, and the number of signatures required.
-func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, isTreasuryEnabled bool) ([]byte, ScriptClass, []stdaddr.Address, int, error) {
+func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int,
+	subScript []byte, hashType txscript.SigHashType, kdb KeyDB, sdb ScriptDB,
+	isTreasuryEnabled bool) ([]byte, txscript.ScriptClass, []stdaddr.Address, int, error) {
 
 	const scriptVersion = 0
-	class, addresses, nrequired, err := ExtractPkScriptAddrs(scriptVersion,
-		subScript, chainParams, isTreasuryEnabled)
+	class, addresses, nrequired, err := txscript.ExtractPkScriptAddrs(
+		scriptVersion, subScript, chainParams, isTreasuryEnabled)
 	if err != nil {
-		return nil, NonStandardTy, nil, 0, err
+		return nil, txscript.NonStandardTy, nil, 0, err
 	}
 
 	subClass := class
-	isStakeType := class == StakeSubmissionTy ||
-		class == StakeSubChangeTy ||
-		class == StakeGenTy ||
-		class == StakeRevocationTy ||
-		(isTreasuryEnabled && class == TreasuryGenTy)
+	isStakeType := class == txscript.StakeSubmissionTy ||
+		class == txscript.StakeSubChangeTy ||
+		class == txscript.StakeGenTy ||
+		class == txscript.StakeRevocationTy ||
+		(isTreasuryEnabled && class == txscript.TreasuryGenTy)
 	if isStakeType {
-		subClass, err = GetStakeOutSubclass(subScript, isTreasuryEnabled)
+		subClass, err = txscript.GetStakeOutSubclass(subScript, isTreasuryEnabled)
 		if err != nil {
 			return nil, 0, nil, 0,
 				fmt.Errorf("unknown stake output subclass encountered")
@@ -206,7 +214,7 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 	}
 
 	switch class {
-	case PubKeyTy:
+	case txscript.PubKeyTy:
 		// look up key for address
 		key, sigType, _, err := kdb.GetKey(addresses[0])
 		if err != nil {
@@ -221,7 +229,7 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 
 		return script, class, addresses, nrequired, nil
 
-	case PubkeyAltTy:
+	case txscript.PubkeyAltTy:
 		// look up key for address
 		key, sigType, _, err := kdb.GetKey(addresses[0])
 		if err != nil {
@@ -236,7 +244,7 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 
 		return script, class, addresses, nrequired, nil
 
-	case PubKeyHashTy:
+	case txscript.PubKeyHashTy:
 		// look up key for address
 		key, sigType, compressed, err := kdb.GetKey(addresses[0])
 		if err != nil {
@@ -251,7 +259,7 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 
 		return script, class, addresses, nrequired, nil
 
-	case PubkeyHashAltTy:
+	case txscript.PubkeyHashAltTy:
 		// look up key for address
 		key, sigType, compressed, err := kdb.GetKey(addresses[0])
 		if err != nil {
@@ -266,7 +274,7 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 
 		return script, class, addresses, nrequired, nil
 
-	case ScriptHashTy:
+	case txscript.ScriptHashTy:
 		script, err := sdb.GetScript(addresses[0])
 		if err != nil {
 			return nil, class, nil, 0, err
@@ -274,32 +282,32 @@ func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript
 
 		return script, class, addresses, nrequired, nil
 
-	case MultiSigTy:
+	case txscript.MultiSigTy:
 		script, _ := signMultiSig(tx, idx, subScript, hashType,
 			addresses, nrequired, kdb)
 		return script, class, addresses, nrequired, nil
 
-	case StakeSubmissionTy:
+	case txscript.StakeSubmissionTy:
 		return handleStakeOutSign(tx, idx, subScript, hashType, kdb,
 			sdb, addresses, class, subClass, nrequired)
 
-	case StakeGenTy:
+	case txscript.StakeGenTy:
 		return handleStakeOutSign(tx, idx, subScript, hashType, kdb,
 			sdb, addresses, class, subClass, nrequired)
 
-	case StakeRevocationTy:
+	case txscript.StakeRevocationTy:
 		return handleStakeOutSign(tx, idx, subScript, hashType, kdb,
 			sdb, addresses, class, subClass, nrequired)
 
-	case StakeSubChangeTy:
+	case txscript.StakeSubChangeTy:
 		return handleStakeOutSign(tx, idx, subScript, hashType, kdb,
 			sdb, addresses, class, subClass, nrequired)
 
-	case TreasuryGenTy:
+	case txscript.TreasuryGenTy:
 		return handleStakeOutSign(tx, idx, subScript, hashType, kdb,
 			sdb, addresses, class, subClass, nrequired)
 
-	case NullDataTy:
+	case txscript.NullDataTy:
 		return nil, class, nil, 0,
 			errors.New("can't sign NULLDATA transactions")
 
@@ -335,7 +343,7 @@ func mergeMultiSig(tx *wire.MsgTx, idx int, addresses []stdaddr.Address,
 	var possibleSigs [][]byte
 	extractSigs := func(script []byte) error {
 		const scriptVersion = 0
-		tokenizer := MakeScriptTokenizer(scriptVersion, script)
+		tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
 		for tokenizer.Next() {
 			if data := tokenizer.Data(); len(data) != 0 {
 				possibleSigs = append(possibleSigs, data)
@@ -370,7 +378,7 @@ sigLoop:
 			continue
 		}
 		tSig := sig[:len(sig)-1]
-		hashType := SigHashType(sig[len(sig)-1])
+		hashType := txscript.SigHashType(sig[len(sig)-1])
 
 		pSig, err := ecdsa.ParseDERSignature(tSig)
 		if err != nil {
@@ -382,7 +390,7 @@ sigLoop:
 		// however, assume no sigs etc are in the script since that
 		// would make the transaction nonstandard and thus not
 		// MultiSigTy, so we just need to hash the full thing.
-		hash, err := calcSignatureHash(pkScript, hashType, tx, idx, nil)
+		hash, err := txscript.CalcSignatureHash(pkScript, hashType, tx, idx, nil)
 		if err != nil {
 			// Decred -- is this the right handling for SIGHASH_SINGLE error ?
 			// TODO make sure this doesn't break anything.
@@ -412,7 +420,7 @@ sigLoop:
 		}
 	}
 
-	builder := NewScriptBuilder()
+	builder := txscript.NewScriptBuilder()
 	doneSigs := 0
 	// This assumes that addresses are in the same order as in the script.
 	for _, addr := range addresses {
@@ -429,13 +437,41 @@ sigLoop:
 
 	// padding for missing ones.
 	for i := doneSigs; i < nRequired; i++ {
-		builder.AddOp(OP_0)
+		builder.AddOp(txscript.OP_0)
 	}
 
 	script, _ := builder.Script()
 	return script
 }
 
+// checkScriptParses returns an error if the provided script fails to parse.
+func checkScriptParses(scriptVersion uint16, script []byte) error {
+	tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
+	for tokenizer.Next() {
+		// Nothing to do.
+	}
+	return tokenizer.Err()
+}
+
+// finalOpcodeData returns the data associated with the final opcode in the
+// script.  It will return nil if the script fails to parse.
+func finalOpcodeData(scriptVersion uint16, script []byte) []byte {
+	// Avoid unnecessary work.
+	if len(script) == 0 {
+		return nil
+	}
+
+	var data []byte
+	tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
+	for tokenizer.Next() {
+		data = tokenizer.Data()
+	}
+	if tokenizer.Err() != nil {
+		return nil
+	}
+	return data
+}
+
 // mergeScripts merges sigScript and prevScript assuming they are both
 // partial solutions for pkScript spending output idx of tx. class, addresses
 // and nrequired are the result of extracting the addresses from pkscript.
@@ -446,7 +482,9 @@ sigLoop:
 // NOTE: This function is only valid for version 0 scripts.  Since the function
 // does not accept a script version, the results are undefined for other script
 // versions.
-func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, class ScriptClass, addresses []stdaddr.Address, nRequired int, sigScript, prevScript []byte, isTreasuryEnabled bool) []byte {
+func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int,
+	pkScript []byte, class txscript.ScriptClass, addresses []stdaddr.Address,
+	nRequired int, sigScript, prevScript []byte, isTreasuryEnabled bool) []byte {
 
 	// TODO(oga) the scripthash and multisig paths here are overly
 	// inefficient in that they will recompute already known data.
@@ -454,7 +492,7 @@ func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 	// extra calculations.
 	const scriptVersion = 0
 	switch class {
-	case ScriptHashTy:
+	case txscript.ScriptHashTy:
 		// Nothing to merge if either the new or previous signature
 		// scripts are empty or fail to parse.
 		if len(sigScript) == 0 ||
@@ -477,8 +515,8 @@ func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 
 		// We already know this information somewhere up the stack,
 		// therefore the error is ignored.
-		class, addresses, nrequired, _ := ExtractPkScriptAddrs(scriptVersion,
-			script, chainParams, isTreasuryEnabled)
+		class, addresses, nrequired, _ := txscript.ExtractPkScriptAddrs(
+			scriptVersion, script, chainParams, isTreasuryEnabled)
 
 		// Merge
 		mergedScript := mergeScripts(chainParams, tx, idx, script,
@@ -486,13 +524,13 @@ func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 			isTreasuryEnabled)
 
 		// Reappend the script and return the result.
-		builder := NewScriptBuilder()
+		builder := txscript.NewScriptBuilder()
 		builder.AddOps(mergedScript)
 		builder.AddData(script)
 		finalScript, _ := builder.Script()
 		return finalScript
 
-	case MultiSigTy:
+	case txscript.MultiSigTy:
 		return mergeMultiSig(tx, idx, addresses, nRequired, pkScript,
 			sigScript, prevScript)
 
@@ -549,7 +587,9 @@ func (sc ScriptClosure) GetScript(address stdaddr.Address) ([]byte, error) {
 // NOTE: This function is only valid for version 0 scripts.  Since the function
 // does not accept a script version, the results are undefined for other script
 // versions.
-func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, previousScript []byte, isTreasuryEnabled bool) ([]byte, error) {
+func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int,
+	pkScript []byte, hashType txscript.SigHashType, kdb KeyDB, sdb ScriptDB,
+	previousScript []byte, isTreasuryEnabled bool) ([]byte, error) {
 
 	sigScript, class, addresses, nrequired, err := sign(chainParams, tx,
 		idx, pkScript, hashType, kdb, sdb, isTreasuryEnabled)
@@ -557,19 +597,19 @@ func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 		return nil, err
 	}
 
-	isStakeType := class == StakeSubmissionTy ||
-		class == StakeSubChangeTy ||
-		class == StakeGenTy ||
-		class == StakeRevocationTy ||
-		(isTreasuryEnabled && class == TreasuryGenTy)
+	isStakeType := class == txscript.StakeSubmissionTy ||
+		class == txscript.StakeSubChangeTy ||
+		class == txscript.StakeGenTy ||
+		class == txscript.StakeRevocationTy ||
+		(isTreasuryEnabled && class == txscript.TreasuryGenTy)
 	if isStakeType {
-		class, err = GetStakeOutSubclass(pkScript, isTreasuryEnabled)
+		class, err = txscript.GetStakeOutSubclass(pkScript, isTreasuryEnabled)
 		if err != nil {
 			return nil, fmt.Errorf("unknown stake output subclass encountered")
 		}
 	}
 
-	if class == ScriptHashTy {
+	if class == txscript.ScriptHashTy {
 		// TODO keep the sub addressed and pass down to merge.
 		realSigScript, _, _, _, err := sign(chainParams, tx, idx,
 			sigScript, hashType, kdb, sdb, isTreasuryEnabled)
@@ -578,7 +618,7 @@ func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 		}
 
 		// Append the p2sh script as the last push in the script.
-		builder := NewScriptBuilder()
+		builder := txscript.NewScriptBuilder()
 		builder.AddOps(realSigScript)
 		builder.AddData(sigScript)
 
@@ -598,7 +638,8 @@ func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pk
 // spent from the treasury.  The private key must correspond to one of the
 // valid public keys for a Pi instance recognized by consensus.
 func TSpendSignatureScript(msgTx *wire.MsgTx, privKey []byte) ([]byte, error) {
-	hash, err := CalcSignatureHash(nil, SigHashAll, msgTx, 0, nil)
+	hash, err := txscript.CalcSignatureHash(nil, txscript.SigHashAll, msgTx, 0,
+		nil)
 	if err != nil {
 		return nil, err
 	}
@@ -611,6 +652,6 @@ func TSpendSignatureScript(msgTx *wire.MsgTx, privKey []byte) ([]byte, error) {
 	sigBytes := sig.Serialize()
 	pkBytes := priv.PubKey().SerializeCompressed()
 
-	return NewScriptBuilder().AddData(sigBytes).AddData(pkBytes).
-		AddOp(OP_TSPEND).Script()
+	return txscript.NewScriptBuilder().AddData(sigBytes).AddData(pkBytes).
+		AddOp(txscript.OP_TSPEND).Script()
 }


Test patch:
diff --git a/blockchain/common_test.go b/blockchain/common_test.go
index 77eada55..d149e880 100644
--- a/blockchain/common_test.go
+++ b/blockchain/common_test.go
@@ -28,6 +28,7 @@ import (
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/lru"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -345,7 +346,7 @@ func newFakeCreateTSpend(privKey []byte, payouts []dcrutil.Amount, fee dcrutil.A
 	})
 
 	// Calculate TSpend signature without SigHashType.
-	sigscript, err := txscript.TSpendSignatureScript(msgTx, privKey)
+	sigscript, err := sign.TSpendSignatureScript(msgTx, privKey)
 	if err != nil {
 		panic(err)
 	}
diff --git a/blockchain/fullblocktests/generate.go b/blockchain/fullblocktests/generate.go
index 64ea868c..0790c9a2 100644
--- a/blockchain/fullblocktests/generate.go
+++ b/blockchain/fullblocktests/generate.go
@@ -20,6 +20,7 @@ import (
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -1561,9 +1562,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
 			// associated p2sh output in bshso0.
 			spend := chaingen.MakeSpendableOut(bshso0, uint32(i+2), 2)
 			tx := g.CreateSpendTx(&spend, lowFee)
-			sig, err := txscript.RawTxInSignature(tx, 0,
-				redeemScript, txscript.SigHashAll, privKeyBytes,
-				dcrec.STEcdsaSecp256k1)
+			sig, err := sign.RawTxInSignature(tx, 0, redeemScript,
+				txscript.SigHashAll, privKeyBytes, dcrec.STEcdsaSecp256k1)
 			if err != nil {
 				panic(err)
 			}
@@ -1597,9 +1597,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
 			// associated p2sh output in bshso0.
 			spend := chaingen.MakeSpendableOut(bshso0, uint32(i+2), 2)
 			tx := g.CreateSpendTx(&spend, lowFee)
-			sig, err := txscript.RawTxInSignature(tx, 0,
-				redeemScript, txscript.SigHashAll, privKeyBytes,
-				dcrec.STEcdsaSecp256k1)
+			sig, err := sign.RawTxInSignature(tx, 0, redeemScript,
+				txscript.SigHashAll, privKeyBytes, dcrec.STEcdsaSecp256k1)
 			if err != nil {
 				panic(err)
 			}
diff --git a/blockchain/treasury_test.go b/blockchain/treasury_test.go
index 46b0305a..b50293c7 100644
--- a/blockchain/treasury_test.go
+++ b/blockchain/treasury_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/decred/dcrd/gcs/v3/blockcf2"
 	"github.com/decred/dcrd/lru"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -2044,18 +2045,18 @@ func TestSpendableTreasuryTxs(t *testing.T) {
 	})
 
 	// Generate the valid signature for the first input, which is a P2PKH.
-	sig, err := txscript.SignatureScript(tx, 0, tspend.TxOut[1].PkScript,
-		txscript.SigHashAll, spendPrivKey.Serialize(),
-		dcrec.STEcdsaSecp256k1, true)
+	sig, err := sign.SignatureScript(tx, 0, tspend.TxOut[1].PkScript,
+		txscript.SigHashAll, spendPrivKey.Serialize(), dcrec.STEcdsaSecp256k1,
+		true)
 	if err != nil {
 		t.Fatalf("unable to generate sig: %v", err)
 	}
 	tx.TxIn[0].SignatureScript = sig
 
 	// Generate the valid signature for the third input, which is a P2PKH.
-	sig, err = txscript.SignatureScript(tx, 2, tadd1.TxOut[1].PkScript,
-		txscript.SigHashAll, addPrivKey.Serialize(),
-		dcrec.STEcdsaSecp256k1, true)
+	sig, err = sign.SignatureScript(tx, 2, tadd1.TxOut[1].PkScript,
+		txscript.SigHashAll, addPrivKey.Serialize(), dcrec.STEcdsaSecp256k1,
+		true)
 	if err != nil {
 		t.Fatalf("unable to generate sig: %v", err)
 	}
diff --git a/internal/mempool/mempool_test.go b/internal/mempool/mempool_test.go
index 9d254e6b..aa50f145 100644
--- a/internal/mempool/mempool_test.go
+++ b/internal/mempool/mempool_test.go
@@ -26,6 +26,7 @@ import (
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/internal/mining"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -464,9 +465,8 @@ func (p *poolHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint32
 
 	// Sign the new transaction.
 	for i := range tx.TxIn {
-		sigScript, err := txscript.SignatureScript(tx,
-			i, p.payScript, txscript.SigHashAll, p.signKey,
-			dcrec.STEcdsaSecp256k1, true)
+		sigScript, err := sign.SignatureScript(tx, i, p.payScript,
+			txscript.SigHashAll, p.signKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			return nil, err
 		}
@@ -501,9 +501,8 @@ func (p *poolHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint32)
 		})
 
 		// Sign the new transaction.
-		sigScript, err := txscript.SignatureScript(tx, 0,
-			p.payScript, txscript.SigHashAll, p.signKey,
-			dcrec.STEcdsaSecp256k1, true)
+		sigScript, err := sign.SignatureScript(tx, 0, p.payScript,
+			txscript.SigHashAll, p.signKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			return nil, err
 		}
@@ -559,9 +558,9 @@ func (p *poolHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (*d
 	tx.AddTxOut(newTxOut(change, changeScriptVer, changeScript))
 
 	// Sign the ticket purchase.
-	sigScript, err := txscript.SignatureScript(tx, 0,
-		sourceTx.MsgTx().TxOut[0].PkScript, txscript.SigHashAll,
-		p.signKey, dcrec.STEcdsaSecp256k1, true)
+	sigScript, err := sign.SignatureScript(tx, 0,
+		sourceTx.MsgTx().TxOut[0].PkScript, txscript.SigHashAll, p.signKey,
+		dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		return nil, err
 	}
@@ -655,9 +654,9 @@ func (p *poolHarness) CreateVote(ticket *dcrutil.Tx, mungers ...func(*wire.MsgTx
 	// Sign the input.
 	inputToSign := 1
 	redeemTicketScript := ticket.MsgTx().TxOut[0].PkScript
-	signedScript, err := txscript.SignTxOutput(p.chainParams, vote, inputToSign,
-		redeemTicketScript, txscript.SigHashAll, p,
-		p, vote.TxIn[inputToSign].SignatureScript, noTreasury)
+	signedScript, err := sign.SignTxOutput(p.chainParams, vote, inputToSign,
+		redeemTicketScript, txscript.SigHashAll, p, p,
+		vote.TxIn[inputToSign].SignatureScript, noTreasury)
 	if err != nil {
 		return nil, err
 	}
@@ -704,7 +703,7 @@ func (p *poolHarness) CreateRevocation(ticket *dcrutil.Tx) (*dcrutil.Tx, error)
 	// Sign the input.
 	inputToSign := 0
 	redeemTicketScript := ticket.MsgTx().TxOut[0].PkScript
-	signedScript, err := txscript.SignTxOutput(p.chainParams, revocation,
+	signedScript, err := sign.SignTxOutput(p.chainParams, revocation,
 		inputToSign, redeemTicketScript, txscript.SigHashAll, p, p,
 		revocation.TxIn[inputToSign].SignatureScript, noTreasury)
 	if err != nil {
@@ -2321,7 +2320,7 @@ func createTSpend(t *testing.T, expiry uint32, tspendAmount, tspendFee int64, pi
 	})
 
 	// Calculate TSpend signature without SigHashType.
-	sigscript, err := txscript.TSpendSignatureScript(msgTx, piKey)
+	sigscript, err := sign.TSpendSignatureScript(msgTx, piKey)
 	if err != nil {
 		t.Fatalf("unable to sign tspend: %v", err)
 	}
@@ -2467,7 +2466,7 @@ func TestHandlesTSpends(t *testing.T) {
 	// should fail.
 	tx := tspends[1].MsgTx()
 	tx.Expiry += 1
-	tx.TxIn[0].SignatureScript, err = txscript.TSpendSignatureScript(tx, piKey)
+	tx.TxIn[0].SignatureScript, err = sign.TSpendSignatureScript(tx, piKey)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2477,7 +2476,7 @@ func TestHandlesTSpends(t *testing.T) {
 	// fail.
 	tx = tspends[1].MsgTx()
 	tx.Expiry = uint32(tvi)
-	tx.TxIn[0].SignatureScript, err = txscript.TSpendSignatureScript(tx, piKey)
+	tx.TxIn[0].SignatureScript, err = sign.TSpendSignatureScript(tx, piKey)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2491,7 +2490,7 @@ func TestHandlesTSpends(t *testing.T) {
 	// in the next block).
 	tx = tspends[1].MsgTx()
 	tx.Expiry = expiry + uint32(tvi*mul*2)
-	tx.TxIn[0].SignatureScript, err = txscript.TSpendSignatureScript(tx, piKey)
+	tx.TxIn[0].SignatureScript, err = sign.TSpendSignatureScript(tx, piKey)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2511,7 +2510,7 @@ func TestHandlesTSpends(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx = tspends[3].MsgTx()
-	tx.TxIn[0].SignatureScript, err = txscript.TSpendSignatureScript(tx, nonPiKey)
+	tx.TxIn[0].SignatureScript, err = sign.TSpendSignatureScript(tx, nonPiKey)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2579,9 +2578,8 @@ func createTAdd(t *testing.T, spend *spendableOutput, payScript, signKey []byte,
 	}
 
 	var err error
-	tx.TxIn[0].SignatureScript, err = txscript.SignatureScript(tx,
-		0, payScript, txscript.SigHashAll, signKey,
-		dcrec.STEcdsaSecp256k1, true)
+	tx.TxIn[0].SignatureScript, err = sign.SignatureScript(tx, 0, payScript,
+		txscript.SigHashAll, signKey, dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		t.Fatalf("Unable to sign tadd: %v", err)
 	}
diff --git a/internal/mining/mining_harness_test.go b/internal/mining/mining_harness_test.go
index 9167bc46..da8d29e3 100644
--- a/internal/mining/mining_harness_test.go
+++ b/internal/mining/mining_harness_test.go
@@ -20,6 +20,7 @@ import (
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -1003,9 +1004,8 @@ func (m *miningHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint3
 			m.payScript))
 
 		// Sign the new transaction.
-		sigScript, err := txscript.SignatureScript(tx, 0,
-			m.payScript, txscript.SigHashAll, m.signKey,
-			dcrec.STEcdsaSecp256k1, true)
+		sigScript, err := sign.SignatureScript(tx, 0, m.payScript,
+			txscript.SigHashAll, m.signKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			return nil, err
 		}
@@ -1076,9 +1076,8 @@ func (m *miningHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint
 
 	// Sign the new transaction.
 	for i := range tx.TxIn {
-		sigScript, err := txscript.SignatureScript(tx,
-			i, m.payScript, txscript.SigHashAll, m.signKey,
-			dcrec.STEcdsaSecp256k1, true)
+		sigScript, err := sign.SignatureScript(tx, i, m.payScript,
+			txscript.SigHashAll, m.signKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			return nil, err
 		}
@@ -1128,9 +1127,9 @@ func (m *miningHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (
 	tx.AddTxOut(newTxOut(change, changeScriptVer, changeScript))
 
 	// Sign the ticket purchase.
-	sigScript, err := txscript.SignatureScript(tx, 0,
-		sourceTx.MsgTx().TxOut[0].PkScript, txscript.SigHashAll,
-		m.signKey, dcrec.STEcdsaSecp256k1, true)
+	sigScript, err := sign.SignatureScript(tx, 0,
+		sourceTx.MsgTx().TxOut[0].PkScript, txscript.SigHashAll, m.signKey,
+		dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		return nil, err
 	}
@@ -1215,7 +1214,7 @@ func (m *miningHarness) CreateVote(ticket *dcrutil.Tx, mungers ...func(*wire.Msg
 	// Sign the input.
 	inputToSign := 1
 	redeemTicketScript := ticket.MsgTx().TxOut[0].PkScript
-	signedScript, err := txscript.SignTxOutput(params, vote, inputToSign,
+	signedScript, err := sign.SignTxOutput(params, vote, inputToSign,
 		redeemTicketScript, txscript.SigHashAll, m, m,
 		vote.TxIn[inputToSign].SignatureScript, m.chain.isTreasuryAgendaActive)
 	if err != nil {
diff --git a/internal/rpcserver/treasury_test.go b/internal/rpcserver/treasury_test.go
index 95ea68b6..57137c2f 100644
--- a/internal/rpcserver/treasury_test.go
+++ b/internal/rpcserver/treasury_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/rpctest"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -92,7 +93,7 @@ func createTSpend(privKey []byte, payouts []tspendPayout, fee dcrutil.Amount, ex
 	})
 
 	// Calculate TSpend signature without SigHashType.
-	sigscript, err := txscript.TSpendSignatureScript(msgTx, privKey)
+	sigscript, err := sign.TSpendSignatureScript(msgTx, privKey)
 	if err != nil {
 		panic(err)
 	}
@@ -120,9 +121,8 @@ func createTAdd(t testing.TB, privKey []byte, prevOut *wire.OutPoint, pkScript [
 	}
 	tx.Version = wire.TxVersionTreasury
 
-	sig, err := txscript.SignatureScript(tx, 0, pkScript,
-		txscript.SigHashAll, privKey,
-		dcrec.STEcdsaSecp256k1, true)
+	sig, err := sign.SignatureScript(tx, 0, pkScript, txscript.SigHashAll,
+		privKey, dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		t.Fatalf("unable to generate sig: %v", err)
 	}
@@ -473,17 +473,15 @@ func TestTreasury(t *testing.T) {
 	})
 
 	// Generate signatures for the P2PKH inputs.
-	sig, err := txscript.SignatureScript(tx, 0, tspendYes.TxOut[1].PkScript,
-		txscript.SigHashAll, privKey.Serialize(),
-		dcrec.STEcdsaSecp256k1, true)
+	sig, err := sign.SignatureScript(tx, 0, tspendYes.TxOut[1].PkScript,
+		txscript.SigHashAll, privKey.Serialize(), dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		t.Fatalf("unable to generate sig: %v", err)
 	}
 	tx.TxIn[0].SignatureScript = sig
 
-	sig, err = txscript.SignatureScript(tx, 2, tadd1.TxOut[1].PkScript,
-		txscript.SigHashAll, privKey.Serialize(),
-		dcrec.STEcdsaSecp256k1, true)
+	sig, err = sign.SignatureScript(tx, 2, tadd1.TxOut[1].PkScript,
+		txscript.SigHashAll, privKey.Serialize(), dcrec.STEcdsaSecp256k1, true)
 	if err != nil {
 		t.Fatalf("unable to generate sig: %v", err)
 	}
diff --git a/rpctest/memwallet.go b/rpctest/memwallet.go
index 594ead34..27c85333 100644
--- a/rpctest/memwallet.go
+++ b/rpctest/memwallet.go
@@ -22,6 +22,7 @@ import (
 	"github.com/decred/dcrd/hdkeychain/v3"
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -529,7 +530,7 @@ func (m *memWallet) CreateTransaction(outputs []*wire.TxOut, feeRate dcrutil.Amo
 			return nil, err
 		}
 
-		sigScript, err := txscript.SignatureScript(tx, i, utxo.pkScript,
+		sigScript, err := sign.SignatureScript(tx, i, utxo.pkScript,
 			txscript.SigHashAll, privKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			return nil, err
diff --git a/rpctest/votingwallet.go b/rpctest/votingwallet.go
index 46d997f6..08af283c 100644
--- a/rpctest/votingwallet.go
+++ b/rpctest/votingwallet.go
@@ -21,6 +21,7 @@ import (
 	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -456,7 +457,7 @@ func (w *VotingWallet) handleBlockConnectedNtfn(ntfn *blockConnectedNtfn) {
 			prevScript = w.voteRetScript
 		}
 
-		sig, err := txscript.SignatureScript(t, 0, prevScript, txscript.SigHashAll,
+		sig, err := sign.SignatureScript(t, 0, prevScript, txscript.SigHashAll,
 			w.privateKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			w.logError(fmt.Errorf("failed to sign ticket tx: %v", err))
@@ -565,7 +566,7 @@ func (w *VotingWallet) handleWinningTicketsNtfn(ntfn *winningTicketsNtfn) {
 			vote.Version = wire.TxVersionTreasury
 		}
 
-		sig, err := txscript.SignatureScript(vote, 1, w.p2sstx, txscript.SigHashAll,
+		sig, err := sign.SignatureScript(vote, 1, w.p2sstx, txscript.SigHashAll,
 			w.privateKey, dcrec.STEcdsaSecp256k1, true)
 		if err != nil {
 			w.logError(fmt.Errorf("failed to sign ticket tx: %v", err))
diff --git a/txscript/bench_test.go b/txscript/bench_test.go
index f1a23005..fa7568ea 100644
--- a/txscript/bench_test.go
+++ b/txscript/bench_test.go
@@ -13,6 +13,18 @@ import (
 	"github.com/decred/dcrd/wire"
 )
 
+const (
+	// noTreasury signifies the treasury agenda should be treated as though
+	// it is inactive.  It is used to increase the readability of the
+	// tests.
+	noTreasury = false
+
+	// withTreasury signifies the treasury agenda should be treated as
+	// though it is active.  It is used to increase the readability of
+	// the tests.
+	withTreasury = true
+)
+
 var (
 	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
 	// useful for benchmarking signature hash calculation.
diff --git a/txscript/example_test.go b/txscript/example_test.go
index 204d375c..7a941e54 100644
--- a/txscript/example_test.go
+++ b/txscript/example_test.go
@@ -9,13 +9,9 @@ import (
 	"encoding/hex"
 	"fmt"
 
-	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/dcrec"
-	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/txscript/v4"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
-	"github.com/decred/dcrd/wire"
 )
 
 const (
@@ -55,106 +51,6 @@ func ExampleExtractPkScriptAddrs() {
 	// Required Signatures: 1
 }
 
-// This example demonstrates manually creating and signing a redeem transaction.
-func ExampleSignTxOutput() {
-	// Ordinarily the private key would come from whatever storage mechanism
-	// is being used, but for this example just hard code it.
-	privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" +
-		"d4f8720ee63e502ee2869afab7de234b80c")
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	pubKey := secp256k1.PrivKeyFromBytes(privKeyBytes).PubKey()
-	pubKeyHash := stdaddr.Hash160(pubKey.SerializeCompressed())
-	mainNetParams := chaincfg.MainNetParams()
-	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubKeyHash,
-		mainNetParams)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-
-	// For this example, create a fake transaction that represents what
-	// would ordinarily be the real transaction that is being spent.  It
-	// contains a single output that pays to address in the amount of 1 DCR.
-	originTx := wire.NewMsgTx()
-	prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), wire.TxTreeRegular)
-	txIn := wire.NewTxIn(prevOut, 100000000, []byte{txscript.OP_0, txscript.OP_0})
-	originTx.AddTxIn(txIn)
-	pkScriptVer, pkScript := addr.PaymentScript()
-	txOut := wire.NewTxOut(100000000, pkScript)
-	txOut.Version = pkScriptVer
-	originTx.AddTxOut(txOut)
-	originTxHash := originTx.TxHash()
-
-	// Create the transaction to redeem the fake transaction.
-	redeemTx := wire.NewMsgTx()
-
-	// Add the input(s) the redeeming transaction will spend.  There is no
-	// signature script at this point since it hasn't been created or signed
-	// yet, hence nil is provided for it.
-	prevOut = wire.NewOutPoint(&originTxHash, 0, wire.TxTreeRegular)
-	txIn = wire.NewTxIn(prevOut, 100000000, nil)
-	redeemTx.AddTxIn(txIn)
-
-	// Ordinarily this would contain that actual destination of the funds,
-	// but for this example don't bother.
-	txOut = wire.NewTxOut(0, nil)
-	redeemTx.AddTxOut(txOut)
-
-	// Sign the redeeming transaction.
-	sigType := dcrec.STEcdsaSecp256k1
-	lookupKey := func(a stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
-		// Ordinarily this function would involve looking up the private
-		// key for the provided address, but since the only thing being
-		// signed in this example uses the address associated with the
-		// private key from above, simply return it with the compressed
-		// flag set since the address is using the associated compressed
-		// public key.
-		//
-		// NOTE: If you want to prove the code is actually signing the
-		// transaction properly, uncomment the following line which
-		// intentionally returns an invalid key to sign with, which in
-		// turn will result in a failure during the script execution
-		// when verifying the signature.
-		//
-		// privKey.D.SetInt64(12345)
-		//
-		return privKeyBytes, sigType, true, nil
-	}
-	// Notice that the script database parameter is nil here since it isn't
-	// used.  It must be specified when pay-to-script-hash transactions are
-	// being signed.
-	sigScript, err := txscript.SignTxOutput(mainNetParams, redeemTx, 0,
-		originTx.TxOut[0].PkScript, txscript.SigHashAll,
-		txscript.KeyClosure(lookupKey), nil, nil, noTreasury)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	redeemTx.TxIn[0].SignatureScript = sigScript
-
-	// Prove that the transaction has been validly signed by executing the
-	// script pair.
-
-	flags := txscript.ScriptDiscourageUpgradableNops
-	vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0,
-		flags, 0, nil)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	if err := vm.Execute(); err != nil {
-		fmt.Println(err)
-		return
-	}
-	fmt.Println("Transaction successfully signed")
-
-	// Output:
-	// Transaction successfully signed
-}
-
 // This example demonstrates creating a script tokenizer instance and using it
 // to count the number of opcodes a script contains.
 func ExampleScriptTokenizer() {
diff --git a/txscript/sign/example_test.go b/txscript/sign/example_test.go
new file mode 100644
index 00000000..f3f59815
--- /dev/null
+++ b/txscript/sign/example_test.go
@@ -0,0 +1,127 @@
+// Copyright (c) 2014-2016 The btcsuite developers
+// Copyright (c) 2015-2021 The Decred developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package sign_test
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"github.com/decred/dcrd/chaincfg/chainhash"
+	"github.com/decred/dcrd/chaincfg/v3"
+	"github.com/decred/dcrd/dcrec"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4"
+	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/sign"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
+	"github.com/decred/dcrd/wire"
+)
+
+const (
+	// noTreasury signifies the treasury agenda should be treated as though
+	// it is inactive.  It is used to increase the readability of the
+	// tests.
+	noTreasury = false
+)
+
+// This example demonstrates manually creating and signing a redeem transaction.
+func ExampleSignTxOutput() {
+	// Ordinarily the private key would come from whatever storage mechanism
+	// is being used, but for this example just hard code it.
+	privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" +
+		"d4f8720ee63e502ee2869afab7de234b80c")
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	pubKey := secp256k1.PrivKeyFromBytes(privKeyBytes).PubKey()
+	pubKeyHash := stdaddr.Hash160(pubKey.SerializeCompressed())
+	mainNetParams := chaincfg.MainNetParams()
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubKeyHash,
+		mainNetParams)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	// For this example, create a fake transaction that represents what
+	// would ordinarily be the real transaction that is being spent.  It
+	// contains a single output that pays to address in the amount of 1 DCR.
+	originTx := wire.NewMsgTx()
+	prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), wire.TxTreeRegular)
+	txIn := wire.NewTxIn(prevOut, 100000000, []byte{txscript.OP_0, txscript.OP_0})
+	originTx.AddTxIn(txIn)
+	pkScriptVer, pkScript := addr.PaymentScript()
+	txOut := wire.NewTxOut(100000000, pkScript)
+	txOut.Version = pkScriptVer
+	originTx.AddTxOut(txOut)
+	originTxHash := originTx.TxHash()
+
+	// Create the transaction to redeem the fake transaction.
+	redeemTx := wire.NewMsgTx()
+
+	// Add the input(s) the redeeming transaction will spend.  There is no
+	// signature script at this point since it hasn't been created or signed
+	// yet, hence nil is provided for it.
+	prevOut = wire.NewOutPoint(&originTxHash, 0, wire.TxTreeRegular)
+	txIn = wire.NewTxIn(prevOut, 100000000, nil)
+	redeemTx.AddTxIn(txIn)
+
+	// Ordinarily this would contain that actual destination of the funds,
+	// but for this example don't bother.
+	txOut = wire.NewTxOut(0, nil)
+	redeemTx.AddTxOut(txOut)
+
+	// Sign the redeeming transaction.
+	sigType := dcrec.STEcdsaSecp256k1
+	lookupKey := func(a stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
+		// Ordinarily this function would involve looking up the private
+		// key for the provided address, but since the only thing being
+		// signed in this example uses the address associated with the
+		// private key from above, simply return it with the compressed
+		// flag set since the address is using the associated compressed
+		// public key.
+		//
+		// NOTE: If you want to prove the code is actually signing the
+		// transaction properly, uncomment the following line which
+		// intentionally returns an invalid key to sign with, which in
+		// turn will result in a failure during the script execution
+		// when verifying the signature.
+		//
+		// privKey.D.SetInt64(12345)
+		//
+		return privKeyBytes, sigType, true, nil
+	}
+	// Notice that the script database parameter is nil here since it isn't
+	// used.  It must be specified when pay-to-script-hash transactions are
+	// being signed.
+	sigScript, err := sign.SignTxOutput(mainNetParams, redeemTx, 0,
+		originTx.TxOut[0].PkScript, txscript.SigHashAll,
+		sign.KeyClosure(lookupKey), nil, nil, noTreasury)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	redeemTx.TxIn[0].SignatureScript = sigScript
+
+	// Prove that the transaction has been validly signed by executing the
+	// script pair.
+
+	flags := txscript.ScriptDiscourageUpgradableNops
+	vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0,
+		flags, 0, nil)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	if err := vm.Execute(); err != nil {
+		fmt.Println(err)
+		return
+	}
+	fmt.Println("Transaction successfully signed")
+
+	// Output:
+	// Transaction successfully signed
+}
diff --git a/txscript/sign_test.go b/txscript/sign/sign_test.go
similarity index 97%
rename from txscript/sign_test.go
rename to txscript/sign/sign_test.go
index 031fa2c8..2ed59f85 100644
--- a/txscript/sign_test.go
+++ b/txscript/sign/sign_test.go
@@ -3,7 +3,7 @@
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
-package txscript
+package sign
 
 import (
 	"crypto/rand"
@@ -17,6 +17,7 @@ import (
 	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/edwards/v2"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
+	"github.com/decred/dcrd/txscript/v4"
 	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
@@ -98,8 +99,8 @@ func mkGetScript(scripts map[string][]byte) ScriptDB {
 
 func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error {
 	tx.TxIn[idx].SignatureScript = sigScript
-	var scriptFlags ScriptFlags
-	vm, err := NewEngine(pkScript, tx, idx, scriptFlags, 0, nil)
+	var scriptFlags txscript.ScriptFlags
+	vm, err := txscript.NewEngine(pkScript, tx, idx, scriptFlags, 0, nil)
 	if err != nil {
 		return fmt.Errorf("failed to make script engine for %s: %v",
 			msg, err)
@@ -114,7 +115,9 @@ func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byt
 	return nil
 }
 
-func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, isTreasuryEnabled bool) error {
+func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte,
+	hashType txscript.SigHashType, kdb KeyDB, sdb ScriptDB,
+	isTreasuryEnabled bool) error {
 
 	sigScript, err := SignTxOutput(testingParams, tx, idx, pkScript,
 		hashType, kdb, sdb, nil, isTreasuryEnabled)
@@ -125,7 +128,10 @@ func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, hashType
 	return checkScripts(msg, tx, idx, sigScript, pkScript)
 }
 
-func signBadAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, isTreasuryEnabled bool) error {
+func signBadAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte,
+	hashType txscript.SigHashType, kdb KeyDB, sdb ScriptDB,
+	isTreasuryEnabled bool) error {
+
 	// Setup a PRNG.
 	randScriptHash := chainhash.HashB(pkScript)
 	tRand := mrand.New(mrand.NewSource(int64(randScriptHash[0])))
@@ -154,13 +160,13 @@ func TestSignTxOutput(t *testing.T) {
 	// make key
 	// make script based on key.
 	// sign with magic pixie dust.
-	hashTypes := []SigHashType{
-		SigHashAll,
-		SigHashNone,
-		SigHashSingle,
-		SigHashAll | SigHashAnyOneCanPay,
-		SigHashNone | SigHashAnyOneCanPay,
-		SigHashSingle | SigHashAnyOneCanPay,
+	hashTypes := []txscript.SigHashType{
+		txscript.SigHashAll,
+		txscript.SigHashNone,
+		txscript.SigHashSingle,
+		txscript.SigHashAll | txscript.SigHashAnyOneCanPay,
+		txscript.SigHashNone | txscript.SigHashAnyOneCanPay,
+		txscript.SigHashSingle | txscript.SigHashAnyOneCanPay,
 	}
 	signatureSuites := []dcrec.SignatureType{
 		dcrec.STEcdsaSecp256k1,
@@ -2073,8 +2079,8 @@ func TestSignTxOutput(t *testing.T) {
 				break
 			}
 
-			pkScript, err := MultiSigScript(2, pk1.SerializeCompressed(),
-				pk2.SerializeCompressed())
+			pkScript, err := txscript.MultiSigScript(2,
+				pk1.SerializeCompressed(), pk2.SerializeCompressed())
 			if err != nil {
 				t.Errorf("failed to make pkscript for %s: %v", msg, err)
 			}
@@ -2171,8 +2177,8 @@ func TestSignTxOutput(t *testing.T) {
 				break
 			}
 
-			pkScript, err := MultiSigScript(2, pk1.SerializeCompressed(),
-				pk2.SerializeCompressed())
+			pkScript, err := txscript.MultiSigScript(2,
+				pk1.SerializeCompressed(), pk2.SerializeCompressed())
 			if err != nil {
 				t.Errorf("failed to make pkscript "+
 					"for %s: %v", msg, err)
@@ -2310,8 +2316,8 @@ func TestSignTxOutput(t *testing.T) {
 				break
 			}
 
-			pkScript, err := MultiSigScript(2, pk1.SerializeCompressed(),
-				pk2.SerializeCompressed())
+			pkScript, err := txscript.MultiSigScript(2,
+				pk1.SerializeCompressed(), pk2.SerializeCompressed())
 			if err != nil {
 				t.Errorf("failed to make pkscript for %s: %v", msg, err)
 			}
@@ -2432,7 +2438,7 @@ type tstInput struct {
 type tstSigScript struct {
 	name               string
 	inputs             []tstInput
-	hashType           SigHashType
+	hashType           txscript.SigHashType
 	compress           bool
 	scriptAtWrongIndex bool
 }
@@ -2477,7 +2483,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2497,7 +2503,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2511,7 +2517,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           true,
 		scriptAtWrongIndex: false,
 	},
@@ -2531,7 +2537,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           true,
 		scriptAtWrongIndex: false,
 	},
@@ -2545,7 +2551,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashNone,
+		hashType:           txscript.SigHashNone,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2559,7 +2565,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashSingle,
+		hashType:           txscript.SigHashSingle,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2573,7 +2579,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAnyOneCanPay | SigHashAll,
+		hashType:           txscript.SigHashAnyOneCanPay | txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2601,7 +2607,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           true,
 		scriptAtWrongIndex: false,
 	},
@@ -2614,7 +2620,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: false,
 	},
@@ -2634,7 +2640,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: true,
 	},
@@ -2654,7 +2660,7 @@ var sigScriptTests = []tstSigScript{
 				indexOutOfRange:    false,
 			},
 		},
-		hashType:           SigHashAll,
+		hashType:           txscript.SigHashAll,
 		compress:           false,
 		scriptAtWrongIndex: true,
 	},
@@ -2672,7 +2678,7 @@ nexttest:
 	for i := range sigScriptTests {
 		tx := wire.NewMsgTx()
 
-		output := wire.NewTxOut(500, []byte{OP_RETURN})
+		output := wire.NewTxOut(500, []byte{txscript.OP_RETURN})
 		tx.AddTxOut(output)
 
 		for range sigScriptTests[i].inputs {
@@ -2722,9 +2728,9 @@ nexttest:
 		}
 
 		// Validate tx input scripts
-		var scriptFlags ScriptFlags
+		var scriptFlags txscript.ScriptFlags
 		for j := range tx.TxIn {
-			vm, err := NewEngine(sigScriptTests[i].inputs[j].txout.
+			vm, err := txscript.NewEngine(sigScriptTests[i].inputs[j].txout.
 				PkScript, tx, j, scriptFlags, 0,
 				nil)
 			if err != nil {