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:
parent
b8e25c397c
commit
5283e30bfc
1 changed files with 51 additions and 37 deletions
|
@ -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
|
||||
// have come from other functions internally and thus are all consistent with
|
||||
// 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,
|
||||
nRequired int, pkScript, sigScript, prevScript []byte) []byte {
|
||||
|
||||
// This is an internal only function and we already parsed this script
|
||||
// as ok for multisig (this is how we got here), so if this fails then
|
||||
// all assumptions are broken and who knows which way is up?
|
||||
pkPops, _ := parseScript(pkScript)
|
||||
|
||||
sigPops, err := parseScript(sigScript)
|
||||
if err != nil || len(sigPops) == 0 {
|
||||
// Nothing to merge if either the new or previous signature scripts are
|
||||
// empty.
|
||||
if len(sigScript) == 0 {
|
||||
return prevScript
|
||||
}
|
||||
|
||||
prevPops, err := parseScript(prevScript)
|
||||
if err != nil || len(prevPops) == 0 {
|
||||
if len(prevScript) == 0 {
|
||||
return sigScript
|
||||
}
|
||||
|
||||
// Convenience function to avoid duplication.
|
||||
extractSigs := func(pops []parsedOpcode, sigs [][]byte) [][]byte {
|
||||
for _, pop := range pops {
|
||||
if len(pop.data) != 0 {
|
||||
sigs = append(sigs, pop.data)
|
||||
var possibleSigs [][]byte
|
||||
extractSigs := func(script []byte) error {
|
||||
const scriptVersion = 0
|
||||
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))
|
||||
possibleSigs = extractSigs(sigPops, possibleSigs)
|
||||
possibleSigs = extractSigs(prevPops, possibleSigs)
|
||||
// Attempt to extract signatures from the two scripts. Return the other
|
||||
// script that is intended to be merged in the case signature extraction
|
||||
// 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
|
||||
// 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
|
||||
// would make the transaction nonstandard and thus not
|
||||
// MultiSigTy, so we just need to hash the full thing.
|
||||
hash, err := calcSignatureHash(pkPops, hashType, tx, idx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot compute sighash: %v", err))
|
||||
}
|
||||
hash := calcSignatureHashRaw(pkScript, hashType, tx, idx)
|
||||
|
||||
for _, addr := range 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
|
||||
// function with addresses, class and nrequired that do not match pkScript is
|
||||
// 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,
|
||||
pkScript []byte, class ScriptClass, addresses []btcutil.Address,
|
||||
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.
|
||||
// some internal refactoring could probably make this avoid needless
|
||||
// extra calculations.
|
||||
const scriptVersion = 0
|
||||
switch class {
|
||||
case ScriptHashTy:
|
||||
// Remove the last push in the script and then recurse.
|
||||
// this could be a lot less inefficient.
|
||||
sigPops, err := parseScript(sigScript)
|
||||
if err != nil || len(sigPops) == 0 {
|
||||
// Nothing to merge if either the new or previous signature
|
||||
// scripts are empty or fail to parse.
|
||||
if len(sigScript) == 0 ||
|
||||
checkScriptParses(scriptVersion, sigScript) != nil {
|
||||
|
||||
return prevScript
|
||||
}
|
||||
prevPops, err := parseScript(prevScript)
|
||||
if err != nil || len(prevPops) == 0 {
|
||||
if len(prevScript) == 0 ||
|
||||
checkScriptParses(scriptVersion, prevScript) != nil {
|
||||
|
||||
return sigScript
|
||||
}
|
||||
|
||||
// assume that script in sigPops is the correct one, we just
|
||||
// made it.
|
||||
script := sigPops[len(sigPops)-1].data
|
||||
// Remove the last push in the script and then recurse.
|
||||
// this could be a lot less inefficient.
|
||||
//
|
||||
// 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,
|
||||
// therefore the error is ignored.
|
||||
class, addresses, nrequired, _ :=
|
||||
ExtractPkScriptAddrs(script, chainParams)
|
||||
|
||||
// regenerate scripts.
|
||||
sigScript, _ := unparseScript(sigPops)
|
||||
prevScript, _ := unparseScript(prevPops)
|
||||
|
||||
// Merge
|
||||
mergedScript := mergeScripts(chainParams, tx, idx, script,
|
||||
class, addresses, nrequired, sigScript, prevScript)
|
||||
|
@ -380,6 +390,7 @@ func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
|
|||
builder.AddData(script)
|
||||
finalScript, _ := builder.Script()
|
||||
return finalScript
|
||||
|
||||
case MultiSigTy:
|
||||
return mergeMultiSig(tx, idx, addresses, nRequired, pkScript,
|
||||
sigScript, prevScript)
|
||||
|
@ -408,8 +419,7 @@ type KeyDB interface {
|
|||
type KeyClosure func(btcutil.Address) (*btcec.PrivateKey, bool, error)
|
||||
|
||||
// GetKey implements KeyDB by returning the result of calling the closure.
|
||||
func (kc KeyClosure) GetKey(address btcutil.Address) (*btcec.PrivateKey,
|
||||
bool, error) {
|
||||
func (kc KeyClosure) GetKey(address btcutil.Address) (*btcec.PrivateKey, bool, error) {
|
||||
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
|
||||
// will be merged in a type-dependent manner with the newly generated.
|
||||
// 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,
|
||||
pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB,
|
||||
previousScript []byte) ([]byte, error) {
|
||||
|
|
Loading…
Reference in a new issue