txscript: Store flags in instance versus bools.
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.
This commit is contained in:
parent
43c053bbfe
commit
0baac03129
3 changed files with 46 additions and 54 deletions
|
@ -80,24 +80,24 @@ const (
|
||||||
|
|
||||||
// Engine is the virtual machine that executes scripts.
|
// Engine is the virtual machine that executes scripts.
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
scripts [][]parsedOpcode
|
scripts [][]parsedOpcode
|
||||||
scriptIdx int
|
scriptIdx int
|
||||||
scriptOff int
|
scriptOff int
|
||||||
lastcodesep int
|
lastcodesep int
|
||||||
dstack Stack // data stack
|
dstack Stack // data stack
|
||||||
astack Stack // alt stack
|
astack Stack // alt stack
|
||||||
tx wire.MsgTx
|
tx wire.MsgTx
|
||||||
txIdx int
|
txIdx int
|
||||||
condStack []int
|
condStack []int
|
||||||
numOps int
|
numOps int
|
||||||
bip16 bool // treat execution as pay-to-script-hash
|
flags ScriptFlags
|
||||||
strictMultiSig bool // verify multisig stack item is zero length
|
bip16 bool // treat execution as pay-to-script-hash
|
||||||
discourageUpgradableNops bool // NOP1 to NOP10 are reserved for future soft-fork upgrades
|
savedFirstStack [][]byte // stack from first script for bip16 scripts
|
||||||
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
|
// hasFlag returns whether the script engine instance has the passed flag set.
|
||||||
verifyLowS bool // verify signatures comply with the DER format and have an S value <= halforder
|
func (vm *Engine) hasFlag(flag ScriptFlags) bool {
|
||||||
savedFirstStack [][]byte // stack from first script for bip16 scripts
|
return vm.flags&flag == flag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute will execute all script in the script engine and return either nil
|
// Execute will execute all script in the script engine and return either nil
|
||||||
|
@ -144,7 +144,9 @@ func (vm *Engine) CheckErrorCondition(finalScript bool) error {
|
||||||
if vm.scriptIdx < len(vm.scripts) {
|
if vm.scriptIdx < len(vm.scripts) {
|
||||||
return ErrStackScriptUnfinished
|
return ErrStackScriptUnfinished
|
||||||
}
|
}
|
||||||
if finalScript && vm.verifyCleanStack && vm.dstack.Depth() != 1 {
|
if finalScript && vm.hasFlag(ScriptVerifyCleanStack) &&
|
||||||
|
vm.dstack.Depth() != 1 {
|
||||||
|
|
||||||
return ErrStackCleanStack
|
return ErrStackCleanStack
|
||||||
} else if vm.dstack.Depth() < 1 {
|
} else if vm.dstack.Depth() < 1 {
|
||||||
return ErrStackEmptyStack
|
return ErrStackEmptyStack
|
||||||
|
@ -302,7 +304,7 @@ func (vm *Engine) subScript() []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.verifyStrictEncoding {
|
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +318,7 @@ 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.verifyStrictEncoding {
|
if !vm.hasFlag(ScriptVerifyStrictEncoding) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +336,10 @@ func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
|
||||||
// checkSignatureEncoding returns whether or not the passed signature adheres to
|
// checkSignatureEncoding returns whether or not the passed signature adheres to
|
||||||
// the strict encoding requirements if enabled.
|
// the strict encoding requirements if enabled.
|
||||||
func (vm *Engine) checkSignatureEncoding(sig []byte) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +422,7 @@ func (vm *Engine) checkSignatureEncoding(sig []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the S value is <= halforder.
|
// Verify the S value is <= halforder.
|
||||||
if vm.verifyLowS {
|
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 ErrStackInvalidLowSSignature
|
return ErrStackInvalidLowSSignature
|
||||||
|
@ -481,8 +486,8 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
|
||||||
}
|
}
|
||||||
scriptSig := tx.TxIn[txIdx].SignatureScript
|
scriptSig := tx.TxIn[txIdx].SignatureScript
|
||||||
|
|
||||||
var vm Engine
|
vm := Engine{flags: flags}
|
||||||
if flags&ScriptVerifySigPushOnly != 0 && !IsPushOnlyScript(scriptSig) {
|
if vm.hasFlag(ScriptVerifySigPushOnly) && !IsPushOnlyScript(scriptSig) {
|
||||||
return nil, ErrStackNonPushOnly
|
return nil, ErrStackNonPushOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +513,7 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse flags.
|
// 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
|
// if we are pay to scripthash then we only accept input
|
||||||
// scripts that push data
|
// scripts that push data
|
||||||
if !isPushOnly(vm.scripts[0]) {
|
if !isPushOnly(vm.scripts[0]) {
|
||||||
|
@ -516,30 +521,12 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
|
||||||
}
|
}
|
||||||
vm.bip16 = true
|
vm.bip16 = true
|
||||||
}
|
}
|
||||||
if flags&ScriptStrictMultiSig == ScriptStrictMultiSig {
|
if vm.hasFlag(ScriptVerifyMinimalData) {
|
||||||
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 {
|
|
||||||
vm.dstack.verifyMinimalData = true
|
vm.dstack.verifyMinimalData = true
|
||||||
vm.astack.verifyMinimalData = true
|
vm.astack.verifyMinimalData = true
|
||||||
}
|
}
|
||||||
if flags&ScriptVerifyCleanStack == ScriptVerifyCleanStack {
|
if vm.hasFlag(ScriptVerifyCleanStack) && !vm.hasFlag(ScriptBip16) {
|
||||||
if flags&ScriptBip16 != ScriptBip16 {
|
return nil, ErrInvalidFlags
|
||||||
return nil, ErrInvalidFlags
|
|
||||||
}
|
|
||||||
vm.verifyCleanStack = true
|
|
||||||
}
|
|
||||||
if flags&ScriptVerifyLowS == ScriptVerifyLowS {
|
|
||||||
vm.verifyLowS = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.tx = *tx
|
vm.tx = *tx
|
||||||
|
|
|
@ -79,7 +79,7 @@ func TestCheckPubKeyEncoding(t *testing.T) {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
vm := Engine{verifyStrictEncoding: true}
|
vm := Engine{flags: ScriptVerifyStrictEncoding}
|
||||||
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 {
|
||||||
|
@ -337,7 +337,7 @@ func TestCheckSignatureEncoding(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
vm := Engine{verifyStrictEncoding: true}
|
vm := Engine{flags: ScriptVerifyStrictEncoding}
|
||||||
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 {
|
||||||
|
|
|
@ -1131,8 +1131,9 @@ func opcodeNop(op *parsedOpcode, vm *Engine) error {
|
||||||
switch op.opcode.value {
|
switch op.opcode.value {
|
||||||
case OP_NOP1, OP_NOP2, OP_NOP3, OP_NOP4, OP_NOP5,
|
case OP_NOP1, OP_NOP2, OP_NOP3, OP_NOP4, OP_NOP5,
|
||||||
OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10:
|
OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10:
|
||||||
if vm.discourageUpgradableNops {
|
if vm.hasFlag(ScriptDiscourageUpgradableNops) {
|
||||||
return fmt.Errorf("%s reserved for soft-fork upgrades", opcodemap[op.opcode.value].name)
|
return fmt.Errorf("%s reserved for soft-fork upgrades",
|
||||||
|
opcodemap[op.opcode.value].name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -1850,7 +1851,9 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var signature *btcec.Signature
|
var signature *btcec.Signature
|
||||||
if vm.verifyStrictEncoding || vm.verifyDERSignatures {
|
if vm.hasFlag(ScriptVerifyStrictEncoding) ||
|
||||||
|
vm.hasFlag(ScriptVerifyDERSignatures) {
|
||||||
|
|
||||||
signature, err = btcec.ParseDERSignature(sigStr, btcec.S256())
|
signature, err = btcec.ParseDERSignature(sigStr, btcec.S256())
|
||||||
} else {
|
} else {
|
||||||
signature, err = btcec.ParseSignature(sigStr, btcec.S256())
|
signature, err = btcec.ParseSignature(sigStr, btcec.S256())
|
||||||
|
@ -1951,7 +1954,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
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",
|
return fmt.Errorf("multisig dummy argument is not zero length: %d",
|
||||||
len(dummy))
|
len(dummy))
|
||||||
}
|
}
|
||||||
|
@ -2010,7 +2013,9 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
|
||||||
|
|
||||||
// Parse the signature.
|
// Parse the signature.
|
||||||
var err error
|
var err error
|
||||||
if vm.verifyStrictEncoding || vm.verifyDERSignatures {
|
if vm.hasFlag(ScriptVerifyStrictEncoding) ||
|
||||||
|
vm.hasFlag(ScriptVerifyDERSignatures) {
|
||||||
|
|
||||||
parsedSig, err = btcec.ParseDERSignature(signature,
|
parsedSig, err = btcec.ParseDERSignature(signature,
|
||||||
btcec.S256())
|
btcec.S256())
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue