BIP0141+txscript: implement witness program validation
This commit implements full witness program validation for the currently defined version 0 witness programs. This includes validation logic for nested p2sh, p2wsh, and p2wkh. Additionally, when in witness validation mode, an additional set of constrains are enforced such as using the new sighash digest algorithm and enforcing clean stack behavior within witness programs.
This commit is contained in:
parent
b564111aff
commit
aaf187427e
6 changed files with 246 additions and 56 deletions
|
@ -5,6 +5,8 @@
|
||||||
package txscript
|
package txscript
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
@ -46,7 +48,8 @@ const (
|
||||||
// ScriptVerifyCleanStack defines that the stack must contain only
|
// ScriptVerifyCleanStack defines that the stack must contain only
|
||||||
// one stack element after evaluation and that the element must be
|
// one stack element after evaluation and that the element must be
|
||||||
// true if interpreted as a boolean. This is rule 6 of BIP0062.
|
// true if interpreted as a boolean. This is rule 6 of BIP0062.
|
||||||
// This flag should never be used without the ScriptBip16 flag.
|
// This flag should never be used without the ScriptBip16 flag nor the
|
||||||
|
// ScriptVerifyWitness flag.
|
||||||
ScriptVerifyCleanStack
|
ScriptVerifyCleanStack
|
||||||
|
|
||||||
// ScriptVerifyDERSignatures defines that signatures are required
|
// ScriptVerifyDERSignatures defines that signatures are required
|
||||||
|
@ -73,6 +76,14 @@ const (
|
||||||
// ScriptVerifyStrictEncoding defines that signature scripts and
|
// ScriptVerifyStrictEncoding defines that signature scripts and
|
||||||
// public keys must follow the strict encoding requirements.
|
// public keys must follow the strict encoding requirements.
|
||||||
ScriptVerifyStrictEncoding
|
ScriptVerifyStrictEncoding
|
||||||
|
|
||||||
|
// ScriptVerifyWitness defines whether or not to verify a transaction
|
||||||
|
// output using a witness program template.
|
||||||
|
ScriptVerifyWitness
|
||||||
|
|
||||||
|
// ScriptVerifyDiscourageUpgradeableWitnessProgram makes witness
|
||||||
|
// program with versions 2-16 non-standard.
|
||||||
|
ScriptVerifyDiscourageUpgradeableWitnessProgram
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -82,6 +93,14 @@ const (
|
||||||
|
|
||||||
// MaxScriptSize is the maximum allowed length of a raw script.
|
// MaxScriptSize is the maximum allowed length of a raw script.
|
||||||
MaxScriptSize = 10000
|
MaxScriptSize = 10000
|
||||||
|
|
||||||
|
// payToWitnessPubKeyHashDataSize is the size of the witness program's
|
||||||
|
// data push for a pay-to-witness-pub-key-hash output.
|
||||||
|
payToWitnessPubKeyHashDataSize = 20
|
||||||
|
|
||||||
|
// payToWitnessScriptHashDataSize is the size of the witness program's
|
||||||
|
// data push for a pay-to-witness-script-hash output.
|
||||||
|
payToWitnessScriptHashDataSize = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// halforder is used to tame ECDSA malleability (see BIP0062).
|
// halforder is used to tame ECDSA malleability (see BIP0062).
|
||||||
|
@ -101,8 +120,13 @@ type Engine struct {
|
||||||
numOps int
|
numOps int
|
||||||
flags ScriptFlags
|
flags ScriptFlags
|
||||||
sigCache *SigCache
|
sigCache *SigCache
|
||||||
|
hashCache *TxSigHashes
|
||||||
bip16 bool // treat execution as pay-to-script-hash
|
bip16 bool // treat execution as pay-to-script-hash
|
||||||
savedFirstStack [][]byte // stack from first script for bip16 scripts
|
savedFirstStack [][]byte // stack from first script for bip16 scripts
|
||||||
|
witness bool // treat execution as a witness program
|
||||||
|
witnessVersion int
|
||||||
|
witnessProgram []byte
|
||||||
|
inputAmount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasFlag returns whether the script engine instance has the passed flag set.
|
// hasFlag returns whether the script engine instance has the passed flag set.
|
||||||
|
@ -209,6 +233,115 @@ func (vm *Engine) curPC() (script int, off int, err error) {
|
||||||
return vm.scriptIdx, vm.scriptOff, nil
|
return vm.scriptIdx, vm.scriptOff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verifyWitnessProgram validates the stored witness program using the passed
|
||||||
|
// witness as input.
|
||||||
|
func (vm *Engine) verifyWitnessProgram(witness [][]byte) error {
|
||||||
|
if vm.witnessVersion == 0 {
|
||||||
|
switch len(vm.witnessProgram) {
|
||||||
|
case payToWitnessPubKeyHashDataSize: // P2WKH
|
||||||
|
// The witness stack should consist of exactly two
|
||||||
|
// items: the signature, and the pubkey.
|
||||||
|
if len(witness) != 2 {
|
||||||
|
err := fmt.Sprintf("should have exactly two "+
|
||||||
|
"items in witness, instead have %v", len(witness))
|
||||||
|
return scriptError(ErrWitnessScriptMismatch, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we'll resume execution as if it were a regular
|
||||||
|
// p2pkh transaction.
|
||||||
|
pkScript, err := payToPubKeyHashScript(vm.witnessProgram)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pops, err := parseScript(pkScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the stack to the provided witness stack, then
|
||||||
|
// append the pkScript generated above as the next
|
||||||
|
// script to execute.
|
||||||
|
vm.scripts = append(vm.scripts, pops)
|
||||||
|
vm.SetStack(witness)
|
||||||
|
|
||||||
|
case payToWitnessScriptHashDataSize: // P2WSH
|
||||||
|
|
||||||
|
// Additionally, The witness stack MUST NOT be empty at
|
||||||
|
// this point.
|
||||||
|
if len(witness) == 0 {
|
||||||
|
return scriptError(ErrWitnessProgramEmpty, "witness "+
|
||||||
|
"program empty passed empty witness")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain the witness script which should be the last
|
||||||
|
// element in the passed stack. The size of the script
|
||||||
|
// MUST NOT exceed the max script size.
|
||||||
|
witnessScript := witness[len(witness)-1]
|
||||||
|
if len(witnessScript) > MaxScriptSize {
|
||||||
|
str := fmt.Sprintf("witnessScript size %d "+
|
||||||
|
"is larger than max allowed size %d",
|
||||||
|
len(witnessScript), MaxScriptSize)
|
||||||
|
return scriptError(ErrScriptTooBig, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the serialized pkScript at the end of
|
||||||
|
// the witness stack matches the witness program.
|
||||||
|
witnessHash := sha256.Sum256(witnessScript)
|
||||||
|
if !bytes.Equal(witnessHash[:], vm.witnessProgram) {
|
||||||
|
return scriptError(ErrWitnessScriptMismatch,
|
||||||
|
"witness program hash mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// With all the validity checks passed, parse the
|
||||||
|
// script into individual op-codes so w can execute it
|
||||||
|
// as the next script.
|
||||||
|
pops, err := parseScript(witnessScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The hash matched successfully, so use the witness as
|
||||||
|
// the stack, and set the witnessScript to be the next
|
||||||
|
// script executed.
|
||||||
|
vm.scripts = append(vm.scripts, pops)
|
||||||
|
vm.SetStack(witness[:len(witness)-1])
|
||||||
|
|
||||||
|
default:
|
||||||
|
errStr := fmt.Sprintf("length of witness program "+
|
||||||
|
"must either be %v or %v bytes, instead is %v bytes",
|
||||||
|
payToWitnessPubKeyHashDataSize,
|
||||||
|
payToWitnessScriptHashDataSize,
|
||||||
|
len(vm.witnessProgram))
|
||||||
|
return scriptError(ErrWitnessProgramWrongLength, errStr)
|
||||||
|
}
|
||||||
|
} else if vm.hasFlag(ScriptVerifyDiscourageUpgradeableWitnessProgram) {
|
||||||
|
return fmt.Errorf("new witness program versions invalid: %v",
|
||||||
|
vm.witnessVersion)
|
||||||
|
} else {
|
||||||
|
// If we encounter an unknown witness program version and we
|
||||||
|
// aren't discouraging future unknown witness based soft-forks,
|
||||||
|
// then we de-activate the segwit behavior within the VM for
|
||||||
|
// the remainder of execution.
|
||||||
|
vm.witness = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if vm.witness {
|
||||||
|
// All elements within the witness stack must not be greater
|
||||||
|
// than the maximum bytes which are allowed to be pushed onto
|
||||||
|
// the stack.
|
||||||
|
for _, witElement := range vm.GetStack() {
|
||||||
|
if len(witElement) > MaxScriptElementSize {
|
||||||
|
str := fmt.Sprintf("element size %d exceeds "+
|
||||||
|
"max allowed size %d", len(witElement),
|
||||||
|
MaxScriptElementSize)
|
||||||
|
return scriptError(ErrElementTooBig, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DisasmPC returns the string for the disassembly of the opcode that will be
|
// DisasmPC returns the string for the disassembly of the opcode that will be
|
||||||
// next to execute when Step() is called.
|
// next to execute when Step() is called.
|
||||||
func (vm *Engine) DisasmPC() (string, error) {
|
func (vm *Engine) DisasmPC() (string, error) {
|
||||||
|
@ -246,6 +379,14 @@ func (vm *Engine) CheckErrorCondition(finalScript bool) error {
|
||||||
return scriptError(ErrScriptUnfinished,
|
return scriptError(ErrScriptUnfinished,
|
||||||
"error check when script unfinished")
|
"error check when script unfinished")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're in witness execution mode, and this was the final script,
|
||||||
|
// then the stack MUST be clean in order to maintain compatibility with
|
||||||
|
// BIP16.
|
||||||
|
if finalScript && vm.witness && vm.dstack.Depth() != 1 {
|
||||||
|
return ErrStackCleanStack
|
||||||
|
}
|
||||||
|
|
||||||
if finalScript && vm.hasFlag(ScriptVerifyCleanStack) &&
|
if finalScript && vm.hasFlag(ScriptVerifyCleanStack) &&
|
||||||
vm.dstack.Depth() != 1 {
|
vm.dstack.Depth() != 1 {
|
||||||
|
|
||||||
|
@ -343,6 +484,14 @@ func (vm *Engine) Step() (done bool, err error) {
|
||||||
// Set stack to be the stack from first script minus the
|
// Set stack to be the stack from first script minus the
|
||||||
// script itself
|
// script itself
|
||||||
vm.SetStack(vm.savedFirstStack[:len(vm.savedFirstStack)-1])
|
vm.SetStack(vm.savedFirstStack[:len(vm.savedFirstStack)-1])
|
||||||
|
} else if (vm.scriptIdx == 1 && vm.witness) ||
|
||||||
|
(vm.scriptIdx == 2 && vm.witness && vm.bip16) { // Nested P2SH.
|
||||||
|
vm.scriptIdx++
|
||||||
|
|
||||||
|
witness := vm.tx.TxIn[vm.txIdx].Witness
|
||||||
|
if err := vm.verifyWitnessProgram(witness); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vm.scriptIdx++
|
vm.scriptIdx++
|
||||||
}
|
}
|
||||||
|
@ -626,7 +775,9 @@ func (vm *Engine) SetAltStack(data [][]byte) {
|
||||||
// NewEngine returns a new script engine for the provided public key script,
|
// NewEngine returns a new script engine for the provided public key script,
|
||||||
// transaction, and input index. The flags modify the behavior of the script
|
// transaction, and input index. The flags modify the behavior of the script
|
||||||
// engine according to the description provided by each flag.
|
// engine according to the description provided by each flag.
|
||||||
func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags, sigCache *SigCache) (*Engine, error) {
|
func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags,
|
||||||
|
sigCache *SigCache, hashCache *TxSigHashes, inputAmount int64) (*Engine, error) {
|
||||||
|
|
||||||
// The provided transaction input index must refer to a valid input.
|
// The provided transaction input index must refer to a valid input.
|
||||||
if txIdx < 0 || txIdx >= len(tx.TxIn) {
|
if txIdx < 0 || txIdx >= len(tx.TxIn) {
|
||||||
str := fmt.Sprintf("transaction input index %d is negative or "+
|
str := fmt.Sprintf("transaction input index %d is negative or "+
|
||||||
|
@ -735,17 +886,14 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
|
||||||
|
|
||||||
witProgram = scriptPubKey
|
witProgram = scriptPubKey
|
||||||
case len(tx.TxIn[txIdx].Witness) != 0 && vm.bip16:
|
case len(tx.TxIn[txIdx].Witness) != 0 && vm.bip16:
|
||||||
pops, err := parseScript(scriptSig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sigScript MUST be *exactly* a single canonical
|
// The sigScript MUST be *exactly* a single canonical
|
||||||
// data push of the witness program, otherwise we
|
// data push of the witness program, otherwise we
|
||||||
// reintroduce malleability.
|
// reintroduce malleability.
|
||||||
if len(pops) == 1 && canonicalPush(pops[0]) &&
|
sigPops := vm.scripts[0]
|
||||||
IsWitnessProgram(pops[0].data) {
|
if len(sigPops) == 1 && canonicalPush(sigPops[0]) &&
|
||||||
witProgram = pops[0].data
|
IsWitnessProgram(sigPops[0].data) {
|
||||||
|
|
||||||
|
witProgram = sigPops[0].data
|
||||||
} else {
|
} else {
|
||||||
errStr := "signature script for witness " +
|
errStr := "signature script for witness " +
|
||||||
"nested p2sh is not canonical"
|
"nested p2sh is not canonical"
|
||||||
|
|
|
@ -54,7 +54,7 @@ func TestBadPC(t *testing.T) {
|
||||||
pkScript := mustParseShortForm("NOP")
|
pkScript := mustParseShortForm("NOP")
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
vm, err := NewEngine(pkScript, tx, 0, 0, nil)
|
vm, err := NewEngine(pkScript, tx, 0, 0, nil, nil, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to create script: %v", err)
|
t.Errorf("Failed to create script: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func TestCheckErrorCondition(t *testing.T) {
|
||||||
pkScript := mustParseShortForm("NOP NOP NOP NOP NOP NOP NOP NOP NOP" +
|
pkScript := mustParseShortForm("NOP NOP NOP NOP NOP NOP NOP NOP NOP" +
|
||||||
" NOP TRUE")
|
" NOP TRUE")
|
||||||
|
|
||||||
vm, err := NewEngine(pkScript, tx, 0, 0, nil)
|
vm, err := NewEngine(pkScript, tx, 0, 0, nil, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create script: %v", err)
|
t.Errorf("failed to create script: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ func TestInvalidFlagCombinations(t *testing.T) {
|
||||||
pkScript := []byte{OP_NOP}
|
pkScript := []byte{OP_NOP}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
_, err := NewEngine(pkScript, tx, 0, test, nil)
|
_, err := NewEngine(pkScript, tx, 0, test, nil, nil, -1)
|
||||||
if !IsErrorCode(err, ErrInvalidFlags) {
|
if !IsErrorCode(err, ErrInvalidFlags) {
|
||||||
t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+
|
t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+
|
||||||
"error: %v", i, err)
|
"error: %v", i, err)
|
||||||
|
|
|
@ -227,6 +227,34 @@ const (
|
||||||
// reached.
|
// reached.
|
||||||
ErrUnsatisfiedLockTime
|
ErrUnsatisfiedLockTime
|
||||||
|
|
||||||
|
// ErrWitnessProgramEmpty is returned if ScriptVerifyWitness is set and
|
||||||
|
// the witness stack itself is empty.
|
||||||
|
ErrWitnessProgramEmpty
|
||||||
|
|
||||||
|
// ErrWitnessScriptMismatch is returned if ScriptVerifyWitness is set
|
||||||
|
// and the witness itself for a p2wkh witness program isn't *exactly* 2
|
||||||
|
// items.
|
||||||
|
ErrWitnessScriptMismatch
|
||||||
|
|
||||||
|
// ErrWitnessProgramWrongLength is returned if ScriptVerifyWitness is
|
||||||
|
// set and the length of the witness program violates the length as
|
||||||
|
// dictated by the current witness version.
|
||||||
|
ErrWitnessProgramWrongLength
|
||||||
|
|
||||||
|
// ErrWitnessMalleated is returned if ScriptVerifyWitness is set and a
|
||||||
|
// native p2wsh program is encountered which has a non-empty sigScript.
|
||||||
|
ErrWitnessMalleated
|
||||||
|
|
||||||
|
// ErrWitnessMalleatedP2SH is returned if ScriptVerifyWitness if set
|
||||||
|
// and the validation logic for nested p2sh encounters a sigScript
|
||||||
|
// which isn't *exactyl* a datapush of the witness program.
|
||||||
|
ErrWitnessMalleatedP2SH
|
||||||
|
|
||||||
|
// ErrWitnessUnexpected is returned if ScriptVerifyWitness is set and a
|
||||||
|
// transaction includes witness data but doesn't spend an which is a
|
||||||
|
// witness program (nested or native).
|
||||||
|
ErrWitnessUnexpected
|
||||||
|
|
||||||
// numErrorCodes is the maximum error code number used in tests. This
|
// numErrorCodes is the maximum error code number used in tests. This
|
||||||
// entry MUST be the last entry in the enum.
|
// entry MUST be the last entry in the enum.
|
||||||
numErrorCodes
|
numErrorCodes
|
||||||
|
|
|
@ -2045,10 +2045,6 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
|
||||||
// Get script starting from the most recent OP_CODESEPARATOR.
|
// Get script starting from the most recent OP_CODESEPARATOR.
|
||||||
subScript := vm.subScript()
|
subScript := vm.subScript()
|
||||||
|
|
||||||
// Remove the signature since there is no way for a signature to sign
|
|
||||||
// itself.
|
|
||||||
subScript = removeOpcodeByData(subScript, fullSigBytes)
|
|
||||||
|
|
||||||
// Generate the signature hash based on the signature hash type.
|
// Generate the signature hash based on the signature hash type.
|
||||||
var hash []byte
|
var hash []byte
|
||||||
if vm.witness {
|
if vm.witness {
|
||||||
|
@ -2065,6 +2061,10 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Remove the signature since there is no way for a signature
|
||||||
|
// to sign itself.
|
||||||
|
subScript = removeOpcodeByData(subScript, fullSigBytes)
|
||||||
|
|
||||||
hash = calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx)
|
hash = calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2232,10 +2232,12 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
|
||||||
// Get script starting from the most recent OP_CODESEPARATOR.
|
// Get script starting from the most recent OP_CODESEPARATOR.
|
||||||
script := vm.subScript()
|
script := vm.subScript()
|
||||||
|
|
||||||
// Remove any of the signatures since there is no way for a signature to
|
// Remove the signature in pre-segwit scripts since there is no way for
|
||||||
// sign itself.
|
// a signature to sign itself.
|
||||||
for _, sigInfo := range signatures {
|
if !vm.witness {
|
||||||
script = removeOpcodeByData(script, sigInfo.signature)
|
for _, sigInfo := range signatures {
|
||||||
|
script = removeOpcodeByData(script, sigInfo.signature)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
success := true
|
success := true
|
||||||
|
|
|
@ -53,10 +53,10 @@ func mkGetScript(scripts map[string][]byte) ScriptDB {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error {
|
func checkScripts(msg string, tx *wire.MsgTx, idx int, inputAmt int64, sigScript, pkScript []byte) error {
|
||||||
tx.TxIn[idx].SignatureScript = sigScript
|
tx.TxIn[idx].SignatureScript = sigScript
|
||||||
vm, err := NewEngine(pkScript, tx, idx,
|
vm, err := NewEngine(pkScript, tx, idx,
|
||||||
ScriptBip16|ScriptVerifyDERSignatures, nil)
|
ScriptBip16|ScriptVerifyDERSignatures, nil, nil, inputAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to make script engine for %s: %v",
|
return fmt.Errorf("failed to make script engine for %s: %v",
|
||||||
msg, err)
|
msg, err)
|
||||||
|
@ -71,7 +71,7 @@ func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte,
|
func signAndCheck(msg string, tx *wire.MsgTx, idx int, inputAmt int64, pkScript []byte,
|
||||||
hashType SigHashType, kdb KeyDB, sdb ScriptDB,
|
hashType SigHashType, kdb KeyDB, sdb ScriptDB,
|
||||||
previousScript []byte) error {
|
previousScript []byte) error {
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte,
|
||||||
return fmt.Errorf("failed to sign output %s: %v", msg, err)
|
return fmt.Errorf("failed to sign output %s: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkScripts(msg, tx, idx, sigScript, pkScript)
|
return checkScripts(msg, tx, idx, inputAmt, sigScript, pkScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignTxOutput(t *testing.T) {
|
func TestSignTxOutput(t *testing.T) {
|
||||||
|
@ -99,6 +99,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
SigHashNone | SigHashAnyOneCanPay,
|
SigHashNone | SigHashAnyOneCanPay,
|
||||||
SigHashSingle | SigHashAnyOneCanPay,
|
SigHashSingle | SigHashAnyOneCanPay,
|
||||||
}
|
}
|
||||||
|
inputAmounts := []int64{5, 10, 15}
|
||||||
tx := &wire.MsgTx{
|
tx := &wire.MsgTx{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
TxIn: []*wire.TxIn{
|
TxIn: []*wire.TxIn{
|
||||||
|
@ -165,7 +166,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
"for %s: %v", msg, err)
|
"for %s: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, pkScript, hashType,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i], pkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, false},
|
address.EncodeAddress(): {key, false},
|
||||||
}), mkGetScript(nil), nil); err != nil {
|
}), mkGetScript(nil), nil); err != nil {
|
||||||
|
@ -226,7 +227,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, pkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i], sigScript, pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -263,7 +264,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
"for %s: %v", msg, err)
|
"for %s: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, pkScript, hashType,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
|
pkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, true},
|
address.EncodeAddress(): {key, true},
|
||||||
}), mkGetScript(nil), nil); err != nil {
|
}), mkGetScript(nil), nil); err != nil {
|
||||||
|
@ -325,7 +327,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, pkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -362,7 +365,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
"for %s: %v", msg, err)
|
"for %s: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, pkScript, hashType,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
|
pkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, false},
|
address.EncodeAddress(): {key, false},
|
||||||
}), mkGetScript(nil), nil); err != nil {
|
}), mkGetScript(nil), nil); err != nil {
|
||||||
|
@ -424,7 +428,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, pkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i], sigScript, pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -461,7 +465,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
"for %s: %v", msg, err)
|
"for %s: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, pkScript, hashType,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
|
pkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, true},
|
address.EncodeAddress(): {key, true},
|
||||||
}), mkGetScript(nil), nil); err != nil {
|
}), mkGetScript(nil), nil); err != nil {
|
||||||
|
@ -523,7 +528,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, pkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -577,8 +583,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, scriptPkScript,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
hashType,
|
scriptPkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, false},
|
address.EncodeAddress(): {key, false},
|
||||||
}), mkGetScript(map[string][]byte{
|
}), mkGetScript(map[string][]byte{
|
||||||
|
@ -662,7 +668,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, scriptPkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -715,8 +722,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, scriptPkScript,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
hashType,
|
scriptPkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, true},
|
address.EncodeAddress(): {key, true},
|
||||||
}), mkGetScript(map[string][]byte{
|
}), mkGetScript(map[string][]byte{
|
||||||
|
@ -800,7 +807,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, scriptPkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -853,8 +861,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, scriptPkScript,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
hashType,
|
scriptPkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, false},
|
address.EncodeAddress(): {key, false},
|
||||||
}), mkGetScript(map[string][]byte{
|
}), mkGetScript(map[string][]byte{
|
||||||
|
@ -937,7 +945,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, scriptPkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -989,8 +998,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, scriptPkScript,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
hashType,
|
scriptPkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address.EncodeAddress(): {key, true},
|
address.EncodeAddress(): {key, true},
|
||||||
}), mkGetScript(map[string][]byte{
|
}), mkGetScript(map[string][]byte{
|
||||||
|
@ -1073,7 +1082,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript, scriptPkScript)
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
|
sigScript, scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("twice signed script invalid for "+
|
t.Errorf("twice signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -1144,8 +1154,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signAndCheck(msg, tx, i, scriptPkScript,
|
if err := signAndCheck(msg, tx, i, inputAmounts[i],
|
||||||
hashType,
|
scriptPkScript, hashType,
|
||||||
mkGetKey(map[string]addressToKey{
|
mkGetKey(map[string]addressToKey{
|
||||||
address1.EncodeAddress(): {key1, true},
|
address1.EncodeAddress(): {key1, true},
|
||||||
address2.EncodeAddress(): {key2, true},
|
address2.EncodeAddress(): {key2, true},
|
||||||
|
@ -1234,7 +1244,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only 1 out of 2 signed, this *should* fail.
|
// Only 1 out of 2 signed, this *should* fail.
|
||||||
if checkScripts(msg, tx, i, sigScript,
|
if checkScripts(msg, tx, i, inputAmounts[i], sigScript,
|
||||||
scriptPkScript) == nil {
|
scriptPkScript) == nil {
|
||||||
t.Errorf("part signed script valid for %s", msg)
|
t.Errorf("part signed script valid for %s", msg)
|
||||||
break
|
break
|
||||||
|
@ -1253,7 +1263,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkScripts(msg, tx, i, sigScript,
|
err = checkScripts(msg, tx, i, inputAmounts[i], sigScript,
|
||||||
scriptPkScript)
|
scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("fully signed script invalid for "+
|
t.Errorf("fully signed script invalid for "+
|
||||||
|
@ -1340,7 +1350,7 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only 1 out of 2 signed, this *should* fail.
|
// Only 1 out of 2 signed, this *should* fail.
|
||||||
if checkScripts(msg, tx, i, sigScript,
|
if checkScripts(msg, tx, i, inputAmounts[i], sigScript,
|
||||||
scriptPkScript) == nil {
|
scriptPkScript) == nil {
|
||||||
t.Errorf("part signed script valid for %s", msg)
|
t.Errorf("part signed script valid for %s", msg)
|
||||||
break
|
break
|
||||||
|
@ -1361,8 +1371,8 @@ func TestSignTxOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we should pass.
|
// Now we should pass.
|
||||||
err = checkScripts(msg, tx, i, sigScript,
|
err = checkScripts(msg, tx, i, inputAmounts[i],
|
||||||
scriptPkScript)
|
sigScript, scriptPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("fully signed script invalid for "+
|
t.Errorf("fully signed script invalid for "+
|
||||||
"%s: %v", msg, err)
|
"%s: %v", msg, err)
|
||||||
|
@ -1635,7 +1645,7 @@ nexttest:
|
||||||
tx.AddTxOut(output)
|
tx.AddTxOut(output)
|
||||||
|
|
||||||
for range sigScriptTests[i].inputs {
|
for range sigScriptTests[i].inputs {
|
||||||
txin := wire.NewTxIn(coinbaseOutPoint, nil)
|
txin := wire.NewTxIn(coinbaseOutPoint, nil, nil)
|
||||||
tx.AddTxIn(txin)
|
tx.AddTxIn(txin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,8 +1693,8 @@ nexttest:
|
||||||
// Validate tx input scripts
|
// Validate tx input scripts
|
||||||
scriptFlags := ScriptBip16 | ScriptVerifyDERSignatures
|
scriptFlags := ScriptBip16 | ScriptVerifyDERSignatures
|
||||||
for j := range tx.TxIn {
|
for j := range tx.TxIn {
|
||||||
vm, err := NewEngine(sigScriptTests[i].inputs[j].txout.
|
vm, err := NewEngine(sigScriptTests[i].
|
||||||
PkScript, tx, j, scriptFlags, nil)
|
inputs[j].txout.PkScript, tx, j, scriptFlags, nil, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("cannot create script vm for test %v: %v",
|
t.Errorf("cannot create script vm for test %v: %v",
|
||||||
sigScriptTests[i].name, err)
|
sigScriptTests[i].name, err)
|
||||||
|
|
|
@ -37,7 +37,9 @@ const (
|
||||||
ScriptVerifyNullFail |
|
ScriptVerifyNullFail |
|
||||||
ScriptVerifyCheckLockTimeVerify |
|
ScriptVerifyCheckLockTimeVerify |
|
||||||
ScriptVerifyCheckSequenceVerify |
|
ScriptVerifyCheckSequenceVerify |
|
||||||
ScriptVerifyLowS
|
ScriptVerifyLowS |
|
||||||
|
ScriptVerifyWitness |
|
||||||
|
ScriptVerifyDiscourageUpgradeableWitnessProgram
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScriptClass is an enumeration for the list of standard types of script.
|
// ScriptClass is an enumeration for the list of standard types of script.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue