WIP: next hard fork #5

Draft
BrannonKing wants to merge 178 commits from WIP-HF-2022 into master
Showing only changes of commit 7ad3a10442 - Show all commits

View file

@ -212,73 +212,6 @@ func sign(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
}
}
// 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.
// 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.
func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
pkScript []byte, class ScriptClass, addresses []btcutil.Address,
nRequired int, sigScript, prevScript []byte) []byte {
// TODO: the scripthash and multisig paths here are overly
// inefficient in that they will recompute already known data.
// some internal refactoring could probably make this avoid needless
// extra calculations.
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 {
return prevScript
}
prevPops, err := parseScript(prevScript)
if err != nil || len(prevPops) == 0 {
return sigScript
}
// assume that script in sigPops is the correct one, we just
// made it.
script := sigPops[len(sigPops)-1].data
// We already know this information somewhere up the stack.
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)
// Reappend the script and return the result.
builder := NewScriptBuilder()
builder.AddOps(mergedScript)
builder.AddData(script)
finalScript, _ := builder.Script()
return finalScript
case MultiSigTy:
return mergeMultiSig(tx, idx, addresses, nRequired, pkScript,
sigScript, prevScript)
// It doesn't actually make sense to merge anything other than multiig
// and scripthash (because it could contain multisig). Everything else
// has either zero signature, can't be spent, or has a single signature
// which is either present or not. The other two cases are handled
// above. In the conflict case here we just assume the longest is
// correct (this matches behaviour of the reference implementation).
default:
if len(sigScript) > len(prevScript) {
return sigScript
}
return prevScript
}
}
// mergeMultiSig combines the two signature scripts sigScript and prevScript
// that both provide signatures for pkScript in output idx of tx. addresses
// and nRequired should be the results from extracting the addresses from
@ -397,6 +330,74 @@ sigLoop:
return script
}
// 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.
// 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.
func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int,
pkScript []byte, class ScriptClass, addresses []btcutil.Address,
nRequired int, sigScript, prevScript []byte) []byte {
// TODO(oga) the scripthash and multisig paths here are overly
// inefficient in that they will recompute already known data.
// some internal refactoring could probably make this avoid needless
// extra calculations.
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 {
return prevScript
}
prevPops, err := parseScript(prevScript)
if err != nil || len(prevPops) == 0 {
return sigScript
}
// assume that script in sigPops is the correct one, we just
// made it.
script := sigPops[len(sigPops)-1].data
// 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)
// Reappend the script and return the result.
builder := NewScriptBuilder()
builder.AddOps(mergedScript)
builder.AddData(script)
finalScript, _ := builder.Script()
return finalScript
case MultiSigTy:
return mergeMultiSig(tx, idx, addresses, nRequired, pkScript,
sigScript, prevScript)
// It doesn't actually make sense to merge anything other than multiig
// and scripthash (because it could contain multisig). Everything else
// has either zero signature, can't be spent, or has a single signature
// which is either present or not. The other two cases are handled
// above. In the conflict case here we just assume the longest is
// correct (this matches behaviour of the reference implementation).
default:
if len(sigScript) > len(prevScript) {
return sigScript
}
return prevScript
}
}
// KeyDB is an interface type provided to SignTxOutput, it encapsulates
// any user state required to get the private keys for an address.
type KeyDB interface {