Skip to content

Commit

Permalink
txscript: Store flags in instance versus bools.
Browse files Browse the repository at this point in the history
Rather than storing a separate bool for whether or not each flag is set in
every script engine instance, store the flags and check if the relevant
flag is set from each specific location.

This reduces the memory needed by each script engine instance and means
future flags will not require new fields.
  • Loading branch information
davecgh committed Apr 20, 2015
1 parent 8dd7412 commit af9ca8c
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 54 deletions.
81 changes: 34 additions & 47 deletions txscript/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,24 @@ const (

// Engine is the virtual machine that executes scripts.
type Engine struct {
scripts [][]parsedOpcode
scriptIdx int
scriptOff int
lastcodesep int
dstack Stack // data stack
astack Stack // alt stack
tx wire.MsgTx
txIdx int
condStack []int
numOps int
bip16 bool // treat execution as pay-to-script-hash
strictMultiSig bool // verify multisig stack item is zero length
discourageUpgradableNops bool // NOP1 to NOP10 are reserved for future soft-fork upgrades
verifyStrictEncoding bool // verify strict encoding of signatures
verifyCleanStack bool // verify stack is clean after script evaluation
verifyDERSignatures bool // verify signatures comply with the DER format
verifyLowS bool // verify signatures comply with the DER format and have an S value <= halforder
savedFirstStack [][]byte // stack from first script for bip16 scripts
scripts [][]parsedOpcode
scriptIdx int
scriptOff int
lastcodesep int
dstack Stack // data stack
astack Stack // alt stack
tx wire.MsgTx
txIdx int
condStack []int
numOps int
flags ScriptFlags
bip16 bool // treat execution as pay-to-script-hash
savedFirstStack [][]byte // stack from first script for bip16 scripts
}

// hasFlag returns whether the script engine instance has the passed flag set.
func (vm *Engine) hasFlag(flag ScriptFlags) bool {
return vm.flags&flag == flag
}

// Execute will execute all script in the script engine and return either nil
Expand Down Expand Up @@ -144,7 +144,9 @@ func (vm *Engine) CheckErrorCondition(finalScript bool) error {
if vm.scriptIdx < len(vm.scripts) {
return ErrStackScriptUnfinished
}
if finalScript && vm.verifyCleanStack && vm.dstack.Depth() != 1 {
if finalScript && vm.hasFlag(ScriptVerifyCleanStack) &&
vm.dstack.Depth() != 1 {

return ErrStackCleanStack
} else if vm.dstack.Depth() < 1 {
return ErrStackEmptyStack
Expand Down Expand Up @@ -302,7 +304,7 @@ func (vm *Engine) subScript() []parsedOpcode {
// checkHashTypeEncoding returns whether or not the passed hashtype adheres to
// the strict encoding requirements if enabled.
func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error {
if !vm.verifyStrictEncoding {
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
return nil
}

Expand All @@ -316,7 +318,7 @@ func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error {
// checkPubKeyEncoding returns whether or not the passed public key adheres to
// the strict encoding requirements if enabled.
func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
if !vm.verifyStrictEncoding {
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
return nil
}

Expand All @@ -334,7 +336,10 @@ func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
// checkSignatureEncoding returns whether or not the passed signature adheres to
// the strict encoding requirements if enabled.
func (vm *Engine) checkSignatureEncoding(sig []byte) error {
if !vm.verifyDERSignatures && !vm.verifyLowS && !vm.verifyStrictEncoding {
if !vm.hasFlag(ScriptVerifyDERSignatures) &&
!vm.hasFlag(ScriptVerifyLowS) &&
!vm.hasFlag(ScriptVerifyStrictEncoding) {

return nil
}

Expand Down Expand Up @@ -417,7 +422,7 @@ func (vm *Engine) checkSignatureEncoding(sig []byte) error {
}

// Verify the S value is <= halforder.
if vm.verifyLowS {
if vm.hasFlag(ScriptVerifyLowS) {
sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen])
if sValue.Cmp(halfOrder) > 0 {
return ErrStackInvalidLowSSignature
Expand Down Expand Up @@ -481,8 +486,8 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
}
scriptSig := tx.TxIn[txIdx].SignatureScript

var vm Engine
if flags&ScriptVerifySigPushOnly != 0 && !IsPushOnlyScript(scriptSig) {
vm := Engine{flags: flags}
if vm.hasFlag(ScriptVerifySigPushOnly) && !IsPushOnlyScript(scriptSig) {
return nil, ErrStackNonPushOnly
}

Expand All @@ -508,38 +513,20 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
}

// Parse flags.
if flags&ScriptBip16 == ScriptBip16 && isScriptHash(vm.scripts[1]) {
if vm.hasFlag(ScriptBip16) && isScriptHash(vm.scripts[1]) {
// if we are pay to scripthash then we only accept input
// scripts that push data
if !isPushOnly(vm.scripts[0]) {
return nil, ErrStackP2SHNonPushOnly
}
vm.bip16 = true
}
if flags&ScriptStrictMultiSig == ScriptStrictMultiSig {
vm.strictMultiSig = true
}
if flags&ScriptDiscourageUpgradableNops == ScriptDiscourageUpgradableNops {
vm.discourageUpgradableNops = true
}
if flags&ScriptVerifyStrictEncoding == ScriptVerifyStrictEncoding {
vm.verifyStrictEncoding = true
}
if flags&ScriptVerifyDERSignatures == ScriptVerifyDERSignatures {
vm.verifyDERSignatures = true
}
if flags&ScriptVerifyMinimalData == ScriptVerifyMinimalData {
if vm.hasFlag(ScriptVerifyMinimalData) {
vm.dstack.verifyMinimalData = true
vm.astack.verifyMinimalData = true
}
if flags&ScriptVerifyCleanStack == ScriptVerifyCleanStack {
if flags&ScriptBip16 != ScriptBip16 {
return nil, ErrInvalidFlags
}
vm.verifyCleanStack = true
}
if flags&ScriptVerifyLowS == ScriptVerifyLowS {
vm.verifyLowS = true
if vm.hasFlag(ScriptVerifyCleanStack) && !vm.hasFlag(ScriptBip16) {
return nil, ErrInvalidFlags
}

vm.tx = *tx
Expand Down
4 changes: 2 additions & 2 deletions txscript/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestCheckPubKeyEncoding(t *testing.T) {
isValid: false,
},
}
vm := Engine{verifyStrictEncoding: true}
vm := Engine{flags: ScriptVerifyStrictEncoding}
for _, test := range tests {
err := vm.checkPubKeyEncoding(test.key)
if err != nil && test.isValid {
Expand Down Expand Up @@ -337,7 +337,7 @@ func TestCheckSignatureEncoding(t *testing.T) {
},
}

vm := Engine{verifyStrictEncoding: true}
vm := Engine{flags: ScriptVerifyStrictEncoding}
for _, test := range tests {
err := vm.checkSignatureEncoding(test.sig)
if err != nil && test.isValid {
Expand Down
15 changes: 10 additions & 5 deletions txscript/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1131,8 +1131,9 @@ func opcodeNop(op *parsedOpcode, vm *Engine) error {
switch op.opcode.value {
case OP_NOP1, OP_NOP2, OP_NOP3, OP_NOP4, OP_NOP5,
OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10:
if vm.discourageUpgradableNops {
return fmt.Errorf("%s reserved for soft-fork upgrades", opcodemap[op.opcode.value].name)
if vm.hasFlag(ScriptDiscourageUpgradableNops) {
return fmt.Errorf("%s reserved for soft-fork upgrades",
opcodemap[op.opcode.value].name)
}
}
return nil
Expand Down Expand Up @@ -1850,7 +1851,9 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
}

var signature *btcec.Signature
if vm.verifyStrictEncoding || vm.verifyDERSignatures {
if vm.hasFlag(ScriptVerifyStrictEncoding) ||
vm.hasFlag(ScriptVerifyDERSignatures) {

signature, err = btcec.ParseDERSignature(sigStr, btcec.S256())
} else {
signature, err = btcec.ParseSignature(sigStr, btcec.S256())
Expand Down Expand Up @@ -1951,7 +1954,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
return err
}

if vm.strictMultiSig && len(dummy) != 0 {
if vm.hasFlag(ScriptStrictMultiSig) && len(dummy) != 0 {
return fmt.Errorf("multisig dummy argument is not zero length: %d",
len(dummy))
}
Expand Down Expand Up @@ -2010,7 +2013,9 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {

// Parse the signature.
var err error
if vm.verifyStrictEncoding || vm.verifyDERSignatures {
if vm.hasFlag(ScriptVerifyStrictEncoding) ||
vm.hasFlag(ScriptVerifyDERSignatures) {

parsedSig, err = btcec.ParseDERSignature(signature,
btcec.S256())
} else {
Expand Down

0 comments on commit af9ca8c

Please sign in to comment.