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
|
// 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) {
|
||||||
|
|
Loading…
Reference in a new issue