Add PayToPubKeyHashScript and SignatureScript functions.

PayToPubKeyHashScript generates a new pay to pubkey hash script to use
as the pkScript when creating new transactions.  If the passed pubkey
hash is an invalid size, StackErrInvalidOpcode will be returned as an
error.

SignatureScript returns the signature script necessary to validate a
single input of a transaction.

This also adds sanity checking for serializing scripts into byte
slices.  If the length of a serialized opcode does not equal the
expected length, StackErrInvalidOpcode will be returned when unparsing
a []parsedOpcode.

New internal tests were added to verify checks for valid and invalid
parsed opcodes.
This commit is contained in:
Josh Rickmar 2013-09-10 11:39:29 -04:00
parent 29ed09cc98
commit a5f81fc545
6 changed files with 4278 additions and 87 deletions

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ import (
"crypto/ecdsa"
"crypto/sha1"
"crypto/sha256"
"encoding/binary"
"fmt"
"github.com/conformal/btcec"
"github.com/conformal/btcwire"
@ -687,36 +688,51 @@ func (pop *parsedOpcode) print(oneline bool) string {
return retString
}
func (pop *parsedOpcode) bytes() []byte {
func (pop *parsedOpcode) bytes() ([]byte, error) {
var retbytes []byte
if pop.opcode.length > 0 {
retbytes = make([]byte, 1, pop.opcode.length)
} else {
retbytes = make([]byte, 1, 1 + len(pop.data) -
retbytes = make([]byte, 1, 1+len(pop.data)-
pop.opcode.length)
}
retbytes[0] = pop.opcode.value
if pop.opcode.length == 1 {
return retbytes
if len(pop.data) != 0 {
return nil, StackErrInvalidOpcode
}
return retbytes, nil
}
nbytes := pop.opcode.length
if pop.opcode.length < 0 {
l := len(pop.data)
// tempting just to hardcode to avoid the complexity here.
switch pop.opcode.length {
case -1:
retbytes = append(retbytes, byte(l))
nbytes = int(retbytes[1]) + len(retbytes)
case -2:
retbytes = append(retbytes, byte(l&0xff),
byte(l>>8&0xff))
nbytes = int(binary.LittleEndian.Uint16(retbytes[1:])) +
len(retbytes)
case -4:
retbytes = append(retbytes, byte(l&0xff),
byte((l>>8)&0xff), byte((l>>16)&0xff),
byte((l>>24)&0xff))
nbytes = int(binary.LittleEndian.Uint32(retbytes[1:])) +
len(retbytes)
}
}
return append(retbytes, pop.data...)
retbytes = append(retbytes, pop.data...)
if len(retbytes) != nbytes {
return nil, StackErrInvalidOpcode
}
return retbytes, nil
}
// opcode implementation functions from here
@ -840,7 +856,7 @@ func opcodeEndif(op *parsedOpcode, s *Script) error {
return StackErrNoIf
}
stk := make([]int, len(s.condStack) -1, len(s.condStack) -1)
stk := make([]int, len(s.condStack)-1, len(s.condStack)-1)
copy(stk, s.condStack[1:])
s.condStack = stk
return nil
@ -1457,7 +1473,7 @@ func opcodeCheckSig(op *parsedOpcode, s *Script) error {
// the script if present.
subScript = removeOpcodeByData(subScript, sigStr)
hash := s.calcScriptHash(subScript, hashType)
hash := calcScriptHash(subScript, hashType, &s.tx, s.txidx)
pubKey, err := btcec.ParsePubKey(pkStr, btcec.S256())
if err != nil {
@ -1576,7 +1592,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error {
// get hashtype from original byte string
hashType := sigStrings[i][len(sigStrings[i])-1]
hash := s.calcScriptHash(script, hashType)
hash := calcScriptHash(script, hashType, &s.tx, s.txidx)
inner:
// Find first pubkey that successfully validates signature.
// we start off the search from the key that was successful

144
script.go
View file

@ -6,11 +6,16 @@ package btcscript
import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"github.com/conformal/btcec"
"github.com/conformal/btcwire"
"github.com/davecgh/go-spew/spew"
"io"
"math/big"
"time"
)
@ -338,12 +343,16 @@ func parseScriptTemplate(script []byte, opcodemap map[byte]*opcode) ([]parsedOpc
// unparseScript reversed the action of parseScript and returns the
// parsedOpcodes as a list of bytes
func unparseScript(pops []parsedOpcode) []byte {
func unparseScript(pops []parsedOpcode) ([]byte, error) {
script := make([]byte, 0, len(pops))
for _, pop := range pops {
script = append(script, pop.bytes()...)
b, err := pop.bytes()
if err != nil {
return nil, err
}
script = append(script, b...)
}
return script
return script, nil
}
// NewScript returns a new script engine for the provided tx and input idx with
@ -625,22 +634,23 @@ func DisasmString(buf []byte) (string, error) {
// 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 {
func calcScriptHash(script []parsedOpcode, hashType byte, tx *btcwire.MsgTx, idx int) []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
txCopy := tx.Copy()
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)
if i == idx {
// unparseScript cannot fail here, because removeOpcode
// above only returns a valid script.
sigscript, _ := unparseScript(script)
txCopy.TxIn[idx].SignatureScript = sigscript
} else {
txCopy.TxIn[i].SignatureScript = []byte{}
}
@ -656,12 +666,12 @@ func (s *Script) calcScriptHash(script []parsedOpcode, hashType byte) []byte {
case SigHashNone:
txCopy.TxOut = txCopy.TxOut[0:0] // empty slice
for i := range txCopy.TxIn {
if i != txidx {
if i != idx {
txCopy.TxIn[i].Sequence = 0
}
}
case SigHashSingle:
if txidx >= len(txCopy.TxOut) {
if idx >= 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
@ -673,15 +683,15 @@ func (s *Script) calcScriptHash(script []parsedOpcode, hashType byte) []byte {
return hash
}
// Resize output array to up to and including requested index.
txCopy.TxOut = txCopy.TxOut[:txidx+1]
txCopy.TxOut = txCopy.TxOut[:idx+1]
// all but current output get zeroed out
for i := 0; i < txidx; i++ {
for i := 0; i < idx; 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 {
if i != idx {
txCopy.TxIn[i].Sequence = 0
}
}
@ -695,8 +705,8 @@ func (s *Script) calcScriptHash(script []parsedOpcode, hashType byte) []byte {
// nothing special here
}
if hashType&SigHashAnyOneCanPay != 0 {
txCopy.TxIn = txCopy.TxIn[s.txidx : s.txidx+1]
txidx = 0
txCopy.TxIn = txCopy.TxIn[idx : idx+1]
idx = 0
}
var wbuf bytes.Buffer
@ -861,3 +871,105 @@ func getSigOpCount(pops []parsedOpcode, precise bool) int {
return nSigs
}
// PayToPubKeyHashScript creates a new script to pay a transaction
// output to a 20-byte pubkey hash.
func PayToPubKeyHashScript(pubKeyHash []byte) (pkScript []byte, err error) {
pops := []parsedOpcode{
parsedOpcode{
opcode: opcodemap[OP_DUP],
},
parsedOpcode{
opcode: opcodemap[OP_HASH160],
},
parsedOpcode{
opcode: opcodemap[OP_DATA_20],
data: pubKeyHash,
},
parsedOpcode{
opcode: opcodemap[OP_EQUALVERIFY],
},
parsedOpcode{
opcode: opcodemap[OP_CHECKSIG],
},
}
return unparseScript(pops)
}
// SignatureScript creates an input signature script for tx to spend
// BTC sent from a previous output to the owner of privkey. tx must
// include all transaction inputs and outputs, however txin scripts are
// allowed to be filled or empty. The returned script is calculated to
// be used as the idx'th txin sigscript for tx. subscript is the PkScript
// of the previous output being used as the idx'th input. privkey is
// serialized in either a compressed or uncompressed format based on
// compress. This format must match the same format used to generate
// the payment address, or the script validation will fail.
func SignatureScript(tx *btcwire.MsgTx, idx int, subscript []byte, hashType byte, privkey *ecdsa.PrivateKey, compress bool) ([]byte, error) {
return signatureScriptCustomReader(rand.Reader, tx, idx, subscript,
hashType, privkey, compress)
}
// This function exists so we can test ecdsa.Sign's error for an invalid
// reader.
func signatureScriptCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int,
subscript []byte, hashType byte, privkey *ecdsa.PrivateKey,
compress bool) ([]byte, error) {
parsedScript, err := parseScript(subscript)
if err != nil {
return nil, fmt.Errorf("cannot parse output script: %v", err)
}
hash := calcScriptHash(parsedScript, hashType, tx, idx)
r, s, err := ecdsa.Sign(reader, privkey, hash)
if err != nil {
return nil, fmt.Errorf("cannot sign tx input: %s", err)
}
sig := append(sigDER(r, s), hashType)
pk := (*btcec.PublicKey)(&privkey.PublicKey)
var pubkeyOpcode *parsedOpcode
if compress {
pubkeyOpcode = &parsedOpcode{
opcode: opcodemap[OP_DATA_33],
data: pk.SerializeCompressed(),
}
} else {
pubkeyOpcode = &parsedOpcode{
opcode: opcodemap[OP_DATA_65],
data: pk.SerializeUncompressed(),
}
}
pops := []parsedOpcode{
parsedOpcode{
opcode: opcodemap[byte(len(sig))],
data: sig,
},
*pubkeyOpcode,
}
return unparseScript(pops)
}
// sigDER returns the ECDSA signature r, s in the DER format used by
// signature scripts. The signature does not include the appended hashtype.
//
// encoding/asn1 is broken so we hand roll this output:
//
// 0x30 <length> 0x02 <length r> r 0x02 <length s> s
func sigDER(r, s *big.Int) []byte {
// total length of returned signature is 1 byte for each magic and
// length (6 total), plus lengths of r and s
length := 6 + len(r.Bytes()) + len(s.Bytes())
b := make([]byte, length, length)
b[0] = 0x30
b[1] = byte(length - 2)
b[2] = 0x02
b[3] = byte(len(r.Bytes()))
offset := copy(b[4:], r.Bytes()) + 4
b[offset] = 0x02
b[offset+1] = byte(len(s.Bytes()))
copy(b[offset+2:], s.Bytes())
return b
}

View file

@ -6,8 +6,11 @@ package btcscript_test
import (
"bytes"
"crypto/ecdsa"
"github.com/conformal/btcec"
"github.com/conformal/btcscript"
"github.com/conformal/btcwire"
"math/big"
"testing"
)
@ -1839,3 +1842,395 @@ func TestCheckErrorCondition(t *testing.T) {
t.Errorf("unexpected error %v on final check", err)
}
}
func TestPayToPubKeyHashScript(t *testing.T) {
validaddr := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20}
invalidaddr := validaddr[:len(validaddr)-2]
expected := []byte{btcscript.OP_DUP, btcscript.OP_HASH160,
btcscript.OP_DATA_20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, btcscript.OP_EQUALVERIFY,
btcscript.OP_CHECKSIG}
script, err := btcscript.PayToPubKeyHashScript(validaddr)
if err != nil {
t.Error(err)
return
}
if !bytes.Equal(script, expected) {
t.Error("Unexpected script result.")
return
}
_, err = btcscript.PayToPubKeyHashScript(invalidaddr)
if err == nil {
t.Error("Erroneously created script for invalid address.")
return
}
}
type TstSigScript struct {
name string
inputs []TstInput
hashtype byte
compress bool
scriptAtWrongIndex bool
}
type TstInput struct {
txout *btcwire.TxOut
sigscriptGenerates bool
inputValidates bool
indexOutOfRange bool
invalidReader bool
}
var coinbaseOutPoint = &btcwire.OutPoint{
Index: (1 << 32) - 1,
}
// Pregenerated private key, with associated public key and pkScripts
// for the uncompressed and compressed hash160.
var (
privkeyD = []byte{0x6b, 0x0f, 0xd8, 0xda, 0x54, 0x22, 0xd0, 0xb7,
0xb4, 0xfc, 0x4e, 0x55, 0xd4, 0x88, 0x42, 0xb3, 0xa1, 0x65,
0xac, 0x70, 0x7f, 0x3d, 0xa4, 0x39, 0x5e, 0xcb, 0x3b, 0xb0,
0xd6, 0x0e, 0x06, 0x92}
pubkeyX = []byte{0xb2, 0x52, 0xf0, 0x49, 0x85, 0x78, 0x03, 0x03, 0xc8,
0x7d, 0xce, 0x51, 0x7f, 0xa8, 0x69, 0x0b, 0x91, 0x95, 0xf4,
0xf3, 0x5c, 0x26, 0x73, 0x05, 0x05, 0xa2, 0xee, 0xbc, 0x09,
0x38, 0x34, 0x3a}
pubkeyY = []byte{0xb7, 0xc6, 0x7d, 0xb2, 0xe1, 0xff, 0xc8, 0x43, 0x1f,
0x63, 0x32, 0x62, 0xaa, 0x60, 0xc6, 0x83, 0x30, 0xbd, 0x24,
0x7e, 0xef, 0xdb, 0x6f, 0x2e, 0x8d, 0x56, 0xf0, 0x3c, 0x9f,
0x6d, 0xb6, 0xf8}
uncompressedPkScript = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5,
0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32,
0x53, 0x90, 0x0e, 0x0a, 0x86, 0xc9, 0xfa, 0x88, 0xac}
compressedPkScript = []byte{0x76, 0xa9, 0x14, 0x27, 0x4d, 0x9f, 0x7f,
0x61, 0x7e, 0x7c, 0x7a, 0x1c, 0x1f, 0xb2, 0x75, 0x79, 0x10,
0x43, 0x65, 0x68, 0x27, 0x9d, 0x86, 0x88, 0xac}
shortPkScript = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5,
0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32,
0x53, 0x90, 0x0e, 0x0a, 0x88, 0xac}
uncompressedAddrStr = "1L6fd93zGmtzkK6CsZFVVoCwzZV3MUtJ4F"
compressedAddrStr = "14apLppt9zTq6cNw8SDfiJhk9PhkZrQtYZ"
)
// Pretend output amounts.
const coinbaseVal = 2500000000
const fee = 5000000
var SigScriptTests = []TstSigScript{
TstSigScript{
name: "one input uncompressed",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "two inputs uncompressed",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
TstInput{
txout: btcwire.NewTxOut(coinbaseVal+fee, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "one input compressed",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, compressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: true,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "two inputs compressed",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, compressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
TstInput{
txout: btcwire.NewTxOut(coinbaseVal+fee, compressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: true,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "hashtype SigHashNone",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashNone,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "hashtype SigHashSingle",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashSingle,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "hashtype SigHashAnyoneCanPay",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAnyOneCanPay,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "hashtype non-standard",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: 0x04,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "invalid compression",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: false,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: true,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "short PkScript",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, shortPkScript),
sigscriptGenerates: false,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: false,
},
TstSigScript{
name: "valid script at wrong index",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
TstInput{
txout: btcwire.NewTxOut(coinbaseVal+fee, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: true,
},
TstSigScript{
name: "index out of range",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
TstInput{
txout: btcwire.NewTxOut(coinbaseVal+fee, uncompressedPkScript),
sigscriptGenerates: true,
inputValidates: true,
indexOutOfRange: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: true,
},
TstSigScript{
name: "invalid reader",
inputs: []TstInput{
TstInput{
txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript),
invalidReader: true,
sigscriptGenerates: false,
},
},
hashtype: btcscript.SigHashAll,
compress: false,
scriptAtWrongIndex: true,
},
}
// Test the sigscript generation for valid and invalid inputs, all
// hashtypes, and with and without compression. This test creates
// sigscripts to spend fake coinbase inputs, as sigscripts cannot be
// created for the MsgTxs in txTests, since they come from the blockchain
// and we don't have the private keys.
func TestSignatureScript(t *testing.T) {
privkey := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: btcec.S256(),
X: new(big.Int),
Y: new(big.Int),
},
D: new(big.Int),
}
privkey.D.SetBytes(privkeyD)
privkey.PublicKey.X.SetBytes(pubkeyX)
privkey.PublicKey.Y.SetBytes(pubkeyY)
nexttest:
for i := range SigScriptTests {
tx := btcwire.NewMsgTx()
output := btcwire.NewTxOut(500, []byte{btcscript.OP_RETURN})
tx.AddTxOut(output)
for _ = range SigScriptTests[i].inputs {
txin := btcwire.NewTxIn(coinbaseOutPoint, nil)
tx.AddTxIn(txin)
}
var script []byte
var err error
for j := range tx.TxIn {
var idx int
if SigScriptTests[i].inputs[j].indexOutOfRange {
t.Errorf("at test %v", SigScriptTests[i].name)
idx = len(SigScriptTests[i].inputs)
} else {
idx = j
}
if SigScriptTests[i].inputs[j].invalidReader {
script, err = btcscript.TstSignatureScriptCustomReader(
new(bytes.Buffer), tx, idx,
SigScriptTests[i].inputs[j].txout.PkScript,
SigScriptTests[i].hashtype,
privkey,
SigScriptTests[i].compress)
} else {
script, err = btcscript.SignatureScript(tx, idx,
SigScriptTests[i].inputs[j].txout.PkScript,
SigScriptTests[i].hashtype,
privkey,
SigScriptTests[i].compress)
}
if (err == nil) != SigScriptTests[i].inputs[j].sigscriptGenerates {
if err == nil {
t.Errorf("passed test '%v' incorrectly",
SigScriptTests[i].name)
} else {
t.Errorf("failed test '%v': %v",
SigScriptTests[i].name, err)
}
continue nexttest
}
if !SigScriptTests[i].inputs[j].sigscriptGenerates {
// done with this test
continue nexttest
}
tx.TxIn[j].SignatureScript = script
}
// If testing using a correct sigscript but for an incorrect
// index, use last input script for first input. Requires > 0
// inputs for test.
if SigScriptTests[i].scriptAtWrongIndex {
tx.TxIn[0].SignatureScript = script
SigScriptTests[i].inputs[0].inputValidates = false
}
// Validate tx input scripts
for j, txin := range tx.TxIn {
engine, err := btcscript.NewScript(txin.SignatureScript,
SigScriptTests[i].inputs[j].txout.PkScript,
j, tx, true)
if err != nil {
t.Errorf("cannot create script vm for test %v: %v",
SigScriptTests[i].name, err)
continue nexttest
}
err = engine.Execute()
if (err == nil) != SigScriptTests[i].inputs[j].inputValidates {
if err == nil {
t.Errorf("passed test '%v' validation incorrectly: %v",
SigScriptTests[i].name, err)
} else {
t.Errorf("failed test '%v' validation: %v",
SigScriptTests[i].name, err)
}
continue nexttest
}
}
}
}

View file

@ -173,7 +173,7 @@ func (s *Stack) nipN(idx int) (so []byte, err error) {
if idx == 0 {
s.stk = s.stk[:sz-1]
} else if idx == sz-1 {
s1 := make([][]byte, sz - 1, sz - 1)
s1 := make([][]byte, sz-1, sz-1)
copy(s1, s.stk[1:])
s.stk = s1
} else {

View file

@ -2,67 +2,70 @@
github.com/conformal/btcscript/address.go scriptToAddressTemplate 100.00% (58/58)
github.com/conformal/btcscript/script.go Script.Step 100.00% (37/37)
github.com/conformal/btcscript/script.go parseScriptTemplate 100.00% (30/30)
github.com/conformal/btcscript/opcode.go parsedOpcode.bytes 100.00% (23/23)
github.com/conformal/btcscript/script.go NewScript 100.00% (18/18)
github.com/conformal/btcscript/stack.go asInt 100.00% (18/18)
github.com/conformal/btcscript/script.go signatureScriptCustomReader 100.00% (15/15)
github.com/conformal/btcscript/stack.go Stack.nipN 100.00% (14/14)
github.com/conformal/btcscript/stack.go fromInt 100.00% (14/14)
github.com/conformal/btcscript/opcode.go parsedOpcode.bytes 100.00% (14/14)
github.com/conformal/btcscript/script.go isMultiSig 100.00% (13/13)
github.com/conformal/btcscript/script.go GetPreciseSigOpCount 100.00% (13/13)
github.com/conformal/btcscript/opcode.go opcodeWithin 100.00% (13/13)
github.com/conformal/btcscript/opcode.go parsedOpcode.print 100.00% (12/12)
github.com/conformal/btcscript/opcode.go opcodeIf 100.00% (11/11)
github.com/conformal/btcscript/script.go sigDER 100.00% (11/11)
github.com/conformal/btcscript/opcode.go opcodeNotIf 100.00% (11/11)
github.com/conformal/btcscript/opcode.go opcodeIf 100.00% (11/11)
github.com/conformal/btcscript/opcode.go opcodeMax 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeLessThanOrEqual 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeGreaterThanOrEqual 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeMin 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeGreaterThan 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeNumNotEqual 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeLessThan 100.00% (10/10)
github.com/conformal/btcscript/script.go getSigOpCount 100.00% (10/10)
github.com/conformal/btcscript/stack.go Stack.Tuck 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeNumEqual 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeBoolOr 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeBoolAnd 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeLessThan 100.00% (10/10)
github.com/conformal/btcscript/stack.go Stack.Tuck 100.00% (10/10)
github.com/conformal/btcscript/script.go getSigOpCount 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeMin 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeMax 100.00% (10/10)
github.com/conformal/btcscript/opcode.go opcodeGreaterThan 100.00% (10/10)
github.com/conformal/btcscript/stack.go Stack.RotN 100.00% (9/9)
github.com/conformal/btcscript/script.go DisasmString 100.00% (9/9)
github.com/conformal/btcscript/opcode.go opcodeGreaterThanOrEqual 100.00% (10/10)
github.com/conformal/btcscript/stack.go Stack.SwapN 100.00% (9/9)
github.com/conformal/btcscript/script.go DisasmString 100.00% (9/9)
github.com/conformal/btcscript/stack.go Stack.RotN 100.00% (9/9)
github.com/conformal/btcscript/stack.go Stack.OverN 100.00% (9/9)
github.com/conformal/btcscript/opcode.go opcodeAdd 100.00% (8/8)
github.com/conformal/btcscript/opcode.go opcodeEqual 100.00% (8/8)
github.com/conformal/btcscript/opcode.go opcodeSub 100.00% (8/8)
github.com/conformal/btcscript/opcode.go opcodeAdd 100.00% (8/8)
github.com/conformal/btcscript/stack.go Stack.DupN 100.00% (8/8)
github.com/conformal/btcscript/script.go unparseScript 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcodePick 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcode0NotEqual 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcodeRoll 100.00% (7/7)
github.com/conformal/btcscript/stack.go Stack.DropN 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcodeNot 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcodePick 100.00% (7/7)
github.com/conformal/btcscript/opcode.go opcodeRoll 100.00% (7/7)
github.com/conformal/btcscript/script.go typeOfScript 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeEndif 100.00% (6/6)
github.com/conformal/btcscript/opcode.go parsedOpcode.conditional 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeIfDup 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeVerify 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeElse 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeSize 100.00% (5/5)
github.com/conformal/btcscript/stack.go Stack.PickN 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeVerify 100.00% (6/6)
github.com/conformal/btcscript/script.go typeOfScript 100.00% (6/6)
github.com/conformal/btcscript/opcode.go opcodeRipemd160 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeSha1 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcode1Add 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcode1Sub 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeNegate 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeAbs 100.00% (5/5)
github.com/conformal/btcscript/stack.go Stack.RollN 100.00% (5/5)
github.com/conformal/btcscript/script.go removeOpcodeByData 100.00% (5/5)
github.com/conformal/btcscript/script.go removeOpcode 100.00% (5/5)
github.com/conformal/btcscript/opcode.go parsedOpcode.exec 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeRipemd160 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeHash256 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeHash160 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeHash256 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeSha256 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeSha1 100.00% (5/5)
github.com/conformal/btcscript/script.go Script.validPC 100.00% (5/5)
github.com/conformal/btcscript/script.go Script.DisasmScript 100.00% (5/5)
github.com/conformal/btcscript/stack.go Stack.PickN 100.00% (5/5)
github.com/conformal/btcscript/stack.go Stack.RollN 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeFromAltStack 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeToAltStack 100.00% (5/5)
github.com/conformal/btcscript/script.go removeOpcodeByData 100.00% (5/5)
github.com/conformal/btcscript/script.go removeOpcode 100.00% (5/5)
github.com/conformal/btcscript/script.go Script.validPC 100.00% (5/5)
github.com/conformal/btcscript/opcode.go parsedOpcode.exec 100.00% (5/5)
github.com/conformal/btcscript/script.go Script.DisasmScript 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodeSize 100.00% (5/5)
github.com/conformal/btcscript/opcode.go opcodePushData 100.00% (4/4)
github.com/conformal/btcscript/opcode.go opcodeEqualVerify 100.00% (4/4)
github.com/conformal/btcscript/opcode.go opcodeNumEqualVerify 100.00% (4/4)
@ -72,76 +75,77 @@ github.com/conformal/btcscript/stack.go asBool 100.00% (4/4)
github.com/conformal/btcscript/stack.go Stack.PeekByteArray 100.00% (4/4)
github.com/conformal/btcscript/stack.go Stack.PeekInt 100.00% (4/4)
github.com/conformal/btcscript/stack.go Stack.PopInt 100.00% (4/4)
github.com/conformal/btcscript/script.go unparseScript 100.00% (4/4)
github.com/conformal/btcscript/script.go IsPayToScriptHash 100.00% (4/4)
github.com/conformal/btcscript/script.go isPushOnly 100.00% (4/4)
github.com/conformal/btcscript/script.go GetScriptClass 100.00% (4/4)
github.com/conformal/btcscript/script.go Script.curPC 100.00% (4/4)
github.com/conformal/btcscript/script.go Script.DisasmPC 100.00% (4/4)
github.com/conformal/btcscript/script.go getStack 100.00% (4/4)
github.com/conformal/btcscript/stack.go Stack.PeekBool 100.00% (4/4)
github.com/conformal/btcscript/script.go getStack 100.00% (4/4)
github.com/conformal/btcscript/script.go scriptUInt16 100.00% (3/3)
github.com/conformal/btcscript/address.go ScriptType.String 100.00% (3/3)
github.com/conformal/btcscript/script.go scriptUInt8 100.00% (3/3)
github.com/conformal/btcscript/script.go setStack 100.00% (3/3)
github.com/conformal/btcscript/stack.go fromBool 100.00% (3/3)
github.com/conformal/btcscript/script.go scriptUInt32 100.00% (3/3)
github.com/conformal/btcscript/script.go setStack 100.00% (3/3)
github.com/conformal/btcscript/address.go ScriptType.String 100.00% (3/3)
github.com/conformal/btcscript/script.go scriptUInt16 100.00% (3/3)
github.com/conformal/btcscript/script.go scriptUInt8 100.00% (3/3)
github.com/conformal/btcscript/opcode.go opcodeFalse 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeCodeSeparator 100.00% (2/2)
github.com/conformal/btcscript/opcode.go calcHash 100.00% (2/2)
github.com/conformal/btcscript/script.go GetSigOpCount 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeDepth 100.00% (2/2)
github.com/conformal/btcscript/stack.go Stack.Depth 100.00% (2/2)
github.com/conformal/btcscript/stack.go Stack.NipN 100.00% (2/2)
github.com/conformal/btcscript/address.go ScriptToAddress 100.00% (2/2)
github.com/conformal/btcscript/stack.go Stack.NipN 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeDepth 100.00% (2/2)
github.com/conformal/btcscript/script.go GetSigOpCount 100.00% (2/2)
github.com/conformal/btcscript/opcode.go calcHash 100.00% (2/2)
github.com/conformal/btcscript/script.go PayToPubKeyHashScript 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeFalse 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeN 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcode1Negate 100.00% (2/2)
github.com/conformal/btcscript/script.go Script.GetStack 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.Depth 100.00% (2/2)
github.com/conformal/btcscript/opcode.go opcodeNop 100.00% (1/1)
github.com/conformal/btcscript/log.go DisableLog 100.00% (1/1)
github.com/conformal/btcscript/script.go isPubkey 100.00% (1/1)
github.com/conformal/btcscript/script.go isPubkeyHash 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.SetStack 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeReserved 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.GetAltStack 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.SetAltStack 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDisabled 100.00% (1/1)
github.com/conformal/btcscript/script.go parseScript 100.00% (1/1)
github.com/conformal/btcscript/opcode.go init 100.00% (1/1)
github.com/conformal/btcscript/log.go newLogClosure 100.00% (1/1)
github.com/conformal/btcscript/script.go isScriptHash 100.00% (1/1)
github.com/conformal/btcscript/log.go UseLogger 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.GetAltStack 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeInvalid 100.00% (1/1)
github.com/conformal/btcscript/script.go parseScript 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeReserved 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDisabled 100.00% (1/1)
github.com/conformal/btcscript/opcode.go init 100.00% (1/1)
github.com/conformal/btcscript/script.go SignatureScript 100.00% (1/1)
github.com/conformal/btcscript/log.go newLogClosure 100.00% (1/1)
github.com/conformal/btcscript/log.go init 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.PushByteArray 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.PushInt 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeRot 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDup 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.GetStack 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.PopByteArray 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDrop 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.SetStack 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode2Swap 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode2Rot 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode2Over 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.SetAltStack 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode3Dup 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.PushInt 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode2Dup 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcode2Drop 100.00% (1/1)
github.com/conformal/btcscript/log.go DisableLog 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeReturn 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeNop 100.00% (1/1)
github.com/conformal/btcscript/stack.go Stack.PushBool 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.subScript 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeInvalid 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.disasm 100.00% (1/1)
github.com/conformal/btcscript/opcode.go calcHash160 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeTuck 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeSwap 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeRot 100.00% (1/1)
github.com/conformal/btcscript/opcode.go calcHash160 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeOver 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeNip 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDup 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeDrop 100.00% (1/1)
github.com/conformal/btcscript/script.go Script.disasm 100.00% (1/1)
github.com/conformal/btcscript/log.go UseLogger 100.00% (1/1)
github.com/conformal/btcscript/log.go init 100.00% (1/1)
github.com/conformal/btcscript/opcode.go opcodeCheckMultiSig 98.21% (55/56)
github.com/conformal/btcscript/opcode.go opcodeCheckSig 96.15% (25/26)
github.com/conformal/btcscript/script.go calcScriptHash 82.05% (32/39)
github.com/conformal/btcscript/script.go Script.CheckErrorCondition 78.57% (11/14)
github.com/conformal/btcscript/opcode.go opcodeCheckSigVerify 75.00% (3/4)
github.com/conformal/btcscript/script.go Script.calcScriptHash 64.10% (25/39)
github.com/conformal/btcscript/script.go Script.Execute 44.44% (8/18)
github.com/conformal/btcscript/log.go SetLogWriter 0.00% (0/7)
github.com/conformal/btcscript/script.go IsPushOnlyScript 0.00% (0/4)
github.com/conformal/btcscript/log.go logClosure.String 0.00% (0/1)
github.com/conformal/btcscript -------------------------- 95.55% (901/943)
github.com/conformal/btcscript --------------------------- 96.44% (949/984)