txscript: Use raw scripts in SignTxOutput.

This converts SignTxOutput and supporting funcs, namely sign,
mergeScripts and mergeMultiSig, to make use of the new tokenizer as well
as some recently added funcs that deal with raw scripts in order to
remove the reliance on parsed opcodes as a step towards utlimately
removing them altogether and updates the comments to explicitly call out
the script version semantics.

It is worth noting that this has the side effect of optimizing the
function as well, however, since this change is not focused on the
optimization aspects, no benchmarks are provided.
This commit is contained in:
Dave Collins 2019-03-13 01:12:50 -05:00 committed by Roy Lee
parent b8e25c397c
commit 5283e30bfc

View file

@ -218,37 +218,44 @@ func sign(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
// pkScript. Since this function is internal only we assume that the arguments // pkScript. Since this function is internal only we assume that the arguments
// have come from other functions internally and thus are all consistent with // have come from other functions internally and thus are all consistent with
// each other, behaviour is undefined if this contract is broken. // each other, behaviour is undefined if this contract is broken.
//
// 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 mergeMultiSig(tx *wire.MsgTx, idx int, addresses []btcutil.Address, func mergeMultiSig(tx *wire.MsgTx, idx int, addresses []btcutil.Address,
nRequired int, pkScript, sigScript, prevScript []byte) []byte { nRequired int, pkScript, sigScript, prevScript []byte) []byte {
// This is an internal only function and we already parsed this script // Nothing to merge if either the new or previous signature scripts are
// as ok for multisig (this is how we got here), so if this fails then // empty.
// all assumptions are broken and who knows which way is up? if len(sigScript) == 0 {
pkPops, _ := parseScript(pkScript)
sigPops, err := parseScript(sigScript)
if err != nil || len(sigPops) == 0 {
return prevScript return prevScript
} }
if len(prevScript) == 0 {
prevPops, err := parseScript(prevScript)
if err != nil || len(prevPops) == 0 {
return sigScript return sigScript
} }
// Convenience function to avoid duplication. // Convenience function to avoid duplication.
extractSigs := func(pops []parsedOpcode, sigs [][]byte) [][]byte { var possibleSigs [][]byte
for _, pop := range pops { extractSigs := func(script []byte) error {
if len(pop.data) != 0 { const scriptVersion = 0
sigs = append(sigs, pop.data) tokenizer := MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
if data := tokenizer.Data(); len(data) != 0 {
possibleSigs = append(possibleSigs, data)
} }
} }
return sigs return tokenizer.Err()
} }
possibleSigs := make([][]byte, 0, len(sigPops)+len(prevPops)) // Attempt to extract signatures from the two scripts. Return the other
possibleSigs = extractSigs(sigPops, possibleSigs) // script that is intended to be merged in the case signature extraction
possibleSigs = extractSigs(prevPops, possibleSigs) // fails for some reason.
if err := extractSigs(sigScript); err != nil {
return prevScript
}
if err := extractSigs(prevScript); err != nil {
return sigScript
}
// Now we need to match the signatures to pubkeys, the only real way to // Now we need to match the signatures to pubkeys, the only real way to
// do that is to try to verify them all and match it to the pubkey // do that is to try to verify them all and match it to the pubkey
@ -278,10 +285,7 @@ sigLoop:
// however, assume no sigs etc are in the script since that // however, assume no sigs etc are in the script since that
// would make the transaction nonstandard and thus not // would make the transaction nonstandard and thus not
// MultiSigTy, so we just need to hash the full thing. // MultiSigTy, so we just need to hash the full thing.
hash, err := calcSignatureHash(pkPops, hashType, tx, idx) hash := calcSignatureHashRaw(pkScript, hashType, tx, idx)
if err != nil {
panic(fmt.Sprintf("cannot compute sighash: %v", err))
}
for _, addr := range addresses { for _, addr := range addresses {
// All multisig addresses should be pubkey addresses // All multisig addresses should be pubkey addresses
@ -336,6 +340,10 @@ sigLoop:
// The return value is the best effort merging of the two scripts. Calling this // The return value is the best effort merging of the two scripts. Calling this
// function with addresses, class and nrequired that do not match pkScript is // function with addresses, class and nrequired that do not match pkScript is
// an error and results in undefined behaviour. // an error and results in undefined behaviour.
//
// 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 *chaincfg.Params, tx *wire.MsgTx, idx int, func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
pkScript []byte, class ScriptClass, addresses []btcutil.Address, pkScript []byte, class ScriptClass, addresses []btcutil.Address,
nRequired int, sigScript, prevScript []byte) []byte { nRequired int, sigScript, prevScript []byte) []byte {
@ -344,32 +352,34 @@ func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
// inefficient in that they will recompute already known data. // inefficient in that they will recompute already known data.
// some internal refactoring could probably make this avoid needless // some internal refactoring could probably make this avoid needless
// extra calculations. // extra calculations.
const scriptVersion = 0
switch class { switch class {
case ScriptHashTy: case ScriptHashTy:
// Remove the last push in the script and then recurse. // Nothing to merge if either the new or previous signature
// this could be a lot less inefficient. // scripts are empty or fail to parse.
sigPops, err := parseScript(sigScript) if len(sigScript) == 0 ||
if err != nil || len(sigPops) == 0 { checkScriptParses(scriptVersion, sigScript) != nil {
return prevScript return prevScript
} }
prevPops, err := parseScript(prevScript) if len(prevScript) == 0 ||
if err != nil || len(prevPops) == 0 { checkScriptParses(scriptVersion, prevScript) != nil {
return sigScript return sigScript
} }
// assume that script in sigPops is the correct one, we just // Remove the last push in the script and then recurse.
// made it. // this could be a lot less inefficient.
script := sigPops[len(sigPops)-1].data //
// Assume that final script is the correct one since it was just
// made and it is a pay-to-script-hash.
script := finalOpcodeData(scriptVersion, sigScript)
// We already know this information somewhere up the stack, // We already know this information somewhere up the stack,
// therefore the error is ignored. // therefore the error is ignored.
class, addresses, nrequired, _ := class, addresses, nrequired, _ :=
ExtractPkScriptAddrs(script, chainParams) ExtractPkScriptAddrs(script, chainParams)
// regenerate scripts.
sigScript, _ := unparseScript(sigPops)
prevScript, _ := unparseScript(prevPops)
// Merge // Merge
mergedScript := mergeScripts(chainParams, tx, idx, script, mergedScript := mergeScripts(chainParams, tx, idx, script,
class, addresses, nrequired, sigScript, prevScript) class, addresses, nrequired, sigScript, prevScript)
@ -380,6 +390,7 @@ func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
builder.AddData(script) builder.AddData(script)
finalScript, _ := builder.Script() finalScript, _ := builder.Script()
return finalScript return finalScript
case MultiSigTy: case MultiSigTy:
return mergeMultiSig(tx, idx, addresses, nRequired, pkScript, return mergeMultiSig(tx, idx, addresses, nRequired, pkScript,
sigScript, prevScript) sigScript, prevScript)
@ -408,8 +419,7 @@ type KeyDB interface {
type KeyClosure func(btcutil.Address) (*btcec.PrivateKey, bool, error) type KeyClosure func(btcutil.Address) (*btcec.PrivateKey, bool, error)
// GetKey implements KeyDB by returning the result of calling the closure. // GetKey implements KeyDB by returning the result of calling the closure.
func (kc KeyClosure) GetKey(address btcutil.Address) (*btcec.PrivateKey, func (kc KeyClosure) GetKey(address btcutil.Address) (*btcec.PrivateKey, bool, error) {
bool, error) {
return kc(address) return kc(address)
} }
@ -434,6 +444,10 @@ func (sc ScriptClosure) GetScript(address btcutil.Address) ([]byte, error) {
// getScript. If previousScript is provided then the results in previousScript // getScript. If previousScript is provided then the results in previousScript
// will be merged in a type-dependent manner with the newly generated. // will be merged in a type-dependent manner with the newly generated.
// signature script. // signature script.
//
// 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 *chaincfg.Params, tx *wire.MsgTx, idx int, func SignTxOutput(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB,
previousScript []byte) ([]byte, error) { previousScript []byte) ([]byte, error) {