WIP: next hard fork #5

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

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) {