[DEV-98] Move script flags from relay rules to consensus (#49)

* [DEV-98] Move script flags from relay rules to consensus

* [DEV-98] remove flags from script_tests.json

* [DEV-98] fix multisig and remove test that assume no minimal data rule

* [DEV-98] rename bip16 bool to isP2sh

* [DEV-98] add sighash type to overly long signature in script_tests.json

* [DEV-98] add test for NUMEQUAL for non equal numbers script_tests.json

* [DEV-98] remove debugging if

* [DEV-98] remove ErrCleanStack from EVAL_FALSE

* [DEV-98] change isP2sh to isP2SH to comply with Go style

* [DEV-98] add ScriptNoFlags to explictly indicate for empty ScriptFlags

* [DEV-98] rename ErrPubKeyType -> ErrPubKeyFormat

* [DEV-98] rename PUBKEYTYPE -> PUBKEYFORMAT
This commit is contained in:
Ori Newman 2018-08-23 17:45:49 +03:00 committed by Svarog
parent c65d9aa168
commit b4b71eec01
20 changed files with 1087 additions and 1465 deletions

View File

@ -40,7 +40,7 @@ func TestCheckBlockScripts(t *testing.T) {
return return
} }
scriptFlags := txscript.ScriptBip16 scriptFlags := txscript.ScriptNoFlags
err = checkBlockScripts(blocks[0], view, scriptFlags, nil) err = checkBlockScripts(blocks[0], view, scriptFlags, nil)
if err != nil { if err != nil {
t.Errorf("Transaction script validation failed: %v\n", err) t.Errorf("Transaction script validation failed: %v\n", err)

View File

@ -955,12 +955,6 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block, view
return err return err
} }
// BIP0016 describes a pay-to-script-hash type that is considered a
// "standard" type. The rules for this BIP only apply to transactions
// after the timestamp defined by txscript.Bip16Activation. See
// https://en.bitcoin.it/wiki/BIP_0016 for more details.
enforceBIP0016 := node.timestamp >= txscript.Bip16Activation.Unix()
// The number of signature operations must be less than the maximum // The number of signature operations must be less than the maximum
// allowed per block. Note that the preliminary sanity checks on a // allowed per block. Note that the preliminary sanity checks on a
// block also include a check similar to this one, but this check // block also include a check similar to this one, but this check
@ -971,19 +965,17 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block, view
totalSigOps := 0 totalSigOps := 0
for i, tx := range transactions { for i, tx := range transactions {
numsigOps := CountSigOps(tx) numsigOps := CountSigOps(tx)
if enforceBIP0016 { // Since the first (and only the first) transaction has
// Since the first (and only the first) transaction has // already been verified to be a coinbase transaction,
// already been verified to be a coinbase transaction, // use i == 0 as an optimization for the flag to
// use i == 0 as an optimization for the flag to // countP2SHSigOps for whether or not the transaction is
// countP2SHSigOps for whether or not the transaction is // a coinbase transaction rather than having to do a
// a coinbase transaction rather than having to do a // full coinbase check again.
// full coinbase check again. numP2SHSigOps, err := CountP2SHSigOps(tx, i == 0, view)
numP2SHSigOps, err := CountP2SHSigOps(tx, i == 0, view) if err != nil {
if err != nil { return err
return err
}
numsigOps += numP2SHSigOps
} }
numsigOps += numP2SHSigOps
// Check for overflow or going over the limits. We have to do // Check for overflow or going over the limits. We have to do
// this on every loop iteration to avoid overflow. // this on every loop iteration to avoid overflow.
@ -1065,12 +1057,7 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block, view
runScripts = false runScripts = false
} }
// Blocks created after the BIP0016 activation time need to have the scriptFlags := txscript.ScriptNoFlags
// pay-to-script-hash checks enabled.
var scriptFlags txscript.ScriptFlags
if enforceBIP0016 {
scriptFlags |= txscript.ScriptBip16
}
// We obtain the MTP of the *previous* block in order to // We obtain the MTP of the *previous* block in order to
// determine if transactions in the current block are final. // determine if transactions in the current block are final.

View File

