26fb20e4ed
This commit modifies the code to no longer require a protocol version. It does this by making use of the new Serialize function in btcwire. Unfortuantely this does entail a public API change which I generally don't like to do, but eliminating the usage of the protocol version throughout the codebase was important enough to warrant the change.
853 lines
26 KiB
Go
853 lines
26 KiB
Go
// Copyright (c) 2013 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcscript
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/conformal/btcwire"
|
|
"github.com/davecgh/go-spew/spew"
|
|
"time"
|
|
)
|
|
|
|
// StackErrShortScript is returned if the script has an opcode that is too long
|
|
// for the length of the script.
|
|
var StackErrShortScript = errors.New("execute past end of script")
|
|
|
|
// StackErrUnderflow is returned if an opcode requires more items on the stack
|
|
// than is present.
|
|
var StackErrUnderflow = errors.New("stack underflow")
|
|
|
|
// StackErrInvalidArgs is returned if the argument for an opcode is out of
|
|
// acceptable range.
|
|
var StackErrInvalidArgs = errors.New("invalid argument")
|
|
|
|
// StackErrOpDisabled is returned when a disabled opcode is encountered in the
|
|
// script.
|
|
var StackErrOpDisabled = errors.New("Disabled Opcode")
|
|
|
|
// StackErrVerifyFailed is returned when one of the OP_VERIFY or OP_*VERIFY
|
|
// instructions is executed and the conditions fails.
|
|
var StackErrVerifyFailed = errors.New("Verify failed")
|
|
|
|
// StackErrNumberTooBig is returned when the argument for an opcode that should
|
|
// be an offset is obviously far too large.
|
|
var StackErrNumberTooBig = errors.New("number too big")
|
|
|
|
// StackErrInvalidOpcode is returned when an opcode marked as invalid or a
|
|
// completely undefined opcode is encountered.
|
|
var StackErrInvalidOpcode = errors.New("Invalid Opcode")
|
|
|
|
// StackErrReservedOpcode is returned when an opcode marked as reserved is
|
|
// encountered.
|
|
var StackErrReservedOpcode = errors.New("Reserved Opcode")
|
|
|
|
// StackErrEarlyReturn is returned when OP_RETURN is executed in the script.
|
|
var StackErrEarlyReturn = errors.New("Script returned early")
|
|
|
|
// StackErrNoIf is returned if an OP_ELSE or OP_ENDIF is encountered without
|
|
// first having an OP_IF or OP_NOTIF in the script.
|
|
var StackErrNoIf = errors.New("OP_ELSE or OP_ENDIF with no matching OP_IF")
|
|
|
|
// StackErrMissingEndif is returned if the end of a script is reached without
|
|
// and OP_ENDIF to correspond to a conditional expression.
|
|
var StackErrMissingEndif = fmt.Errorf("execute fail, in conditional execution")
|
|
|
|
// StackErrTooManyPubkeys is returned if an OP_CHECKMULTISIG is encountered
|
|
// with more than MaxPubKeysPerMultiSig pubkeys present.
|
|
var StackErrTooManyPubkeys = errors.New("Invalid pubkey count in OP_CHECKMULTISIG")
|
|
|
|
// StackErrTooManyOperations is returned if a script has more then
|
|
// MaxOpsPerScript opcodes that do not push data.
|
|
var StackErrTooManyOperations = errors.New("Too many operations in script")
|
|
|
|
// StackErrElementTooBig is returned if the size of an element to be pushed to
|
|
// the stack is over MaxScriptElementSize.
|
|
var StackErrElementTooBig = errors.New("Element in script too large")
|
|
|
|
// StackErrUnknownAddress is returned when ScriptToAddress does not recognise
|
|
// the pattern of the script and thus can not find the address for payment.
|
|
var StackErrUnknownAddress = errors.New("non-recognised address")
|
|
|
|
// StackErrScriptFailed is returned when at the end of a script the boolean
|
|
// on top of the stack is false signifying that the script has failed.
|
|
var StackErrScriptFailed = errors.New("execute fail, fail on stack")
|
|
|
|
// StackErrScriptUnfinished is returned when CheckErrorCondition is called
|
|
// on a script that has not finished executing.
|
|
var StackErrScriptUnfinished = errors.New("Error check when script unfinished")
|
|
|
|
// StackErrEmpyStack is returned when the stack is empty at the end of
|
|
// execution. Normal operation requires that a boolean is on top of the stack
|
|
// when the scripts have finished executing.
|
|
var StackErrEmptyStack = errors.New("Stack empty at end of execution")
|
|
|
|
// StackErrP2SHNonPushOnly is returned when a Pay-to-Script-Hash transaction
|
|
// is encountered and the ScriptSig does operations other than push data (in
|
|
// violation of bip16).
|
|
var StackErrP2SHNonPushOnly = errors.New("pay to script hash with non " +
|
|
"pushonly input")
|
|
|
|
// StackErrInvalidParseType is an internal error returned from ScriptToAddress
|
|
// ony if the internal data tables are wrong.
|
|
var StackErrInvalidParseType = errors.New("internal error: invalid parsetype found")
|
|
|
|
// StackErrInvalidAddrOffset is an internal error returned from ScriptToAddress
|
|
// ony if the internal data tables are wrong.
|
|
var StackErrInvalidAddrOffset = errors.New("internal error: invalid offset found")
|
|
|
|
// StackErrInvalidIndex is returned when an out-of-bounds index was passed to
|
|
// a function.
|
|
var StackErrInvalidIndex = errors.New("Invalid script index")
|
|
|
|
// 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)
|
|
|
|
// Hash type bits from the end of a signature.
|
|
const (
|
|
SigHashOld = 0x0
|
|
SigHashAll = 0x1
|
|
SigHashNone = 0x2
|
|
SigHashSingle = 0x3
|
|
SigHashAnyOneCanPay = 0x80
|
|
)
|
|
|
|
// These are the constants specified for maximums in individual scripts.
|
|
const (
|
|
MaxOpsPerScript = 201 // Max number of non-push operations.
|
|
MaxPubKeysPerMultiSig = 20 // Multisig can't have more sigs than this.
|
|
MaxScriptElementSize = 520 // Max bytes pushable to the stack.
|
|
)
|
|
|
|
// ScriptClass is an enumeration for the list of standard types of script.
|
|
type ScriptClass byte
|
|
|
|
// Classes of script payment known about in the blockchain.
|
|
const (
|
|
PubKeyTy ScriptClass = iota // Pay pubkey.
|
|
PubKeyHashTy // Pay pubkey hash.
|
|
ScriptHashTy // Pay to script hash.
|
|
MultiSigTy // Multi signature.
|
|
NonStandardTy // None of the above.
|
|
)
|
|
|
|
// Script is the virtual machine that executes btcscripts.
|
|
type Script struct {
|
|
scripts [][]parsedOpcode
|
|
scriptidx int
|
|
scriptoff int
|
|
lastcodesep int
|
|
dstack Stack // data stack
|
|
astack Stack // alt stack
|
|
tx btcwire.MsgTx
|
|
txidx int
|
|
condStack []int
|
|
numOps int
|
|
bip16 bool // treat execution as pay-to-script-hash
|
|
savedFirstStack [][]byte // stack from first script for bip16 scripts
|
|
}
|
|
|
|
// isPubkey returns true if the script passed is a pubkey transaction, false
|
|
// otherwise.
|
|
func isPubkey(pops []parsedOpcode) bool {
|
|
return len(pops) == 2 &&
|
|
pops[0].opcode.value > OP_FALSE &&
|
|
pops[0].opcode.value <= OP_DATA_75 &&
|
|
pops[1].opcode.value == OP_CHECKSIG
|
|
}
|
|
|
|
// isPubkeyHash returns true if the script passed is a pubkey hash transaction,
|
|
// false otherwise.
|
|
func isPubkeyHash(pops []parsedOpcode) bool {
|
|
return len(pops) == 5 &&
|
|
pops[0].opcode.value == OP_DUP &&
|
|
pops[1].opcode.value == OP_HASH160 &&
|
|
pops[2].opcode.value == OP_DATA_20 &&
|
|
pops[3].opcode.value == OP_EQUALVERIFY &&
|
|
pops[4].opcode.value == OP_CHECKSIG
|
|
|
|
}
|
|
|
|
// isScriptHash returns true if the script passed is a pay-to-script-hash (P2SH)
|
|
// transction, false otherwise.
|
|
func isScriptHash(pops []parsedOpcode) bool {
|
|
return len(pops) == 3 &&
|
|
pops[0].opcode.value == OP_HASH160 &&
|
|
pops[1].opcode.value == OP_DATA_20 &&
|
|
pops[2].opcode.value == OP_EQUAL
|
|
}
|
|
|
|
// IsPayToScriptHash returns true if the script is in the standard
|
|
// Pay-To-Script-Hash format, false otherwise.
|
|
func IsPayToScriptHash(script []byte) bool {
|
|
pops, err := parseScript(script)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return isScriptHash(pops)
|
|
}
|
|
|
|
// isMultiSig returns true if the passed script is a multisig transaction, false
|
|
// otherwise.
|
|
func isMultiSig(pops []parsedOpcode) bool {
|
|
l := len(pops)
|
|
// absolute minimum is 1 pubkey so
|
|
// OP_1-16, pubkey, OP_1, OP_CHECK_MULTISIG
|
|
if l < 4 {
|
|
return false
|
|
}
|
|
if pops[0].opcode.value < OP_1 ||
|
|
pops[0].opcode.value > OP_16 {
|
|
return false
|
|
}
|
|
if pops[l-2].opcode.value < OP_1 ||
|
|
pops[l-2].opcode.value > OP_16 {
|
|
return false
|
|
}
|
|
if pops[l-1].opcode.value != OP_CHECK_MULTISIG {
|
|
return false
|
|
}
|
|
for _, pop := range pops[1 : l-2] {
|
|
// valid pubkeys are either 65 or 33 bytes
|
|
if len(pop.data) != 33 &&
|
|
len(pop.data) != 65 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// isPushOnly returns true if the script only pushes data, false otherwise.
|
|
func isPushOnly(pops []parsedOpcode) bool {
|
|
// technically we cheat here, we don't look at opcodes
|
|
for _, pop := range pops {
|
|
// all opcodes up to OP_16 are data instructions.
|
|
if pop.opcode.value < OP_FALSE ||
|
|
pop.opcode.value > OP_16 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// GetScriptClass returns the class of the script passed. If the script does not
|
|
// parse then NonStandardTy will be returned.
|
|
func GetScriptClass(script []byte) ScriptClass {
|
|
pops, err := parseScript(script)
|
|
if err != nil {
|
|
return NonStandardTy
|
|
}
|
|
return typeOfScript(pops)
|
|
}
|
|
|
|
// scriptType returns the type of the script being inspected from the known
|
|
// standard types.
|
|
func typeOfScript(pops []parsedOpcode) ScriptClass {
|
|
// XXX dubious optimisation: order these in order of popularity in the
|
|
// blockchain
|
|
if isPubkey(pops) {
|
|
return PubKeyTy
|
|
} else if isPubkeyHash(pops) {
|
|
return PubKeyHashTy
|
|
} else if isScriptHash(pops) {
|
|
return ScriptHashTy
|
|
} else if isMultiSig(pops) {
|
|
return MultiSigTy
|
|
}
|
|
return NonStandardTy
|
|
|
|
}
|
|
|
|
// parseScript preparses the script in bytes into a list of parsedOpcodes while
|
|
// applying a number of sanity checks.
|
|
func parseScript(script []byte) ([]parsedOpcode, error) {
|
|
return parseScriptTemplate(script, opcodemap)
|
|
}
|
|
|
|
// parseScriptTemplate is the same as parseScript but allows the passing of the
|
|
// template list for testing purposes. On error we return the list of parsed
|
|
// opcodes so far.
|
|
func parseScriptTemplate(script []byte, opcodemap map[byte]*opcode) ([]parsedOpcode, error) {
|
|
retScript := []parsedOpcode{}
|
|
for i := 0; i < len(script); {
|
|
instr := script[i]
|
|
op, ok := opcodemap[instr]
|
|
if !ok {
|
|
return retScript, StackErrInvalidOpcode
|
|
}
|
|
pop := parsedOpcode{opcode: op}
|
|
// parse data out of instruction.
|
|
switch {
|
|
case op.length == 1:
|
|
// no data, done here
|
|
i++
|
|
case op.length > 1:
|
|
if len(script[i:]) < op.length {
|
|
return retScript, StackErrShortScript
|
|
}
|
|
// slice out the data.
|
|
pop.data = script[i+1 : i+op.length]
|
|
i += op.length
|
|
case op.length < 0:
|
|
var err error
|
|
var l uint
|
|
off := i + 1
|
|
switch op.length {
|
|
case -1:
|
|
l, err = scriptUInt8(script[off:])
|
|
case -2:
|
|
l, err = scriptUInt16(script[off:])
|
|
case -4:
|
|
l, err = scriptUInt32(script[off:])
|
|
default:
|
|
return retScript,
|
|
fmt.Errorf("invalid opcode length %d",
|
|
op.length)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
off = i + 1 - op.length // beginning of data
|
|
if int(l) > len(script[off:]) {
|
|
return retScript, StackErrShortScript
|
|
}
|
|
pop.data = script[off : off+int(l)]
|
|
i += 1 - op.length + int(l)
|
|
}
|
|
retScript = append(retScript, pop)
|
|
}
|
|
return retScript, nil
|
|
}
|
|
|
|
// unparseScript reversed the action of parseScript and returns the
|
|
// parsedOpcodes as a list of bytes
|
|
func unparseScript(pops []parsedOpcode) []byte {
|
|
script := []byte{}
|
|
for _, pop := range pops {
|
|
script = append(script, pop.bytes()...)
|
|
}
|
|
return script
|
|
}
|
|
|
|
// NewScript returns a new script engine for the provided tx and input idx with
|
|
// a signature script scriptSig and a pubkeyscript scriptPubKey. If bip16 is
|
|
// true then it will be treated as if the bip16 threshhold has passed and thus
|
|
// pay-to-script hash transactions will be fully validated.
|
|
func NewScript(scriptSig []byte, scriptPubKey []byte, txidx int, tx *btcwire.MsgTx, bip16 bool) (*Script, error) {
|
|
var m Script
|
|
scripts := [][]byte{scriptSig, scriptPubKey}
|
|
m.scripts = make([][]parsedOpcode, len(scripts))
|
|
for i, scr := range scripts {
|
|
var err error
|
|
m.scripts[i], err = parseScript(scr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If the first scripts(s) are empty, must start on later ones.
|
|
if i == 0 && len(scr) == 0 {
|
|
// This could end up seeing an invalid initial pc if
|
|
// all scripts were empty. However, that is an invalid
|
|
// case and should fail.
|
|
m.scriptidx = i + 1
|
|
}
|
|
}
|
|
|
|
if bip16 && isScriptHash(m.scripts[1]) {
|
|
// if we are pay to scripthash then we only accept input
|
|
// scripts that push data
|
|
if !isPushOnly(m.scripts[0]) {
|
|
return nil, StackErrP2SHNonPushOnly
|
|
}
|
|
m.bip16 = true
|
|
}
|
|
|
|
m.tx = *tx
|
|
m.txidx = txidx
|
|
m.condStack = []int{OpCondTrue}
|
|
|
|
return &m, nil
|
|
}
|
|
|
|
// Execute will execute all script in the script engine and return either nil
|
|
// for successful validation or an error if one occurred.
|
|
func (s *Script) Execute() (err error) {
|
|
done := false
|
|
for done != true {
|
|
log.Tracef("%v", newLogClosure(func() string {
|
|
dis, err := s.DisasmPC()
|
|
if err != nil {
|
|
return fmt.Sprintf("stepping (%v)", err)
|
|
}
|
|
return fmt.Sprintf("stepping %v", dis)
|
|
}))
|
|
|
|
done, err = s.Step()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Tracef("%v", newLogClosure(func() string {
|
|
var dstr, astr string
|
|
|
|
// if we're tracing, dump the stacks.
|
|
if s.dstack.Depth() != 0 {
|
|
dstr = "Stack\n" + spew.Sdump(s.dstack)
|
|
}
|
|
if s.astack.Depth() != 0 {
|
|
astr = "AltStack\n" + spew.Sdump(s.astack)
|
|
}
|
|
|
|
return dstr + astr
|
|
}))
|
|
}
|
|
|
|
return s.CheckErrorCondition()
|
|
}
|
|
|
|
// CheckErrorCondition returns nil if the running script has ended and was
|
|
// successful, leaving a a true boolean on the stack. An error otherwise,
|
|
// including if the script has not finished.
|
|
func (s *Script) CheckErrorCondition() (err error) {
|
|
// Check we are actually done. if pc is past the end of script array
|
|
// then we have run out of scripts to run.
|
|
if s.scriptidx < len(s.scripts) {
|
|
return StackErrScriptUnfinished
|
|
}
|
|
if s.dstack.Depth() < 1 {
|
|
return StackErrEmptyStack
|
|
}
|
|
v, err := s.dstack.PopBool()
|
|
if err == nil && v == false {
|
|
// log interesting data.
|
|
log.Tracef("%v", func() string {
|
|
dis0, _ := s.DisasmScript(0)
|
|
dis1, _ := s.DisasmScript(1)
|
|
return fmt.Sprintf("scripts failed: script0: %s\n"+
|
|
"script1: %s", dis0, dis1)
|
|
})
|
|
err = StackErrScriptFailed
|
|
}
|
|
if err == nil && len(s.condStack) != 1 {
|
|
// conditional execution stack context left active
|
|
err = StackErrMissingEndif
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Step will execute the next instruction and move the program counter to the
|
|
// next opcode in the script, or the next script if the curent has ended. Step
|
|
// will return true in the case that the last opcode was successfully executed.
|
|
// if an error is returned then the result of calling Step or any other method
|
|
// is undefined.
|
|
func (m *Script) Step() (done bool, err error) {
|
|
// verify that it is pointing to a valid script address
|
|
err = m.validPC()
|
|
if err != nil {
|
|
return
|
|
}
|
|
opcode := m.scripts[m.scriptidx][m.scriptoff]
|
|
|
|
executeInstr := true
|
|
if m.condStack[0] != OpCondTrue {
|
|
// some opcodes still 'activate' if on the non-executing side
|
|
// of conditional execution
|
|
if opcode.conditional() {
|
|
executeInstr = true
|
|
} else {
|
|
executeInstr = false
|
|
}
|
|
}
|
|
if executeInstr {
|
|
err = opcode.exec(m)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// prepare for next instruction
|
|
m.scriptoff++
|
|
if m.scriptoff >= len(m.scripts[m.scriptidx]) {
|
|
m.numOps = 0 // number of ops is per script.
|
|
m.scriptoff = 0
|
|
if m.scriptidx == 0 && m.bip16 {
|
|
m.scriptidx++
|
|
m.savedFirstStack = m.GetStack()
|
|
} else if m.scriptidx == 1 && m.bip16 {
|
|
// Put us past the end for CheckErrorCondition()
|
|
m.scriptidx++
|
|
// We check script ran ok, if so then we pull
|
|
// the script out of the first stack and executre that.
|
|
err := m.CheckErrorCondition()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
script := m.savedFirstStack[len(m.savedFirstStack)-1]
|
|
pops, err := parseScript(script)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
m.scripts = append(m.scripts, pops)
|
|
// Set stack to be the stack from first script
|
|
// minus the script itself
|
|
m.SetStack(m.savedFirstStack[:len(m.savedFirstStack)-1])
|
|
} else {
|
|
m.scriptidx++
|
|
}
|
|
// there are zero length scripts in the wild
|
|
if m.scriptidx < len(m.scripts) && m.scriptoff >= len(m.scripts[m.scriptidx]) {
|
|
m.scriptidx++
|
|
}
|
|
m.lastcodesep = 0
|
|
if m.scriptidx >= len(m.scripts) {
|
|
done = true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// curPC returns either the current script and offset, or an error if the
|
|
// position isn't valid.
|
|
func (m *Script) curPC() (script int, off int, err error) {
|
|
err = m.validPC()
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return m.scriptidx, m.scriptoff, nil
|
|
}
|
|
|
|
// validPC returns an error if the current script position is valid for
|
|
// execution, nil otherwise.
|
|
func (m *Script) validPC() error {
|
|
if m.scriptidx >= len(m.scripts) {
|
|
return fmt.Errorf("Past input scripts %v:%v %v:xxxx", m.scriptidx, m.scriptoff, len(m.scripts))
|
|
}
|
|
if m.scriptoff >= len(m.scripts[m.scriptidx]) {
|
|
return fmt.Errorf("Past input scripts %v:%v %v:%04d", m.scriptidx, m.scriptoff, m.scriptidx, len(m.scripts[m.scriptidx]))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DisasmScript returns the disassembly string for the script at offset
|
|
// ``idx''. Where 0 is the scriptSig and 1 is the scriptPubKey.
|
|
func (m *Script) DisasmScript(idx int) (disstr string, err error) {
|
|
if idx >= len(m.scripts) {
|
|
return "", StackErrInvalidIndex
|
|
}
|
|
for i := range m.scripts[idx] {
|
|
disstr = disstr + m.disasm(idx, i) + "\n"
|
|
}
|
|
return disstr, nil
|
|
}
|
|
|
|
// DisasmPC returns the string for the disassembly of the opcode that will be
|
|
// next to execute when Step() is called.
|
|
func (m *Script) DisasmPC() (disstr string, err error) {
|
|
scriptidx, scriptoff, err := m.curPC()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return m.disasm(scriptidx, scriptoff), nil
|
|
}
|
|
|
|
// disasm is a helper member to produce the output for DisasmPC and
|
|
// DisasmScript. It produces the opcode prefixed by the program counter at the
|
|
// provided position in the script. it does no error checking and leaves that
|
|
// to the caller to provide a valid offse.
|
|
func (m *Script) disasm(scriptidx int, scriptoff int) string {
|
|
return fmt.Sprintf("%02x:%04x: %s", scriptidx, scriptoff,
|
|
m.scripts[scriptidx][scriptoff].print(false))
|
|
}
|
|
|
|
// subScript will return the script since the last OP_CODESEPARATOR
|
|
func (s *Script) subScript() []parsedOpcode {
|
|
return s.scripts[s.scriptidx][s.lastcodesep:]
|
|
}
|
|
|
|
// removeOpcode will remove any opcode matching ``opcode'' from the opcode
|
|
// stream in pkscript
|
|
func removeOpcode(pkscript []parsedOpcode, opcode byte) []parsedOpcode {
|
|
retScript := []parsedOpcode{}
|
|
for _, pop := range pkscript {
|
|
if pop.opcode.value != opcode {
|
|
retScript = append(retScript, pop)
|
|
}
|
|
}
|
|
return retScript
|
|
}
|
|
|
|
// removeOpcodeByData will return the pkscript minus any opcodes that would
|
|
// push the data in ``data'' to the stack.
|
|
func removeOpcodeByData(pkscript []parsedOpcode, data []byte) []parsedOpcode {
|
|
retScript := []parsedOpcode{}
|
|
for _, pop := range pkscript {
|
|
if !bytes.Equal(pop.data, data) {
|
|
retScript = append(retScript, pop)
|
|
}
|
|
}
|
|
return retScript
|
|
|
|
}
|
|
|
|
// DisasmString formats a disassembled script for one line printing.
|
|
func DisasmString(buf []byte) (string, error) {
|
|
disbuf := ""
|
|
opcodes, err := parseScript(buf)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
for _, pop := range opcodes {
|
|
disbuf += pop.print(true) + " "
|
|
}
|
|
if disbuf != "" {
|
|
disbuf = disbuf[:len(disbuf)-1]
|
|
}
|
|
return disbuf, nil
|
|
}
|
|
|
|
// calcScriptHash will, given the a script and hashtype for the current
|
|
// scriptmachine, calculate the doubleSha256 hash of the transaction and
|
|
// script to be used for signature signing and verification.
|
|
func (s *Script) calcScriptHash(script []parsedOpcode, hashType byte) []byte {
|
|
|
|
// remove all instances of OP_CODESEPARATOR still left in the script
|
|
script = removeOpcode(script, OP_CODESEPARATOR)
|
|
|
|
// Make a deep copy of the transaction, zeroing out the script
|
|
// for all inputs that are not currently being processed.
|
|
txCopy := s.tx.Copy()
|
|
txidx := s.txidx
|
|
for i := range txCopy.TxIn {
|
|
var txIn btcwire.TxIn
|
|
txIn = *txCopy.TxIn[i]
|
|
txCopy.TxIn[i] = &txIn
|
|
if i == txidx {
|
|
txCopy.TxIn[txidx].SignatureScript =
|
|
unparseScript(script)
|
|
} else {
|
|
txCopy.TxIn[i].SignatureScript = []byte{}
|
|
}
|
|
}
|
|
// Default behaviour has all outputs set up.
|
|
for i := range txCopy.TxOut {
|
|
var txOut btcwire.TxOut
|
|
txOut = *txCopy.TxOut[i]
|
|
txCopy.TxOut[i] = &txOut
|
|
}
|
|
|
|
switch hashType & 31 {
|
|
case SigHashNone:
|
|
txCopy.TxOut = txCopy.TxOut[0:0] // empty slice
|
|
for i := range txCopy.TxIn {
|
|
if i != txidx {
|
|
txCopy.TxIn[i].Sequence = 0
|
|
}
|
|
}
|
|
case SigHashSingle:
|
|
if txidx >= len(txCopy.TxOut) {
|
|
// This was created by a buggy implementation.
|
|
// In this case we do the same as bitcoind and bitcoinj
|
|
// and return 1 (as a uint256 little endian) as an
|
|
// error. Unfortunately this was not checked anywhere
|
|
// and thus is treated as the actual
|
|
// hash.
|
|
hash := make([]byte, 32)
|
|
hash[0] = 0x01
|
|
return hash
|
|
}
|
|
// Resize output array to up to and including requested index.
|
|
txCopy.TxOut = txCopy.TxOut[:txidx+1]
|
|
// all but current output get zeroed out
|
|
for i := 0; i < txidx; i++ {
|
|
txCopy.TxOut[i].Value = -1
|
|
txCopy.TxOut[i].PkScript = []byte{}
|
|
}
|
|
// Sequence on all other inputs is 0, too.
|
|
for i := range txCopy.TxIn {
|
|
if i != txidx {
|
|
txCopy.TxIn[i].Sequence = 0
|
|
}
|
|
}
|
|
default:
|
|
// XXX bitcoind treats undefined hashtypes like normal
|
|
// SigHashAll for purposes of hash generation.
|
|
fallthrough
|
|
case SigHashOld:
|
|
fallthrough
|
|
case SigHashAll:
|
|
// nothing special here
|
|
}
|
|
if hashType&SigHashAnyOneCanPay != 0 {
|
|
txCopy.TxIn = txCopy.TxIn[s.txidx : s.txidx+1]
|
|
txidx = 0
|
|
}
|
|
|
|
var wbuf bytes.Buffer
|
|
txCopy.Serialize(&wbuf)
|
|
// Append LE 4 bytes hash type
|
|
binary.Write(&wbuf, binary.LittleEndian, uint32(hashType))
|
|
|
|
return btcwire.DoubleSha256(wbuf.Bytes())
|
|
}
|
|
|
|
// scriptUInt8 return the number stored in the first byte of a slice.
|
|
func scriptUInt8(script []byte) (uint, error) {
|
|
if len(script) <= 1 {
|
|
return 0, StackErrShortScript
|
|
}
|
|
return uint(script[0]), nil
|
|
}
|
|
|
|
// scriptUInt16 returns the number stored in the next 2 bytes of a slice.
|
|
func scriptUInt16(script []byte) (uint, error) {
|
|
if len(script) <= 2 {
|
|
return 0, StackErrShortScript
|
|
}
|
|
// Yes this is little endian
|
|
return ((uint(script[1]) << 8) | uint(script[0])), nil
|
|
}
|
|
|
|
// scriptUInt32 returns the number stored in the first 4 bytes of a slice.
|
|
func scriptUInt32(script []byte) (uint, error) {
|
|
if len(script) <= 4 {
|
|
return 0, StackErrShortScript
|
|
}
|
|
// Yes this is little endian
|
|
return ((uint(script[3]) << 24) | (uint(script[2]) << 16) |
|
|
(uint(script[1]) << 8) | uint(script[0])), nil
|
|
}
|
|
|
|
// getStack returns the contents of stack as a byte array bottom up
|
|
func getStack(stack *Stack) [][]byte {
|
|
array := make([][]byte, stack.Depth())
|
|
for i := range array {
|
|
// PeekByteArry can't fail due to overflow, already checked
|
|
array[len(array)-i-1], _ =
|
|
stack.PeekByteArray(i)
|
|
}
|
|
return array
|
|
}
|
|
|
|
// setStack sets the stack to the contents of the array where the last item in
|
|
// the array is the top item in the stack.
|
|
func setStack(stack *Stack, data [][]byte) {
|
|
// This can not error. Only errors are for invalid arguments.
|
|
_ = stack.DropN(stack.Depth())
|
|
|
|
for i := range data {
|
|
stack.PushByteArray(data[i])
|
|
}
|
|
}
|
|
|
|
// GetStack returns the contents of the primary stack as an array. where the
|
|
// last item in the array is the top of the stack.
|
|
func (s *Script) GetStack() [][]byte {
|
|
return getStack(&s.dstack)
|
|
}
|
|
|
|
// SetStack sets the contents of the primary stack to the contents of the
|
|
// provided array where the last item in the array will be the top of the stack.
|
|
func (s *Script) SetStack(data [][]byte) {
|
|
setStack(&s.dstack, data)
|
|
}
|
|
|
|
// GetAltStack returns the contents of the primary stack as an array. where the
|
|
// last item in the array is the top of the stack.
|
|
func (s *Script) GetAltStack() [][]byte {
|
|
return getStack(&s.astack)
|
|
}
|
|
|
|
// SetAltStack sets the contents of the primary stack to the contents of the
|
|
// provided array where the last item in the array will be the top of the stack.
|
|
func (s *Script) SetAltStack(data [][]byte) {
|
|
setStack(&s.astack, data)
|
|
}
|
|
|
|
// GetSigOpCount provides a quick count of the number of signature operations
|
|
// in a script. a CHECKSIG operations counts for 1, and a CHECK_MULTISIG for 20.
|
|
// If the script fails to parse, then the count up to the point of failure is
|
|
// returned.
|
|
func GetSigOpCount(script []byte) int {
|
|
// We don't check error since parseScript returns the parsed-up-to-error
|
|
// list of pops.
|
|
pops, _ := parseScript(script)
|
|
|
|
return getSigOpCount(pops, false)
|
|
}
|
|
|
|
// GetPreciseSigOpCount returns the number of signature operations in
|
|
// scriptPubKey. If bip16 is true then scriptSig may be searched for the
|
|
// 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 up to the point of failure is returned.
|
|
func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, bip16 bool) int {
|
|
// We don't check error since parseScript returns the parsed-up-to-error
|
|
// list of pops.
|
|
pops, _ := parseScript(scriptPubKey)
|
|
// non P2SH transactions just treated as normal.
|
|
if !(bip16 && isScriptHash(pops)) {
|
|
return getSigOpCount(pops, true)
|
|
}
|
|
|
|
// Ok so this is P2SH, get the contained script and count it..
|
|
|
|
sigPops, err := parseScript(scriptSig)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
if !isPushOnly(sigPops) || len(sigPops) == 0 {
|
|
return 0
|
|
}
|
|
|
|
shScript := sigPops[len(sigPops)-1].data
|
|
// Means that sigPops is jus OP_1 - OP_16, no sigops there.
|
|
if shScript == nil {
|
|
return 0
|
|
}
|
|
|
|
shPops, _ := parseScript(shScript)
|
|
|
|
return getSigOpCount(shPops, true)
|
|
}
|
|
|
|
// getSigOpCount is the implementation function for counting the number of
|
|
// signature operations in the script provided by pops. If precise mode is
|
|
// requested then we attempt to count the number of operations for a multisig
|
|
// op. Otherwise we use the maximum.
|
|
func getSigOpCount(pops []parsedOpcode, precise bool) int {
|
|
nSigs := 0
|
|
for i, pop := range pops {
|
|
switch pop.opcode.value {
|
|
case OP_CHECKSIG:
|
|
fallthrough
|
|
case OP_CHECKSIGVERIFY:
|
|
nSigs++
|
|
case OP_CHECK_MULTISIG:
|
|
fallthrough
|
|
case OP_CHECKMULTISIGVERIFY:
|
|
// If we are being precise then look for familiar
|
|
// patterns for multisig, for now all we recognise is
|
|
// OP_1 - OP_16 to signify the number of pubkeys.
|
|
// Otherwise, we use the max of 20.
|
|
if precise && i > 0 &&
|
|
pops[i-1].opcode.value >= OP_1 &&
|
|
pops[i-1].opcode.value <= OP_16 {
|
|
nSigs += int(pops[i-1].opcode.value -
|
|
(OP_1 - 1))
|
|
} else {
|
|
nSigs += MaxPubKeysPerMultiSig
|
|
}
|
|
default:
|
|
// not a sigop.
|
|
}
|
|
}
|
|
|
|
return nSigs
|
|
}
|