@ -10,8 +10,8 @@ import (
"github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/blockdag"
"github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/txscript"
"github.com/daglabs/btcd/wire"
"github.com/daglabs/btcd/util" "github.com/daglabs/btcd/util"
"github.com/daglabs/btcd/wire"
) )
const ( const (
@ -84,12 +84,7 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee util.Amount)
// to ensure they are "standard". A standard transaction input within the // to ensure they are "standard". A standard transaction input within the
// context of this function is one whose referenced public key script is of a // context of this function is one whose referenced public key script is of a
// standard form and, for pay-to-script-hash, does not have more than // standard form and, for pay-to-script-hash, does not have more than
// maxStandardP2SHSigOps signature operations. However, it should also be noted // maxStandardP2SHSigOps signature operations.
// that standard inputs also are those which have a clean stack after execution
// and only contain pushed data in their signature scripts. This function does
// not perform those checks because the script engine already does this more
// accurately and concisely via the txscript.ScriptVerifyCleanStack and
// txscript.ScriptVerifySigPushOnly flags.
func checkInputsStandard(tx *util.Tx, utxoView *blockdag.UtxoViewpoint) error { func checkInputsStandard(tx *util.Tx, utxoView *blockdag.UtxoViewpoint) error {
// NOTE: The reference implementation also does a coinbase check here, // NOTE: The reference implementation also does a coinbase check here,
// but coinbases have already been rejected prior to calling this // but coinbases have already been rejected prior to calling this

File diff suppressed because one or more lines are too long

View File

@ -17,9 +17,9 @@ import (
type ScriptFlags uint32 type ScriptFlags uint32
const ( const (
// ScriptBip16 defines whether the bip16 threshold has passed and thus
// pay-to-script hash transactions will be fully validated. // ScriptNoFlags is used when you want to use ScriptFlags without raising any flags
ScriptBip16 ScriptFlags = 1 << iota ScriptNoFlags ScriptFlags = 0
// ScriptDiscourageUpgradableNops defines whether to verify that // ScriptDiscourageUpgradableNops defines whether to verify that
// NOP1 through NOP10 are reserved for future soft-fork upgrades. This // NOP1 through NOP10 are reserved for future soft-fork upgrades. This
@ -27,40 +27,13 @@ const (
// blocks as this flag is only for stricter standard transaction // blocks as this flag is only for stricter standard transaction
// checks. This flag is only applied when the above opcodes are // checks. This flag is only applied when the above opcodes are
// executed. // executed.
ScriptDiscourageUpgradableNops ScriptDiscourageUpgradableNops ScriptFlags = 1 << iota
// ScriptVerifyCleanStack defines that the stack must contain only
// one stack element after evaluation and that the element must be
// true if interpreted as a boolean. This is rule 6 of BIP0062.
// This flag should never be used without the ScriptBip16 flag.
ScriptVerifyCleanStack
// ScriptVerifyLowS defines that signtures are required to comply with
// the DER format and whose S value is <= order / 2. This is rule 5
// of BIP0062.
ScriptVerifyLowS
// ScriptVerifyMinimalData defines that signatures must use the smallest
// push operator. This is both rules 3 and 4 of BIP0062.
ScriptVerifyMinimalData
// ScriptVerifyNullFail defines that signatures must be empty if
// a CHECKSIG or CHECKMULTISIG operation fails.
ScriptVerifyNullFail
// ScriptVerifySigPushOnly defines that signature scripts must contain
// only pushed data. This is rule 2 of BIP0062.
ScriptVerifySigPushOnly
// ScriptVerifyStrictEncoding defines that signature scripts and
// public keys must follow the strict encoding requirements.
ScriptVerifyStrictEncoding
) )
const ( const (
// MaxStackSize is the maximum combined height of stack and alt stack // MaxStackSize is the maximum combined height of stack and alt stack
// during execution. // during execution.
MaxStackSize = 1000 MaxStackSize = 244
// MaxScriptSize is the maximum allowed length of a raw script. // MaxScriptSize is the maximum allowed length of a raw script.
MaxScriptSize = 10000 MaxScriptSize = 10000
@ -82,8 +55,8 @@ type Engine struct {
numOps int numOps int
flags ScriptFlags flags ScriptFlags
sigCache *SigCache sigCache *SigCache
bip16 bool // treat execution as pay-to-script-hash isP2SH bool // treat execution as pay-to-script-hash
savedFirstStack [][]byte // stack from first script for bip16 scripts savedFirstStack [][]byte // stack from first script for ps2h scripts
} }
// hasFlag returns whether the script engine instance has the passed flag set. // hasFlag returns whether the script engine instance has the passed flag set.
@ -143,7 +116,7 @@ func (vm *Engine) executeOpcode(pop *parsedOpcode) error {
// Ensure all executed data push opcodes use the minimal encoding when // Ensure all executed data push opcodes use the minimal encoding when
// the minimal data verification flag is set. // the minimal data verification flag is set.
if vm.dstack.verifyMinimalData && vm.isBranchExecuting() && if vm.isBranchExecuting() &&
pop.opcode.value >= 0 && pop.opcode.value <= OpPushData4 { pop.opcode.value >= 0 && pop.opcode.value <= OpPushData4 {
if err := pop.checkMinimalDataPush(); err != nil { if err := pop.checkMinimalDataPush(); err != nil {
@ -228,15 +201,16 @@ func (vm *Engine) CheckErrorCondition(finalScript bool) error {
"error check when script unfinished") "error check when script unfinished")
} }
if finalScript && vm.hasFlag(ScriptVerifyCleanStack) && if finalScript {
vm.dstack.Depth() != 1 { if vm.dstack.Depth() > 1 {
str := fmt.Sprintf("stack contains %d unexpected items", str := fmt.Sprintf("stack contains %d unexpected items",
vm.dstack.Depth()-1) vm.dstack.Depth()-1)
return scriptError(ErrCleanStack, str) return scriptError(ErrCleanStack, str)
} else if vm.dstack.Depth() < 1 { } else if vm.dstack.Depth() < 1 {
return scriptError(ErrEmptyStack, return scriptError(ErrEmptyStack,
"stack empty at end of script execution") "stack empty at end of script execution")
}
} }
v, err := vm.dstack.PopBool() v, err := vm.dstack.PopBool()
@ -302,10 +276,10 @@ func (vm *Engine) Step() (done bool, err error) {
vm.numOps = 0 // number of ops is per script. vm.numOps = 0 // number of ops is per script.
vm.scriptOff = 0 vm.scriptOff = 0
if vm.scriptIdx == 0 && vm.bip16 { if vm.scriptIdx == 0 && vm.isP2SH {
vm.scriptIdx++ vm.scriptIdx++
vm.savedFirstStack = vm.GetStack() vm.savedFirstStack = vm.GetStack()
} else if vm.scriptIdx == 1 && vm.bip16 { } else if vm.scriptIdx == 1 && vm.isP2SH {
// Put us past the end for CheckErrorCondition() // Put us past the end for CheckErrorCondition()
vm.scriptIdx++ vm.scriptIdx++
// Check script ran successfully and pull the script // Check script ran successfully and pull the script
@ -382,10 +356,6 @@ func (vm *Engine) currentScript() []parsedOpcode {
// checkHashTypeEncoding returns whether or not the passed hashtype adheres to // checkHashTypeEncoding returns whether or not the passed hashtype adheres to
// the strict encoding requirements if enabled. // the strict encoding requirements if enabled.
func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error { func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error {
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
return nil
}
sigHashType := hashType & ^SigHashAnyOneCanPay sigHashType := hashType & ^SigHashAnyOneCanPay
if sigHashType < SigHashAll || sigHashType > SigHashSingle { if sigHashType < SigHashAll || sigHashType > SigHashSingle {
str := fmt.Sprintf("invalid hash type 0x%x", hashType) str := fmt.Sprintf("invalid hash type 0x%x", hashType)
@ -397,10 +367,6 @@ func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error {
// checkPubKeyEncoding returns whether or not the passed public key adheres to // checkPubKeyEncoding returns whether or not the passed public key adheres to
// the strict encoding requirements if enabled. // the strict encoding requirements if enabled.
func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error { func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
return nil
}
if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) { if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) {
// Compressed // Compressed
return nil return nil
@ -410,7 +376,7 @@ func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
return nil return nil
} }
return scriptError(ErrPubKeyType, "unsupported public key type") return scriptError(ErrPubKeyFormat, "unsupported public key type")
} }
// checkSignatureEncoding returns whether or not the passed signature adheres to // checkSignatureEncoding returns whether or not the passed signature adheres to
@ -542,13 +508,11 @@ func (vm *Engine) checkSignatureEncoding(sig []byte) error {
// valid transaction with the complement while still being a valid // valid transaction with the complement while still being a valid
// signature that verifies. This would result in changing the // signature that verifies. This would result in changing the
// transaction hash and thus is source of malleability. // transaction hash and thus is source of malleability.
if vm.hasFlag(ScriptVerifyLowS) { sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen])
sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen]) if sValue.Cmp(halfOrder) > 0 {
if sValue.Cmp(halfOrder) > 0 { return scriptError(ErrSigHighS,
return scriptError(ErrSigHighS, "signature is not canonical due to "+
"signature is not canonical due to "+ "unnecessarily high S value")
"unnecessarily high S value")
}
} }
return nil return nil
@ -622,64 +586,43 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
"false stack entry at end of script execution") "false stack entry at end of script execution")
} }
// The clean stack flag (ScriptVerifyCleanStack) is not allowed without
// the pay-to-script-hash (P2SH) evaluation (ScriptBip16) flag.
//
// Recall that evaluating a P2SH script without the flag set results in
// non-P2SH evaluation which leaves the P2SH inputs on the stack. Thus,
// allowing the clean stack flag without the P2SH flag would make it
// possible to have a situation where P2SH would not be a soft fork when
// it should be.
vm := Engine{flags: flags, sigCache: sigCache} vm := Engine{flags: flags, sigCache: sigCache}
if vm.hasFlag(ScriptVerifyCleanStack) && !vm.hasFlag(ScriptBip16) {
return nil, scriptError(ErrInvalidFlags,
"invalid flags combination")
}
// The signature script must only contain data pushes when the parsedScriptSig, err := parseScriptAndVerifySize(scriptSig)
// associated flag is set. if err != nil {
if vm.hasFlag(ScriptVerifySigPushOnly) && !IsPushOnlyScript(scriptSig) { return nil, err
}
// The signature script must only contain data pushes
if !isPushOnly(parsedScriptSig) {
return nil, scriptError(ErrNotPushOnly, return nil, scriptError(ErrNotPushOnly,
"signature script is not push only") "signature script is not push only")
} }
parsedScriptPubKey, err := parseScriptAndVerifySize(scriptPubKey)
if err != nil {
return nil, err
}
// The engine stores the scripts in parsed form using a slice. This // The engine stores the scripts in parsed form using a slice. This
// allows multiple scripts to be executed in sequence. For example, // allows multiple scripts to be executed in sequence. For example,
// with a pay-to-script-hash transaction, there will be ultimately be // with a pay-to-script-hash transaction, there will be ultimately be
// a third script to execute. // a third script to execute.
scripts := [][]byte{scriptSig, scriptPubKey} vm.scripts = [][]parsedOpcode{parsedScriptSig, parsedScriptPubKey}
vm.scripts = make([][]parsedOpcode, len(scripts))
for i, scr := range scripts {
if len(scr) > MaxScriptSize {
str := fmt.Sprintf("script size %d is larger than max "+
"allowed size %d", len(scr), MaxScriptSize)
return nil, scriptError(ErrScriptTooBig, str)
}
var err error
vm.scripts[i], err = parseScript(scr)
if err != nil {
return nil, err
}
}
// Advance the program counter to the public key script if the signature // Advance the program counter to the public key script if the signature
// script is empty since there is nothing to execute for it in that // script is empty since there is nothing to execute for it in that
// case. // case.
if len(scripts[0]) == 0 { if len(scriptSig) == 0 {
vm.scriptIdx++ vm.scriptIdx++
} }
if vm.hasFlag(ScriptBip16) && isScriptHash(vm.scripts[1]) { if isScriptHash(vm.scripts[1]) {
// Only accept input scripts that push data for P2SH. // Only accept input scripts that push data for P2SH.
if !isPushOnly(vm.scripts[0]) { if !isPushOnly(vm.scripts[0]) {
return nil, scriptError(ErrNotPushOnly, return nil, scriptError(ErrNotPushOnly,
"pay to script hash is not push only") "pay to script hash is not push only")
} }
vm.bip16 = true vm.isP2SH = true
}
if vm.hasFlag(ScriptVerifyMinimalData) {
vm.dstack.verifyMinimalData = true
vm.astack.verifyMinimalData = true
} }
vm.tx = *tx vm.tx = *tx
@ -687,3 +630,12 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
return &vm, nil return &vm, nil
} }
func parseScriptAndVerifySize(script []byte) ([]parsedOpcode, error) {
if len(script) > MaxScriptSize {
str := fmt.Sprintf("script size %d is larger than max "+
"allowed size %d", len(script), MaxScriptSize)
return nil, scriptError(ErrScriptTooBig, str)
}
return parseScript(script)
}

View File

@ -41,7 +41,7 @@ func TestBadPC(t *testing.T) {
}), }),
Index: 0, Index: 0,
}, },
SignatureScript: mustParseShortForm("NOP"), SignatureScript: mustParseShortForm(""),
Sequence: 4294967295, Sequence: 4294967295,
}, },
}, },
@ -145,56 +145,6 @@ func TestCheckErrorCondition(t *testing.T) {
} }
} }
// TestInvalidFlagCombinations ensures the script engine returns the expected
// error when disallowed flag combinations are specified.
func TestInvalidFlagCombinations(t *testing.T) {
t.Parallel()
tests := []ScriptFlags{
ScriptVerifyCleanStack,
}
// tx with almost empty scripts.
tx := &wire.MsgTx{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: daghash.Hash([32]byte{
0xc9, 0x97, 0xa5, 0xe5,
0x6e, 0x10, 0x41, 0x02,
0xfa, 0x20, 0x9c, 0x6a,
0x85, 0x2d, 0xd9, 0x06,
0x60, 0xa2, 0x0b, 0x2d,
0x9c, 0x35, 0x24, 0x23,
0xed, 0xce, 0x25, 0x85,
0x7f, 0xcd, 0x37, 0x04,
}),
Index: 0,
},
SignatureScript: []uint8{OpNop},
Sequence: 4294967295,
},
},
TxOut: []*wire.TxOut{
{
Value: 1000000000,
PkScript: nil,
},
},
LockTime: 0,
}
pkScript := []byte{OpNop}
for i, test := range tests {
_, err := NewEngine(pkScript, tx, 0, test, nil)
if !IsErrorCode(err, ErrInvalidFlags) {
t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+
"error: %v", i, err)
}
}
}
// TestCheckPubKeyEncoding ensures the internal checkPubKeyEncoding function // TestCheckPubKeyEncoding ensures the internal checkPubKeyEncoding function
// works as expected. // works as expected.
func TestCheckPubKeyEncoding(t *testing.T) { func TestCheckPubKeyEncoding(t *testing.T) {
@ -240,7 +190,7 @@ func TestCheckPubKeyEncoding(t *testing.T) {
}, },
} }
vm := Engine{flags: ScriptVerifyStrictEncoding} vm := Engine{}
for _, test := range tests { for _, test := range tests {
err := vm.checkPubKeyEncoding(test.key) err := vm.checkPubKeyEncoding(test.key)
if err != nil && test.isValid { if err != nil && test.isValid {
@ -412,7 +362,7 @@ func TestCheckSignatureEncoding(t *testing.T) {
}, },
} }
vm := Engine{flags: ScriptVerifyStrictEncoding} vm := Engine{}
for _, test := range tests { for _, test := range tests {
err := vm.checkSignatureEncoding(test.sig) err := vm.checkSignatureEncoding(test.sig)
if err != nil && test.isValid { if err != nil && test.isValid {

View File

@ -166,9 +166,8 @@ const (
// Failures related to malleability. // Failures related to malleability.
// --------------------------------- // ---------------------------------
// ErrMinimalData is returned when the ScriptVerifyMinimalData flag // ErrMinimalData is returned when the script contains
// is set and the script contains push operations that do not use // push operations that do not use the minimal opcode required.
// the minimal opcode required.
ErrMinimalData ErrMinimalData
// ErrInvalidSigHashType is returned when a signature hash type is not // ErrInvalidSigHashType is returned when a signature hash type is not
@ -189,23 +188,20 @@ const (
ErrSigHighS ErrSigHighS
// ErrNotPushOnly is returned when a script that is required to only // ErrNotPushOnly is returned when a script that is required to only
// push data to the stack performs other operations. A couple of cases // push data to the stack performs other operations.
// where this applies is for a pay-to-script-hash signature script when
// bip16 is active and when the ScriptVerifySigPushOnly flag is set.
ErrNotPushOnly ErrNotPushOnly
// ErrPubKeyType is returned when the ScriptVerifyStrictEncoding // ErrPubKeyFormat is returned when the script contains invalid public keys.
// flag is set and the script contains invalid public keys. // A valid pubkey should be in uncompressed format as a 64 byte string prefixed with 0x04,
ErrPubKeyType // or to be in compressed format as a 32 byte string prefixed with 0x02 or 0x03 to signal oddness.
ErrPubKeyFormat
// ErrCleanStack is returned when the ScriptVerifyCleanStack flag // ErrCleanStack is returned when after evalution, the stack
// is set, and after evalution, the stack does not contain only a // contains more than one element.
// single element.
ErrCleanStack ErrCleanStack
// ErrNullFail is returned when the ScriptVerifyNullFail flag is // ErrNullFail is returned when signatures are not empty
// set and signatures are not empty on failed checksig or checkmultisig // on failed checksig or checkmultisig operations.
// operations.
ErrNullFail ErrNullFail
// ------------------------------- // -------------------------------
@ -268,7 +264,7 @@ var errorCodeStrings = map[ErrorCode]string{
ErrSigDER: "ErrSigDER", ErrSigDER: "ErrSigDER",
ErrSigHighS: "ErrSigHighS", ErrSigHighS: "ErrSigHighS",
ErrNotPushOnly: "ErrNotPushOnly", ErrNotPushOnly: "ErrNotPushOnly",
ErrPubKeyType: "ErrPubKeyType", ErrPubKeyFormat: "ErrPubKeyFormat",
ErrCleanStack: "ErrCleanStack", ErrCleanStack: "ErrCleanStack",
ErrNullFail: "ErrNullFail", ErrNullFail: "ErrNullFail",
ErrDiscourageUpgradableNOPs: "ErrDiscourageUpgradableNOPs", ErrDiscourageUpgradableNOPs: "ErrDiscourageUpgradableNOPs",

View File

@ -51,7 +51,7 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrSigDER, "ErrSigDER"}, {ErrSigDER, "ErrSigDER"},
{ErrSigHighS, "ErrSigHighS"}, {ErrSigHighS, "ErrSigHighS"},
{ErrNotPushOnly, "ErrNotPushOnly"}, {ErrNotPushOnly, "ErrNotPushOnly"},
{ErrPubKeyType, "ErrPubKeyType"}, {ErrPubKeyFormat, "ErrPubKeyFormat"},
{ErrCleanStack, "ErrCleanStack"}, {ErrCleanStack, "ErrCleanStack"},
{ErrNullFail, "ErrNullFail"}, {ErrNullFail, "ErrNullFail"},
{ErrDiscourageUpgradableNOPs, "ErrDiscourageUpgradableNOPs"}, {ErrDiscourageUpgradableNOPs, "ErrDiscourageUpgradableNOPs"},

View File

@ -162,8 +162,7 @@ func ExampleSignTxOutput() {
// Prove that the transaction has been validly signed by executing the // Prove that the transaction has been validly signed by executing the
// script pair. // script pair.
flags := txscript.ScriptBip16 | flags := txscript.ScriptDiscourageUpgradableNops
txscript.ScriptDiscourageUpgradableNops
vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, nil) vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -1101,7 +1101,7 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error {
if err != nil { if err != nil {
return err return err
} }
lockTime, err := makeScriptNum(so, vm.dstack.verifyMinimalData, 5) lockTime, err := makeScriptNum(so, 5)
if err != nil { if err != nil {
return err return err
} }
@ -1164,7 +1164,7 @@ func opcodeCheckSequenceVerify(op *parsedOpcode, vm *Engine) error {
if err != nil { if err != nil {
return err return err
} }
stackSequence, err := makeScriptNum(so, vm.dstack.verifyMinimalData, 5) stackSequence, err := makeScriptNum(so, 5)
if err != nil { if err != nil {
return err return err
} }
@ -2043,7 +2043,7 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
valid = signature.Verify(hash, pubKey) valid = signature.Verify(hash, pubKey)
} }
if !valid && vm.hasFlag(ScriptVerifyNullFail) && len(sigBytes) > 0 { if !valid && len(sigBytes) > 0 {
str := "signature not empty on failed checksig" str := "signature not empty on failed checksig"
return scriptError(ErrNullFail, str) return scriptError(ErrNullFail, str)
} }
@ -2248,7 +2248,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
} }
} }
if !success && vm.hasFlag(ScriptVerifyNullFail) { if !success {
for _, sig := range signatures { for _, sig := range signatures {
if len(sig.signature) > 0 { if len(sig.signature) > 0 {
str := "not all signatures empty on failed checkmultisig" str := "not all signatures empty on failed checkmultisig"

View File

@ -133,24 +133,8 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) {
switch flag { switch flag {
case "": case "":
// Nothing. // Nothing.
case "CLEANSTACK":
flags |= ScriptVerifyCleanStack
case "DISCOURAGE_UPGRADABLE_NOPS": case "DISCOURAGE_UPGRADABLE_NOPS":
flags |= ScriptDiscourageUpgradableNops flags |= ScriptDiscourageUpgradableNops
case "LOW_S":
flags |= ScriptVerifyLowS
case "MINIMALDATA":
flags |= ScriptVerifyMinimalData
case "NONE":
// Nothing.
case "NULLFAIL":
flags |= ScriptVerifyNullFail
case "P2SH":
flags |= ScriptBip16
case "SIGPUSHONLY":
flags |= ScriptVerifySigPushOnly
case "STRICTENC":
flags |= ScriptVerifyStrictEncoding
default: default:
return flags, fmt.Errorf("invalid flag: %s", flag) return flags, fmt.Errorf("invalid flag: %s", flag)
} }
@ -167,12 +151,14 @@ func parseExpectedResult(expected string) ([]ErrorCode, error) {
return nil, nil return nil, nil
case "UNKNOWN_ERROR": case "UNKNOWN_ERROR":
return []ErrorCode{ErrNumberTooBig, ErrMinimalData}, nil return []ErrorCode{ErrNumberTooBig, ErrMinimalData}, nil
case "PUBKEYTYPE": case "PUBKEYFORMAT":
return []ErrorCode{ErrPubKeyType}, nil return []ErrorCode{ErrPubKeyFormat}, nil
case "SIG_DER": case "SIG_DER":
return []ErrorCode{ErrSigDER, ErrInvalidSigHashType}, nil return []ErrorCode{ErrSigDER, ErrInvalidSigHashType}, nil
case "EVAL_FALSE": case "EVAL_FALSE":
return []ErrorCode{ErrEvalFalse, ErrEmptyStack}, nil return []ErrorCode{ErrEvalFalse, ErrEmptyStack}, nil
case "EMPTY_STACK":
return []ErrorCode{ErrEmptyStack}, nil
case "EQUALVERIFY": case "EQUALVERIFY":
return []ErrorCode{ErrEqualVerify}, nil return []ErrorCode{ErrEqualVerify}, nil
case "NULLFAIL": case "NULLFAIL":

View File

@ -8,17 +8,11 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"time"
"github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/dagconfig/daghash"
"github.com/daglabs/btcd/wire" "github.com/daglabs/btcd/wire"
) )
// Bip16Activation is the timestamp where BIP0016 is valid to use in the
// blockchain. To be used to determine if BIP0016 should be called for or not.
// This timestamp corresponds to Sun Apr 1 00:00:00 UTC 2012.
var Bip16Activation = time.Unix(1333238400, 0)
// SigHashType represents hash type bits at the end of a signature. // SigHashType represents hash type bits at the end of a signature.
type SigHashType uint32 type SigHashType uint32
@ -424,17 +418,17 @@ func GetSigOpCount(script []byte) int {
} }
// GetPreciseSigOpCount returns the number of signature operations in // GetPreciseSigOpCount returns the number of signature operations in
// scriptPubKey. If bip16 is true then scriptSig may be searched for the // scriptPubKey. If p2sh is true then scriptSig may be searched for the
// Pay-To-Script-Hash script in order to find the precise number of signature // Pay-To-Script-Hash script in order to find the precise number of signature
// operations in the transaction. If the script fails to parse, then the count // operations in the transaction. If the script fails to parse, then the count
// up to the point of failure is returned. // up to the point of failure is returned.
func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, bip16 bool) int { func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, isP2SH bool) int {
// Don't check error since parseScript returns the parsed-up-to-error // Don't check error since parseScript returns the parsed-up-to-error
// list of pops. // list of pops.
pops, _ := parseScript(scriptPubKey) pops, _ := parseScript(scriptPubKey)
// Treat non P2SH transactions as normal. // Treat non P2SH transactions as normal.
if !(bip16 && isScriptHash(pops)) { if !(isP2SH && isScriptHash(pops)) {
return getSigOpCount(pops, true) return getSigOpCount(pops, true)
} }

View File

@ -47,6 +47,11 @@ type scriptNum int64
// checkMinimalDataEncoding returns whether or not the passed byte array adheres // checkMinimalDataEncoding returns whether or not the passed byte array adheres
// to the minimal encoding requirements. // to the minimal encoding requirements.
// An error will be returned if it will determined that
// the encoding is not represented with the smallest possible
// number of bytes or is the negative 0 encoding, [0x80]. For example, consider
// the number 127. It could be encoded as [0x7f], [0x7f 0x00],
// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error
func checkMinimalDataEncoding(v []byte) error { func checkMinimalDataEncoding(v []byte) error {
if len(v) == 0 { if len(v) == 0 {
return nil return nil
@ -167,13 +172,6 @@ func (n scriptNum) Int32() int32 {
// bytes and therefore will pass that value to this function resulting in an // bytes and therefore will pass that value to this function resulting in an
// allowed range of [-2^31 + 1, 2^31 - 1]. // allowed range of [-2^31 + 1, 2^31 - 1].
// //
// The requireMinimal flag causes an error to be returned if additional checks
// on the encoding determine it is not represented with the smallest possible
// number of bytes or is the negative 0 encoding, [0x80]. For example, consider
// the number 127. It could be encoded as [0x7f], [0x7f 0x00],
// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error with
// requireMinimal enabled.
//
// The scriptNumLen is the maximum number of bytes the encoded value can be // The scriptNumLen is the maximum number of bytes the encoded value can be
// before an ErrStackNumberTooBig is returned. This effectively limits the // before an ErrStackNumberTooBig is returned. This effectively limits the
// range of allowed values. // range of allowed values.
@ -182,7 +180,7 @@ func (n scriptNum) Int32() int32 {
// overflows. // overflows.
// //
// See the Bytes function documentation for example encodings. // See the Bytes function documentation for example encodings.
func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum, error) { func makeScriptNum(v []byte, scriptNumLen int) (scriptNum, error) {
// Interpreting data requires that it is not larger than // Interpreting data requires that it is not larger than
// the the passed scriptNumLen value. // the the passed scriptNumLen value.
if len(v) > scriptNumLen { if len(v) > scriptNumLen {
@ -192,11 +190,8 @@ func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum,
return 0, scriptError(ErrNumberTooBig, str) return 0, scriptError(ErrNumberTooBig, str)
} }
// Enforce minimal encoded if requested. if err := checkMinimalDataEncoding(v); err != nil {
if requireMinimal { return 0, err
if err := checkMinimalDataEncoding(v); err != nil {
return 0, err
}
} }
// Zero is encoded as an empty byte slice. // Zero is encoded as an empty byte slice.

View File

@ -97,105 +97,88 @@ func TestMakeScriptNum(t *testing.T) {
errMinimalData := scriptError(ErrMinimalData, "") errMinimalData := scriptError(ErrMinimalData, "")
tests := []struct { tests := []struct {
serialized []byte serialized []byte
num scriptNum num scriptNum
numLen int numLen int
minimalEncoding bool err error
err error
}{ }{
// Minimal encoding must reject negative 0. // Minimal encoding must reject negative 0.
{hexToBytes("80"), 0, defaultScriptNumLen, true, errMinimalData}, {hexToBytes("80"), 0, defaultScriptNumLen, errMinimalData},
// Minimally encoded valid values with minimal encoding flag. // Minimally encoded valid values with minimal encoding flag.
// Should not error and return expected integral number. // Should not error and return expected integral number.
{nil, 0, defaultScriptNumLen, true, nil}, {nil, 0, defaultScriptNumLen, nil},
{hexToBytes("01"), 1, defaultScriptNumLen, true, nil}, {hexToBytes("01"), 1, defaultScriptNumLen, nil},
{hexToBytes("81"), -1, defaultScriptNumLen, true, nil}, {hexToBytes("81"), -1, defaultScriptNumLen, nil},
{hexToBytes("7f"), 127, defaultScriptNumLen, true, nil}, {hexToBytes("7f"), 127, defaultScriptNumLen, nil},
{hexToBytes("ff"), -127, defaultScriptNumLen, true, nil}, {hexToBytes("ff"), -127, defaultScriptNumLen, nil},
{hexToBytes("8000"), 128, defaultScriptNumLen, true, nil}, {hexToBytes("8000"), 128, defaultScriptNumLen, nil},
{hexToBytes("8080"), -128, defaultScriptNumLen, true, nil}, {hexToBytes("8080"), -128, defaultScriptNumLen, nil},
{hexToBytes("8100"), 129, defaultScriptNumLen, true, nil}, {hexToBytes("8100"), 129, defaultScriptNumLen, nil},
{hexToBytes("8180"), -129, defaultScriptNumLen, true, nil}, {hexToBytes("8180"), -129, defaultScriptNumLen, nil},
{hexToBytes("0001"), 256, defaultScriptNumLen, true, nil}, {hexToBytes("0001"), 256, defaultScriptNumLen, nil},
{hexToBytes("0081"), -256, defaultScriptNumLen, true, nil}, {hexToBytes("0081"), -256, defaultScriptNumLen, nil},
{hexToBytes("ff7f"), 32767, defaultScriptNumLen, true, nil}, {hexToBytes("ff7f"), 32767, defaultScriptNumLen, nil},
{hexToBytes("ffff"), -32767, defaultScriptNumLen, true, nil}, {hexToBytes("ffff"), -32767, defaultScriptNumLen, nil},
{hexToBytes("008000"), 32768, defaultScriptNumLen, true, nil}, {hexToBytes("008000"), 32768, defaultScriptNumLen, nil},
{hexToBytes("008080"), -32768, defaultScriptNumLen, true, nil}, {hexToBytes("008080"), -32768, defaultScriptNumLen, nil},
{hexToBytes("ffff00"), 65535, defaultScriptNumLen, true, nil}, {hexToBytes("ffff00"), 65535, defaultScriptNumLen, nil},
{hexToBytes("ffff80"), -65535, defaultScriptNumLen, true, nil}, {hexToBytes("ffff80"), -65535, defaultScriptNumLen, nil},
{hexToBytes("000008"), 524288, defaultScriptNumLen, true, nil}, {hexToBytes("000008"), 524288, defaultScriptNumLen, nil},
{hexToBytes("000088"), -524288, defaultScriptNumLen, true, nil}, {hexToBytes("000088"), -524288, defaultScriptNumLen, nil},
{hexToBytes("000070"), 7340032, defaultScriptNumLen, true, nil}, {hexToBytes("000070"), 7340032, defaultScriptNumLen, nil},
{hexToBytes("0000f0"), -7340032, defaultScriptNumLen, true, nil}, {hexToBytes("0000f0"), -7340032, defaultScriptNumLen, nil},
{hexToBytes("00008000"), 8388608, defaultScriptNumLen, true, nil}, {hexToBytes("00008000"), 8388608, defaultScriptNumLen, nil},
{hexToBytes("00008080"), -8388608, defaultScriptNumLen, true, nil}, {hexToBytes("00008080"), -8388608, defaultScriptNumLen, nil},
{hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, true, nil}, {hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, nil},
{hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, true, nil}, {hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, nil},
{hexToBytes("ffffffff7f"), 549755813887, 5, true, nil}, {hexToBytes("ffffffff7f"), 549755813887, 5, nil},
{hexToBytes("ffffffffff"), -549755813887, 5, true, nil}, {hexToBytes("ffffffffff"), -549755813887, 5, nil},
{hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, true, nil}, {hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, nil},
{hexToBytes("ffffffffffffffff"), -9223372036854775807, 8, true, nil}, {hexToBytes("ffffffffffffffff"), -9223372036854775807, 8, nil},
{hexToBytes("ffffffffffffffff7f"), -1, 9, true, nil}, {hexToBytes("ffffffffffffffff7f"), -1, 9, nil},
{hexToBytes("ffffffffffffffffff"), 1, 9, true, nil}, {hexToBytes("ffffffffffffffffff"), 1, 9, nil},
{hexToBytes("ffffffffffffffffff7f"), -1, 10, true, nil}, {hexToBytes("ffffffffffffffffff7f"), -1, 10, nil},
{hexToBytes("ffffffffffffffffffff"), 1, 10, true, nil}, {hexToBytes("ffffffffffffffffffff"), 1, 10, nil},
// Minimally encoded values that are out of range for data that // Minimally encoded values that are out of range for data that
// is interpreted as script numbers with the minimal encoding // is interpreted as script numbers with the minimal encoding
// flag set. Should error and return 0. // flag set. Should error and return 0.
{hexToBytes("0000008000"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000008000"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("0000008080"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000008080"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("0000009000"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000009000"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("0000009080"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000009080"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffff00"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffff80"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("0000000001"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000000001"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("0000000081"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("0000000081"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, errNumTooBig},
{hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, true, errNumTooBig}, {hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, errNumTooBig},
// Non-minimally encoded, but otherwise valid values with // Non-minimally encoded, but otherwise valid values with
// minimal encoding flag. Should error and return 0. // minimal encoding flag. Should error and return 0.
{hexToBytes("00"), 0, defaultScriptNumLen, true, errMinimalData}, // 0 {hexToBytes("00"), 0, defaultScriptNumLen, errMinimalData}, // 0
{hexToBytes("0100"), 0, defaultScriptNumLen, true, errMinimalData}, // 1 {hexToBytes("0100"), 0, defaultScriptNumLen, errMinimalData}, // 1
{hexToBytes("7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 127 {hexToBytes("7f00"), 0, defaultScriptNumLen, errMinimalData}, // 127
{hexToBytes("800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 128 {hexToBytes("800000"), 0, defaultScriptNumLen, errMinimalData}, // 128
{hexToBytes("810000"), 0, defaultScriptNumLen, true, errMinimalData}, // 129 {hexToBytes("810000"), 0, defaultScriptNumLen, errMinimalData}, // 129
{hexToBytes("000100"), 0, defaultScriptNumLen, true, errMinimalData}, // 256 {hexToBytes("000100"), 0, defaultScriptNumLen, errMinimalData}, // 256
{hexToBytes("ff7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 32767 {hexToBytes("ff7f00"), 0, defaultScriptNumLen, errMinimalData}, // 32767
{hexToBytes("00800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 32768 {hexToBytes("00800000"), 0, defaultScriptNumLen, errMinimalData}, // 32768
{hexToBytes("ffff0000"), 0, defaultScriptNumLen, true, errMinimalData}, // 65535 {hexToBytes("ffff0000"), 0, defaultScriptNumLen, errMinimalData}, // 65535
{hexToBytes("00000800"), 0, defaultScriptNumLen, true, errMinimalData}, // 524288 {hexToBytes("00000800"), 0, defaultScriptNumLen, errMinimalData}, // 524288
{hexToBytes("00007000"), 0, defaultScriptNumLen, true, errMinimalData}, // 7340032 {hexToBytes("00007000"), 0, defaultScriptNumLen, errMinimalData}, // 7340032
{hexToBytes("0009000100"), 0, 5, true, errMinimalData}, // 16779520 {hexToBytes("0009000100"), 0, 5, errMinimalData}, // 16779520
// Non-minimally encoded, but otherwise valid values without
// minimal encoding flag. Should not error and return expected
// integral number.
{hexToBytes("00"), 0, defaultScriptNumLen, false, nil},
{hexToBytes("0100"), 1, defaultScriptNumLen, false, nil},
{hexToBytes("7f00"), 127, defaultScriptNumLen, false, nil},
{hexToBytes("800000"), 128, defaultScriptNumLen, false, nil},
{hexToBytes("810000"), 129, defaultScriptNumLen, false, nil},
{hexToBytes("000100"), 256, defaultScriptNumLen, false, nil},
{hexToBytes("ff7f00"), 32767, defaultScriptNumLen, false, nil},
{hexToBytes("00800000"), 32768, defaultScriptNumLen, false, nil},
{hexToBytes("ffff0000"), 65535, defaultScriptNumLen, false, nil},
{hexToBytes("00000800"), 524288, defaultScriptNumLen, false, nil},
{hexToBytes("00007000"), 7340032, defaultScriptNumLen, false, nil},
{hexToBytes("0009000100"), 16779520, 5, false, nil},
} }
for _, test := range tests { for _, test := range tests {
// Ensure the error code is of the expected type and the error // Ensure the error code is of the expected type and the error
// code matches the value specified in the test instance. // code matches the value specified in the test instance.
gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding, gotNum, err := makeScriptNum(test.serialized,
test.numLen) test.numLen)
if e := tstCheckScriptError(err, test.err); e != nil { if e := tstCheckScriptError(err, test.err); e != nil {
t.Errorf("makeScriptNum(%#x): %v", test.serialized, e) t.Errorf("makeScriptNum(%#x): %v", test.serialized, e)

View File

@ -314,9 +314,7 @@ sigLoop:
} }
} }
// Extra opcode to handle the extra arg consumed (due to previous bugs builder := NewScriptBuilder()
// in the reference implementation).
builder := NewScriptBuilder().AddOp(OpFalse)
doneSigs := 0 doneSigs := 0
// This assumes that addresses are in the same order as in the script. // This assumes that addresses are in the same order as in the script.
for _, addr := range addresses { for _, addr := range addresses {

View File

@ -55,8 +55,9 @@ 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, sigScript, pkScript []byte) error {
tx.TxIn[idx].SignatureScript = sigScript tx.TxIn[idx].SignatureScript = sigScript
var flags ScriptFlags
vm, err := NewEngine(pkScript, tx, idx, vm, err := NewEngine(pkScript, tx, idx,
ScriptBip16, nil) flags, nil)
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)
@ -91,7 +92,6 @@ func TestSignTxOutput(t *testing.T) {
// make script based on key. // make script based on key.
// sign with magic pixie dust. // sign with magic pixie dust.
hashTypes := []SigHashType{ hashTypes := []SigHashType{
SigHashOld, // no longer used but should act like all
SigHashAll, SigHashAll,
SigHashNone, SigHashNone,
SigHashSingle, SigHashSingle,
@ -1498,7 +1498,7 @@ var sigScriptTests = []tstSigScript{
scriptAtWrongIndex: false, scriptAtWrongIndex: false,
}, },
{ {
name: "hashType SigHashAnyoneCanPay", name: "hashType SigHashAll | SigHashAnyoneCanPay",
inputs: []tstInput{ inputs: []tstInput{
{ {
txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript),
@ -1507,17 +1507,31 @@ var sigScriptTests = []tstSigScript{
indexOutOfRange: false, indexOutOfRange: false,
}, },
}, },
hashType: SigHashAll | SigHashAnyOneCanPay,
compress: false,
scriptAtWrongIndex: false,
},
{
name: "hashType SigHashAnyoneCanPay",
inputs: []tstInput{
{
txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: false,
indexOutOfRange: false,
},
},
hashType: SigHashAnyOneCanPay, hashType: SigHashAnyOneCanPay,
compress: false, compress: false,
scriptAtWrongIndex: false, scriptAtWrongIndex: false,
}, },
{ {
name: "hashType non-standard", name: "hashType non-exist",
inputs: []tstInput{ inputs: []tstInput{
{ {
txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true, sigscriptGenerates: true,
inputValidates: true, inputValidates: false,
indexOutOfRange: false, indexOutOfRange: false,
}, },
}, },
@ -1658,7 +1672,7 @@ nexttest:
} }
// Validate tx input scripts // Validate tx input scripts
scriptFlags := ScriptBip16 var scriptFlags ScriptFlags
for j := range tx.TxIn { for j := range tx.TxIn {
vm, err := NewEngine(sigScriptTests[i]. vm, err := NewEngine(sigScriptTests[i].
inputs[j].txout.PkScript, tx, j, scriptFlags, nil) inputs[j].txout.PkScript, tx, j, scriptFlags, nil)

View File

@ -36,8 +36,7 @@ func fromBool(v bool) []byte {
// changed it *must* be deep-copied first to avoid changing other values on the // changed it *must* be deep-copied first to avoid changing other values on the
// stack. // stack.
type stack struct { type stack struct {
stk [][]byte stk [][]byte
verifyMinimalData bool
} }
// Depth returns the number of items on the stack. // Depth returns the number of items on the stack.
@ -86,7 +85,7 @@ func (s *stack) PopInt() (scriptNum, error) {
return 0, err return 0, err
} }
return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) return makeScriptNum(so, defaultScriptNumLen)
} }
// PopBool pops the value off the top of the stack, converts it into a bool, and // PopBool pops the value off the top of the stack, converts it into a bool, and
@ -123,7 +122,7 @@ func (s *stack) PeekInt(idx int32) (scriptNum, error) {
return 0, err return 0, err
} }
return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) return makeScriptNum(so, defaultScriptNumLen)
} }
// PeekBool returns the Nth item on the stack as a bool without removing it. // PeekBool returns the Nth item on the stack as a bool without removing it.

View File

@ -186,7 +186,7 @@ func TestStack(t *testing.T) {
}, },
{ {
"popInt 0", "popInt 0",
[][]byte{{0x0}}, [][]byte{{}},
func(s *stack) error { func(s *stack) error {
v, err := s.PopInt() v, err := s.PopInt()
if err != nil { if err != nil {
@ -200,22 +200,6 @@ func TestStack(t *testing.T) {
nil, nil,
nil, nil,
}, },
{
"popInt -0",
[][]byte{{0x80}},
func(s *stack) error {
v, err := s.PopInt()
if err != nil {
return err
}
if v != 0 {
return errors.New("-0 != 0 on popInt")
}
return nil
},
nil,
nil,
},
{ {
"popInt 1", "popInt 1",
[][]byte{{0x01}}, [][]byte{{0x01}},
@ -232,23 +216,6 @@ func TestStack(t *testing.T) {
nil, nil,
nil, nil,
}, },
{
"popInt 1 leading 0",
[][]byte{{0x01, 0x00, 0x00, 0x00}},
func(s *stack) error {
v, err := s.PopInt()
if err != nil {
return err
}
if v != 1 {
fmt.Printf("%v != %v\n", v, 1)
return errors.New("1 != 1 on popInt")
}
return nil
},
nil,
nil,
},
{ {
"popInt -1", "popInt -1",
[][]byte{{0x81}}, [][]byte{{0x81}},
@ -265,23 +232,6 @@ func TestStack(t *testing.T) {
nil, nil,
nil, nil,
}, },
{
"popInt -1 leading 0",
[][]byte{{0x01, 0x00, 0x00, 0x80}},
func(s *stack) error {
v, err := s.PopInt()
if err != nil {
return err
}
if v != -1 {
fmt.Printf("%v != %v\n", v, -1)
return errors.New("-1 != -1 on popInt")
}
return nil
},
nil,
nil,
},
// Triggers the multibyte case in asInt // Triggers the multibyte case in asInt
{ {
"popInt -513", "popInt -513",
@ -300,24 +250,6 @@ func TestStack(t *testing.T) {
nil, nil,
nil, nil,
}, },
// Confirm that the asInt code doesn't modify the base data.
{
"peekint nomodify -1",
[][]byte{{0x01, 0x00, 0x00, 0x80}},
func(s *stack) error {
v, err := s.PeekInt(0)
if err != nil {
return err
}
if v != -1 {
fmt.Printf("%v != %v\n", v, -1)
return errors.New("-1 != -1 on popInt")
}
return nil
},
nil,
[][]byte{{0x01, 0x00, 0x00, 0x80}},
},
{ {
"PushInt 0", "PushInt 0",
nil, nil,
@ -852,24 +784,6 @@ func TestStack(t *testing.T) {
nil, nil,
[][]byte{{1}}, [][]byte{{1}},
}, },
{
"Peek int 2",
[][]byte{{0}},
func(s *stack) error {
// Peek int is otherwise pretty well tested,
// just check it works.
val, err := s.PeekInt(0)
if err != nil {
return err
}
if val != 0 {
return errors.New("invalid result")
}
return nil
},
nil,
[][]byte{{0}},
},
{ {
"pop int", "pop int",
nil, nil,

View File

@ -26,13 +26,7 @@ const (
// //
// TODO: This definition does not belong here. It belongs in a policy // TODO: This definition does not belong here. It belongs in a policy
// package. // package.
StandardVerifyFlags = ScriptBip16 | StandardVerifyFlags = ScriptDiscourageUpgradableNops
ScriptVerifyStrictEncoding |
ScriptVerifyMinimalData |
ScriptDiscourageUpgradableNops |
ScriptVerifyCleanStack |
ScriptVerifyNullFail |
ScriptVerifyLowS
) )
// ScriptClass is an enumeration for the list of standard types of script. // ScriptClass is an enumeration for the list of standard types of script.
@ -227,7 +221,7 @@ type ScriptInfo struct {
// pair. It will error if the pair is in someway invalid such that they can not // pair. It will error if the pair is in someway invalid such that they can not
// be analysed, i.e. if they do not parse or the pkScript is not a push-only // be analysed, i.e. if they do not parse or the pkScript is not a push-only
// script // script
func CalcScriptInfo(sigScript, pkScript []byte, bip16 bool) (*ScriptInfo, error) { func CalcScriptInfo(sigScript, pkScript []byte, isP2SH bool) (*ScriptInfo, error) {
sigPops, err := parseScript(sigScript) sigPops, err := parseScript(sigScript)
if err != nil { if err != nil {
return nil, err return nil, err
@ -253,7 +247,7 @@ func CalcScriptInfo(sigScript, pkScript []byte, bip16 bool) (*ScriptInfo, error)
// All entries pushed to stack (or are OP_RESERVED and exec will fail). // All entries pushed to stack (or are OP_RESERVED and exec will fail).
si.NumInputs = len(sigPops) si.NumInputs = len(sigPops)
if si.PkScriptClass == ScriptHashTy && bip16 { if si.PkScriptClass == ScriptHashTy && isP2SH {
// The pay-to-hash-script is the final data push of the // The pay-to-hash-script is the final data push of the
// signature script. // signature script.
script := sigPops[len(sigPops)-1].data script := sigPops[len(sigPops)-1].data
@ -552,7 +546,7 @@ func ExtractAtomicSwapDataPushes(version uint16, pkScript []byte) (*AtomicSwapDa
copy(pushes.RecipientHash160[:], pops[9].data) copy(pushes.RecipientHash160[:], pops[9].data)
copy(pushes.RefundHash160[:], pops[16].data) copy(pushes.RefundHash160[:], pops[16].data)
if pops[2].data != nil { if pops[2].data != nil {
locktime, err := makeScriptNum(pops[2].data, true, 5) locktime, err := makeScriptNum(pops[2].data, 5)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }
@ -563,7 +557,7 @@ func ExtractAtomicSwapDataPushes(version uint16, pkScript []byte) (*AtomicSwapDa
return nil, nil return nil, nil
} }
if pops[11].data != nil { if pops[11].data != nil {
locktime, err := makeScriptNum(pops[11].data, true, 5) locktime, err := makeScriptNum(pops[11].data, 5)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }

View File

@ -379,7 +379,7 @@ func TestCalcScriptInfo(t *testing.T) {
sigScript string sigScript string
pkScript string pkScript string
bip16 bool isP2SH bool
scriptInfo ScriptInfo scriptInfo ScriptInfo
scriptInfoErr error scriptInfoErr error
@ -392,7 +392,7 @@ func TestCalcScriptInfo(t *testing.T) {
"SWAP ABS EQUAL", "SWAP ABS EQUAL",
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
"3152205ec4f59c", "3152205ec4f59c",
bip16: true, isP2SH: true,
scriptInfoErr: scriptError(ErrMalformedPush, ""), scriptInfoErr: scriptError(ErrMalformedPush, ""),
}, },
{ {
@ -402,7 +402,7 @@ func TestCalcScriptInfo(t *testing.T) {
"SWAP ABS", "SWAP ABS",
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
"3152205ec4f59c74 EQUAL", "3152205ec4f59c74 EQUAL",
bip16: true, isP2SH: true,
scriptInfoErr: scriptError(ErrMalformedPush, ""), scriptInfoErr: scriptError(ErrMalformedPush, ""),
}, },
{ {
@ -413,7 +413,7 @@ func TestCalcScriptInfo(t *testing.T) {
"CHECKSIG", "CHECKSIG",
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
"3152205ec4f59c74 EQUAL", "3152205ec4f59c74 EQUAL",
bip16: true, isP2SH: true,
scriptInfo: ScriptInfo{ scriptInfo: ScriptInfo{
PkScriptClass: ScriptHashTy, PkScriptClass: ScriptHashTy,
NumInputs: 3, NumInputs: 3,
@ -429,7 +429,7 @@ func TestCalcScriptInfo(t *testing.T) {
"SWAP ABS EQUAL", "SWAP ABS EQUAL",
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
"3152205ec4f59c74 EQUAL", "3152205ec4f59c74 EQUAL",
bip16: true, isP2SH: true,
scriptInfo: ScriptInfo{ scriptInfo: ScriptInfo{
PkScriptClass: ScriptHashTy, PkScriptClass: ScriptHashTy,
NumInputs: 3, NumInputs: 3,
@ -449,7 +449,7 @@ func TestCalcScriptInfo(t *testing.T) {
"161718191a1b1c1d1e1f2021 DATA_33 0x010203040" + "161718191a1b1c1d1e1f2021 DATA_33 0x010203040" +
"5060708090a0b0c0d0e0f101112131415161718191a1" + "5060708090a0b0c0d0e0f101112131415161718191a1" +
"b1c1d1e1f2021 3 CHECKMULTISIG", "b1c1d1e1f2021 3 CHECKMULTISIG",
bip16: true, isP2SH: true,
scriptInfo: ScriptInfo{ scriptInfo: ScriptInfo{
PkScriptClass: MultiSigTy, PkScriptClass: MultiSigTy,
NumInputs: 4, NumInputs: 4,
@ -463,7 +463,7 @@ func TestCalcScriptInfo(t *testing.T) {
sigScript := mustParseShortForm(test.sigScript) sigScript := mustParseShortForm(test.sigScript)
pkScript := mustParseShortForm(test.pkScript) pkScript := mustParseShortForm(test.pkScript)
si, err := CalcScriptInfo(sigScript, pkScript, test.bip16) si, err := CalcScriptInfo(sigScript, pkScript, test.isP2SH)
if e := tstCheckScriptError(err, test.scriptInfoErr); e != nil { if e := tstCheckScriptError(err, test.scriptInfoErr); e != nil {
t.Errorf("scriptinfo test %q: %v", test.name, e) t.Errorf("scriptinfo test %q: %v", test.name, e)
continue continue