txscript: Convert to new scriptnum type.

This commit implements a new type, named scriptNum, for handling all
numeric values used in scripts and converts the code over to make use of
it.  This is being done for a few of reasons.

First, the consensus rules for handling numeric values in the scripts
require special handling with subtle semantics.  By encapsulating those
details into a type specifically dedicated to that purpose, it
simplifies the code and generally helps prevent improper usage.

Second, the new type is quite a bit more efficient than big.Ints which
are designed to be arbitrarily large and thus involve a lot of heap
allocations and additional multi-precision bookkeeping.  Because this
new type is based on an int64, it allows the numbers to be stack
allocated thereby eliminating a lot of GC and also eliminates the extra
multi-precision arithmetic bookkeeping.

The use of an int64 is possible because the consensus rules dictate that
when data is interpreted as a number, it is limited to an int32 even
though results outside of this range are allowed so long as they are not
interpreted as integers again themselves.   Thus, the maximum possible
result comes from multiplying a max int32 by itself which safely fits
into an int64 and can then still appropriately provide the serialization
of the larger number as required by consensus.

Finally, it more closely resembles the implementation used by Bitcoin
Core and thus makes is easier to compare the behavior between the two
implementations.

This commit also includes a full suite of tests with 100% coverage of
the semantics of the new type.
This commit is contained in:
Dave Collins 2015-04-29 20:16:00 -05:00
parent 6e402deb35
commit b6e52fbd93
9 changed files with 582 additions and 340 deletions

View file

@ -182,7 +182,8 @@ func mergeTxStore(txStoreA blockchain.TxStore, txStoreB blockchain.TxStore) {
// the extra nonce as well as additional coinbase flags.
func standardCoinbaseScript(nextBlockHeight int64, extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().AddInt64(nextBlockHeight).
AddUint64(extraNonce).AddData([]byte(coinbaseFlags)).Script()
AddInt64(int64(extraNonce)).AddData([]byte(coinbaseFlags)).
Script()
}
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy

View file

@ -548,7 +548,7 @@ 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)
array[len(array)-i-1], _ = stack.PeekByteArray(int32(i))
}
return array
}

View file

@ -10,7 +10,6 @@ import (
"encoding/binary"
"fmt"
"hash"
"math/big"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
@ -855,7 +854,7 @@ func opcodePushData(op *parsedOpcode, vm *Engine) error {
// opcode1Negate pushes -1, encoded as a number, to the data stack.
func opcode1Negate(op *parsedOpcode, vm *Engine) error {
vm.dstack.PushInt(big.NewInt(-1))
vm.dstack.PushInt(scriptNum(-1))
return nil
}
@ -865,7 +864,7 @@ func opcode1Negate(op *parsedOpcode, vm *Engine) error {
func opcodeN(op *parsedOpcode, vm *Engine) error {
// The opcodes are all defined consecutively, so the numeric value is
// the difference.
vm.dstack.PushInt(big.NewInt(int64(op.opcode.value - (OP_1 - 1))))
vm.dstack.PushInt(scriptNum((op.opcode.value - (OP_1 - 1))))
return nil
}
@ -1091,7 +1090,7 @@ func opcodeIfDup(op *parsedOpcode, vm *Engine) error {
}
// Push copy of data iff it isn't zero
if val.Sign() != 0 {
if val != 0 {
vm.dstack.PushInt(val)
}
@ -1105,7 +1104,7 @@ func opcodeIfDup(op *parsedOpcode, vm *Engine) error {
// Example with 2 items: [x1 x2] -> [x1 x2 2]
// Example with 3 items: [x1 x2 x3] -> [x1 x2 x3 3]
func opcodeDepth(op *parsedOpcode, vm *Engine) error {
vm.dstack.PushInt(big.NewInt(int64(vm.dstack.Depth())))
vm.dstack.PushInt(scriptNum(vm.dstack.Depth()))
return nil
}
@ -1144,14 +1143,12 @@ func opcodeOver(op *parsedOpcode, vm *Engine) error {
// Example with n=1: [x2 x1 x0 1] -> [x2 x1 x0 x1]
// Example with n=2: [x2 x1 x0 2] -> [x2 x1 x0 x2]
func opcodePick(op *parsedOpcode, vm *Engine) error {
pidx, err := vm.dstack.PopInt()
val, err := vm.dstack.PopInt()
if err != nil {
return err
}
// PopInt promises that the int returned is 32 bit.
val := int(pidx.Int64())
return vm.dstack.PickN(val)
return vm.dstack.PickN(val.Int32())
}
// opcodeRoll treats the top item on the data stack as an integer and moves
@ -1161,14 +1158,12 @@ func opcodePick(op *parsedOpcode, vm *Engine) error {
// Example with n=1: [x2 x1 x0 1] -> [x2 x0 x1]
// Example with n=2: [x2 x1 x0 2] -> [x1 x0 x2]
func opcodeRoll(op *parsedOpcode, vm *Engine) error {
ridx, err := vm.dstack.PopInt()
val, err := vm.dstack.PopInt()
if err != nil {
return err
}
// PopInt promises that the int returned is 32 bit.
val := int(ridx.Int64())
return vm.dstack.RollN(val)
return vm.dstack.RollN(val.Int32())
}
// opcodeRot rotates the top 3 items on the data stack to the left.
@ -1203,7 +1198,7 @@ func opcodeSize(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(big.NewInt(int64(len(so))))
vm.dstack.PushInt(scriptNum(len(so)))
return nil
}
@ -1250,7 +1245,7 @@ func opcode1Add(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(new(big.Int).Add(m, big.NewInt(1)))
vm.dstack.PushInt(m + 1)
return nil
}
@ -1263,7 +1258,7 @@ func opcode1Sub(op *parsedOpcode, vm *Engine) error {
if err != nil {
return err
}
vm.dstack.PushInt(new(big.Int).Sub(m, big.NewInt(1)))
vm.dstack.PushInt(m - 1)
return nil
}
@ -1278,7 +1273,7 @@ func opcodeNegate(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(new(big.Int).Neg(m))
vm.dstack.PushInt(-m)
return nil
}
@ -1292,7 +1287,10 @@ func opcodeAbs(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(new(big.Int).Abs(m))
if m < 0 {
m = -m
}
vm.dstack.PushInt(m)
return nil
}
@ -1314,10 +1312,10 @@ func opcodeNot(op *parsedOpcode, vm *Engine) error {
return err
}
if m.Sign() == 0 {
vm.dstack.PushInt(big.NewInt(1))
if m == 0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
}
@ -1334,8 +1332,8 @@ func opcode0NotEqual(op *parsedOpcode, vm *Engine) error {
return err
}
if m.Sign() != 0 {
m.SetInt64(1)
if m != 0 {
m = 1
}
vm.dstack.PushInt(m)
return nil
@ -1356,7 +1354,7 @@ func opcodeAdd(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(new(big.Int).Add(v0, v1))
vm.dstack.PushInt(v0 + v1)
return nil
}
@ -1376,7 +1374,7 @@ func opcodeSub(op *parsedOpcode, vm *Engine) error {
return err
}
vm.dstack.PushInt(new(big.Int).Sub(v1, v0))
vm.dstack.PushInt(v1 - v0)
return nil
}
@ -1398,10 +1396,10 @@ func opcodeBoolAnd(op *parsedOpcode, vm *Engine) error {
return err
}
if v0.Sign() != 0 && v1.Sign() != 0 {
vm.dstack.PushInt(big.NewInt(1))
if v0 != 0 && v1 != 0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1425,10 +1423,10 @@ func opcodeBoolOr(op *parsedOpcode, vm *Engine) error {
return err
}
if v0.Sign() != 0 || v1.Sign() != 0 {
vm.dstack.PushInt(big.NewInt(1))
if v0 != 0 || v1 != 0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1450,10 +1448,10 @@ func opcodeNumEqual(op *parsedOpcode, vm *Engine) error {
return err
}
if v0.Cmp(v1) == 0 {
vm.dstack.PushInt(big.NewInt(1))
if v0 == v1 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1491,10 +1489,10 @@ func opcodeNumNotEqual(op *parsedOpcode, vm *Engine) error {
return err
}
if v0.Cmp(v1) != 0 {
vm.dstack.PushInt(big.NewInt(1))
if v0 != v1 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1516,10 +1514,10 @@ func opcodeLessThan(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) == -1 {
vm.dstack.PushInt(big.NewInt(1))
if v1 < v0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1541,10 +1539,10 @@ func opcodeGreaterThan(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) == 1 {
vm.dstack.PushInt(big.NewInt(1))
if v1 > v0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
}
@ -1565,10 +1563,10 @@ func opcodeLessThanOrEqual(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) <= 0 {
vm.dstack.PushInt(big.NewInt(1))
if v1 <= v0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
}
@ -1589,10 +1587,10 @@ func opcodeGreaterThanOrEqual(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) >= 0 {
vm.dstack.PushInt(big.NewInt(1))
if v1 >= v0 {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
@ -1613,10 +1611,10 @@ func opcodeMin(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) == -1 {
vm.dstack.PushInt(new(big.Int).Set(v1))
if v1 < v0 {
vm.dstack.PushInt(v1)
} else {
vm.dstack.PushInt(new(big.Int).Set(v0))
vm.dstack.PushInt(v0)
}
return nil
@ -1637,10 +1635,10 @@ func opcodeMax(op *parsedOpcode, vm *Engine) error {
return err
}
if v1.Cmp(v0) == 1 {
vm.dstack.PushInt(new(big.Int).Set(v1))
if v1 > v0 {
vm.dstack.PushInt(v1)
} else {
vm.dstack.PushInt(new(big.Int).Set(v0))
vm.dstack.PushInt(v0)
}
return nil
@ -1670,10 +1668,10 @@ func opcodeWithin(op *parsedOpcode, vm *Engine) error {
return err
}
if x.Cmp(minVal) >= 0 && x.Cmp(maxVal) == -1 {
vm.dstack.PushInt(big.NewInt(1))
if x >= minVal && x < maxVal {
vm.dstack.PushInt(scriptNum(1))
} else {
vm.dstack.PushInt(big.NewInt(0))
vm.dstack.PushInt(scriptNum(0))
}
return nil
}
@ -1904,8 +1902,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
return err
}
// PopInt promises that the int returned is 32 bit.
numPubKeys := int(numKeys.Int64())
numPubKeys := int(numKeys.Int32())
if numPubKeys < 0 || numPubKeys > MaxPubKeysPerMultiSig {
return ErrStackTooManyPubkeys
}
@ -1927,8 +1924,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
if err != nil {
return err
}
// PopInt promises that the int returned is 32 bit.
numSignatures := int(numSigs.Int64())
numSignatures := int(numSigs.Int32())
if numSignatures < 0 {
return fmt.Errorf("number of signatures '%d' is less than 0",
numSignatures)

View file

@ -7,7 +7,6 @@ package txscript
import (
"encoding/binary"
"fmt"
"math/big"
)
const (
@ -228,38 +227,7 @@ func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
return b
}
return b.AddData(fromInt(new(big.Int).SetInt64(val)))
}
// AddUint64 pushes the passed integer to the end of the script. The script
// will not be modified if pushing the data would cause the script to
// exceed the maximum allowed script engine size.
func (b *ScriptBuilder) AddUint64(val uint64) *ScriptBuilder {
if b.err != nil {
return b
}
// Pushes that would cause the script to exceed the largest allowed
// script size would result in a non-canonical script.
if len(b.script)+1 > maxScriptSize {
str := fmt.Sprintf("adding an unsigned integer would exceed "+
"the maximum allow canonical script length of %d",
maxScriptSize)
b.err = ErrScriptNotCanonical(str)
return b
}
// Fast path for small integers.
if val == 0 {
b.script = append(b.script, OP_0)
return b
}
if val >= 1 && val <= 16 {
b.script = append(b.script, byte((OP_1-1)+val))
return b
}
return b.AddData(fromInt(new(big.Int).SetUint64(val)))
return b.AddData(scriptNum(val).Bytes())
}
// Reset resets the script so it has no content.

View file

@ -129,62 +129,6 @@ func TestScriptBuilderAddInt64(t *testing.T) {
}
}
// TestScriptBuilderAddUint64 tests that pushing unsigned integers to a script
// via the ScriptBuilder API works as expected.
func TestScriptBuilderAddUint64(t *testing.T) {
t.Parallel()
tests := []struct {
name string
val uint64
expected []byte
}{
{name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}},
{name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}},
{name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}},
{name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}},
{name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}},
{name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}},
{name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}},
{name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}},
{name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}},
{name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}},
{name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}},
{name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}},
{name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}},
{name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}},
{name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}},
{name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}},
{name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}},
{name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}},
{name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}},
{name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}},
{name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}},
{name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}},
{name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}},
{name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}},
{name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}},
}
builder := txscript.NewScriptBuilder()
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
builder.Reset().AddUint64(test.val)
result, err := builder.Script()
if err != nil {
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) unexpected "+
"error: %v", i, test.name, err)
continue
}
if !bytes.Equal(result, test.expected) {
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) wrong result\n"+
"got: %x\nwant: %x", i, test.name, result,
test.expected)
continue
}
}
}
// TestScriptBuilderAddData tests that pushing data to a script via the
// ScriptBuilder API works as expected and conforms to BIP0062.
func TestScriptBuilderAddData(t *testing.T) {
@ -373,19 +317,6 @@ func TestExceedMaxScriptSize(t *testing.T) {
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
"got len %d, want len %d", len(script), len(origScript))
}
// Ensure adding an unsigned integer that would exceed the maximum size
// of the script does not add the data.
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
script, err = builder.AddUint64(0).Script()
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
"got len %d, want len %d", len(script), len(origScript))
}
if !bytes.Equal(script, origScript) {
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
"got len %d, want len %d", len(script), len(origScript))
}
}
// TestErroredScript ensures that all of the functions that can be used to add
@ -457,17 +388,6 @@ func TestErroredScript(t *testing.T) {
"got len %d, want len %d", len(script), len(origScript))
}
// Ensure adding an unsigned integer to a script that has errored
// doesn't succeed.
script, err = builder.AddUint64(0).Script()
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
t.Fatal("ScriptBuilder.AddUint64 succeeded on errored script")
}
if !bytes.Equal(script, origScript) {
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
"got len %d, want len %d", len(script), len(origScript))
}
// Ensure the error has a message set.
if err.Error() == "" {
t.Fatal("ErrScriptNotCanonical.Error does not have any text")

205
txscript/scriptnum.go Normal file
View file

@ -0,0 +1,205 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
const (
maxInt32 = 1<<31 - 1
minInt32 = -1 << 31
// maxScriptNumLen is the maximum number of bytes data being interpreted
// as an integer may be.
maxScriptNumLen = 4
)
// scriptNum represents a numeric value used in the scripting engine with
// special handling to deal with the subtle semantics required by consensus.
//
// All numbers are stored on the data and alternate stacks encoded as little
// endian with a sign bit. All numeric opcodes such as OP_ADD, OP_SUB,
// and OP_MUL, are only allowed to operate on 4-byte integers in the range
// [-2^31 + 1, 2^31 - 1], however the results of numeric operations may overflow
// and remain valid so long as they are not used as inputs to other numeric
// operations or otherwise interpreted as an integer.
//
// For example, it is possible for OP_ADD to have 2^31 - 1 for its two operands
// resulting 2^32 - 2, which overflows, but is still pushed to the stack as the
// result of the addition. That value can then be used as input to OP_VERIFY
// which will succeed because the data is being interpreted as a boolean.
// However, if that same value were to be used as input to another numeric
// opcode, such as OP_SUB, it must fail.
//
// This type handles the aforementioned requirements by storing all numeric
// operation results as an int64 to handle overflow and provides the Bytes
// method to get the serialized representation (including values that overflow).
//
// Then, whenever data is interpreted as an integer, it is converted to this
// type by using the makeScriptNum function which will return an error if the
// number is out of range (or not minimally encoded depending on a flag). Since
// all numeric opcodes involve pulling data from the stack and interpreting it
// as an integer, it provides the required behavior.
type scriptNum int64
// checkMinimalDataEncoding returns whether or not the passed byte array adheres
// to the minimal encoding requirements.
func checkMinimalDataEncoding(v []byte) error {
if len(v) == 0 {
return nil
}
// Check that the number is encoded with the minimum possible
// number of bytes.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, [0x80].
if v[len(v)-1]&0x7f == 0 {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if len(v) == 1 || v[len(v)-2]&0x80 == 0 {
return ErrStackMinimalData
}
}
return nil
}
// Bytes returns the number serialized as a little endian with a sign bit.
//
// Example encodings:
// 127 -> [0x7f]
// -127 -> [0xff]
// 128 -> [0x80 0x00]
// -128 -> [0x80 0x80]
// 129 -> [0x81 0x00]
// -129 -> [0x81 0x80]
// 256 -> [0x00 0x01]
// -256 -> [0x00 0x81]
// 32767 -> [0xff 0x7f]
// -32767 -> [0xff 0xff]
// 32768 -> [0x00 0x80 0x00]
// -32768 -> [0x00 0x80 0x80]
func (n scriptNum) Bytes() []byte {
// Zero encodes as an empty byte slice.
if n == 0 {
return nil
}
// Take the absolute value and keep track of whether it was originally
// negative.
isNegative := n < 0
if isNegative {
n = -n
}
// Encode to little endian. The maximum number of encoded bytes is 9
// (8 bytes for max int64 plus a potential byte for sign extension).
result := make([]byte, 0, 9)
for n > 0 {
result = append(result, byte(n&0xff))
n >>= 8
}
// When the most significant byte already has the high bit set, an
// additional high byte is required to indicate whether the number is
// negative or positive. The additional byte is removed when converting
// back to an integral and its high bit is used to denote the sign.
//
// Otherwise, when the most significant byte does not already have the
// high bit set, use it to indicate the value is negative, if needed.
if result[len(result)-1]&0x80 != 0 {
extraByte := byte(0x00)
if isNegative {
extraByte = 0x80
}
result = append(result, extraByte)
} else if isNegative {
result[len(result)-1] |= 0x80
}
return result
}
// Int32 returns the script number clamped to a valid int32. That is to say
// when the script number is higher than the max allowed int32, the max int32
// value is returned and vice versa for the minimum value. Note that this
// behavior is different from a simple int32 cast because that truncates
// and the consensus rules dictate numbers which are directly cast to ints
// provide this behavior.
//
// In practice, the number should never really be out of range since it will
// have been created with makeScriptNum which rejects them, but in case
// something in the future ends up calling this function against the result
// of some arithmetic, which IS allowed to be out of range before being
// reinterpreted as an integer, this will provide the correct behavior.
func (n scriptNum) Int32() int32 {
if n > maxInt32 {
return maxInt32
}
if n < minInt32 {
return minInt32
}
return int32(n)
}
// makeScriptNum interprets the passed serialized bytes as an encoded integer
// and returns the result as a script number.
//
// Since the consensus rules dictate the serialized bytes interpreted as ints
// are only allowed to be in the range [-2^31 + 1, 2^31 - 1], an error will be
// returned when the provided bytes would result in a number outside of that
// range.
//
// 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.
//
// See the Bytes function documentation for example encodings.
func makeScriptNum(v []byte, requireMinimal bool) (scriptNum, error) {
// Interpreting data as an integer requires that it is not larger than
// a 32-bit integer.
if len(v) > maxScriptNumLen {
return 0, ErrStackNumberTooBig
}
// Enforce minimal encoded if requested.
if requireMinimal {
if err := checkMinimalDataEncoding(v); err != nil {
return 0, err
}
}
// Zero is encoded as an empty byte slice.
if len(v) == 0 {
return 0, nil
}
// Decode from little endian.
var result int64
for i, val := range v {
result |= int64(val) << uint8(8*i)
}
// When the most significant byte of the input bytes has the sign bit
// set, the result is negative. So, remove the sign bit from the result
// and make it negative.
if v[len(v)-1]&0x80 != 0 {
// The maximum length of v has already been determined to be 4
// above, so uint8 is enough to cover the max possible shift
// value of 24.
result &= ^(int64(0x80) << uint8(8*(len(v)-1)))
return scriptNum(-result), nil
}
return scriptNum(result), nil
}

256
txscript/scriptnum_test.go Normal file
View file

@ -0,0 +1,256 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
import (
"bytes"
"encoding/hex"
"testing"
)
// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
// TestScriptNumBytes ensures that converting from integral script numbers to
// byte representations works as expected.
func TestScriptNumBytes(t *testing.T) {
t.Parallel()
tests := []struct {
num scriptNum
serialized []byte
}{
{0, nil},
{1, hexToBytes("01")},
{-1, hexToBytes("81")},
{127, hexToBytes("7f")},
{-127, hexToBytes("ff")},
{128, hexToBytes("8000")},
{-128, hexToBytes("8080")},
{129, hexToBytes("8100")},
{-129, hexToBytes("8180")},
{256, hexToBytes("0001")},
{-256, hexToBytes("0081")},
{32767, hexToBytes("ff7f")},
{-32767, hexToBytes("ffff")},
{32768, hexToBytes("008000")},
{-32768, hexToBytes("008080")},
{65535, hexToBytes("ffff00")},
{-65535, hexToBytes("ffff80")},
{524288, hexToBytes("000008")},
{-524288, hexToBytes("000088")},
{7340032, hexToBytes("000070")},
{-7340032, hexToBytes("0000f0")},
{8388608, hexToBytes("00008000")},
{-8388608, hexToBytes("00008080")},
{2147483647, hexToBytes("ffffff7f")},
{-2147483647, hexToBytes("ffffffff")},
// Values that are out of range for data that is interpreted as
// numbers, but are allowed as the result of numeric operations.
{2147483648, hexToBytes("0000008000")},
{-2147483648, hexToBytes("0000008080")},
{2415919104, hexToBytes("0000009000")},
{-2415919104, hexToBytes("0000009080")},
{4294967295, hexToBytes("ffffffff00")},
{-4294967295, hexToBytes("ffffffff80")},
{4294967296, hexToBytes("0000000001")},
{-4294967296, hexToBytes("0000000081")},
{281474976710655, hexToBytes("ffffffffffff00")},
{-281474976710655, hexToBytes("ffffffffffff80")},
{72057594037927935, hexToBytes("ffffffffffffff00")},
{-72057594037927935, hexToBytes("ffffffffffffff80")},
{9223372036854775807, hexToBytes("ffffffffffffff7f")},
{-9223372036854775807, hexToBytes("ffffffffffffffff")},
}
for _, test := range tests {
gotBytes := test.num.Bytes()
if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("Bytes: did not get expected bytes for %d - "+
"got %x, want %x", test.num, gotBytes,
test.serialized)
continue
}
}
}
// TestMakeScriptNum ensures that converting from byte representations to
// integral script numbers works as expected.
func TestMakeScriptNum(t *testing.T) {
t.Parallel()
tests := []struct {
serialized []byte
num scriptNum
minimalEncoding bool
err error
}{
// Minimal encoding must reject negative 0.
{hexToBytes("80"), 0, true, ErrStackMinimalData},
// Minimally encoded valid values with minimal encoding flag.
// Should not error and return expected integral number.
{nil, 0, true, nil},
{hexToBytes("01"), 1, true, nil},
{hexToBytes("81"), -1, true, nil},
{hexToBytes("7f"), 127, true, nil},
{hexToBytes("ff"), -127, true, nil},
{hexToBytes("8000"), 128, true, nil},
{hexToBytes("8080"), -128, true, nil},
{hexToBytes("8100"), 129, true, nil},
{hexToBytes("8180"), -129, true, nil},
{hexToBytes("0001"), 256, true, nil},
{hexToBytes("0081"), -256, true, nil},
{hexToBytes("ff7f"), 32767, true, nil},
{hexToBytes("ffff"), -32767, true, nil},
{hexToBytes("008000"), 32768, true, nil},
{hexToBytes("008080"), -32768, true, nil},
{hexToBytes("ffff00"), 65535, true, nil},
{hexToBytes("ffff80"), -65535, true, nil},
{hexToBytes("000008"), 524288, true, nil},
{hexToBytes("000088"), -524288, true, nil},
{hexToBytes("000070"), 7340032, true, nil},
{hexToBytes("0000f0"), -7340032, true, nil},
{hexToBytes("00008000"), 8388608, true, nil},
{hexToBytes("00008080"), -8388608, true, nil},
{hexToBytes("ffffff7f"), 2147483647, true, nil},
{hexToBytes("ffffffff"), -2147483647, true, nil},
// Minimally encoded values that are out of range for data that
// is interpreted as script numbers with the minimal encoding
// flag set. Should error and return 0.
{hexToBytes("0000008000"), 0, true, ErrStackNumberTooBig},
{hexToBytes("0000008080"), 0, true, ErrStackNumberTooBig},
{hexToBytes("0000009000"), 0, true, ErrStackNumberTooBig},
{hexToBytes("0000009080"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffff00"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffff80"), 0, true, ErrStackNumberTooBig},
{hexToBytes("0000000001"), 0, true, ErrStackNumberTooBig},
{hexToBytes("0000000081"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffff00"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffff80"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffffff00"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffffff80"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffffff7f"), 0, true, ErrStackNumberTooBig},
{hexToBytes("ffffffffffffffff"), 0, true, ErrStackNumberTooBig},
// Non-minimally encoded, but otherwise valid values with
// minimal encoding flag. Should error and return 0.
{hexToBytes("00"), 0, true, ErrStackMinimalData}, // 0
{hexToBytes("0100"), 0, true, ErrStackMinimalData}, // 1
{hexToBytes("7f00"), 0, true, ErrStackMinimalData}, // 127
{hexToBytes("800000"), 0, true, ErrStackMinimalData}, // 128
{hexToBytes("810000"), 0, true, ErrStackMinimalData}, // 129
{hexToBytes("000100"), 0, true, ErrStackMinimalData}, // 256
{hexToBytes("ff7f00"), 0, true, ErrStackMinimalData}, // 32767
{hexToBytes("00800000"), 0, true, ErrStackMinimalData}, // 32768
{hexToBytes("ffff0000"), 0, true, ErrStackMinimalData}, // 65535
{hexToBytes("00000800"), 0, true, ErrStackMinimalData}, // 524288
{hexToBytes("00007000"), 0, true, ErrStackMinimalData}, // 7340032
// Non-minimally encoded, but otherwise valid values without
// minimal encoding flag. Should not error and return expected
// integral number.
{hexToBytes("00"), 0, false, nil},
{hexToBytes("0100"), 1, false, nil},
{hexToBytes("7f00"), 127, false, nil},
{hexToBytes("800000"), 128, false, nil},
{hexToBytes("810000"), 129, false, nil},
{hexToBytes("000100"), 256, false, nil},
{hexToBytes("ff7f00"), 32767, false, nil},
{hexToBytes("00800000"), 32768, false, nil},
{hexToBytes("ffff0000"), 65535, false, nil},
{hexToBytes("00000800"), 524288, false, nil},
{hexToBytes("00007000"), 7340032, false, nil},
}
for _, test := range tests {
gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding)
if err != test.err {
t.Errorf("makeScriptNum: did not received expected "+
"error for %x - got %v, want %v",
test.serialized, err, test.err)
continue
}
if gotNum != test.num {
t.Errorf("makeScriptNum: did not get expected number "+
"for %x - got %d, want %d", test.serialized,
gotNum, test.num)
continue
}
}
}
// TestScriptNumInt32 ensures that the Int32 function on script number behaves
// as expected.
func TestScriptNumInt32(t *testing.T) {
t.Parallel()
tests := []struct {
in scriptNum
want int32
}{
// Values inside the valid int32 range are just the values
// themselves cast to an int32.
{0, 0},
{1, 1},
{-1, -1},
{127, 127},
{-127, -127},
{128, 128},
{-128, -128},
{129, 129},
{-129, -129},
{256, 256},
{-256, -256},
{32767, 32767},
{-32767, -32767},
{32768, 32768},
{-32768, -32768},
{65535, 65535},
{-65535, -65535},
{524288, 524288},
{-524288, -524288},
{7340032, 7340032},
{-7340032, -7340032},
{8388608, 8388608},
{-8388608, -8388608},
{2147483647, 2147483647},
{-2147483647, -2147483647},
{-2147483648, -2147483648},
// Values outside of the valid int32 range are limited to int32.
{2147483648, 2147483647},
{-2147483649, -2147483648},
{1152921504606846975, 2147483647},
{-1152921504606846975, -2147483648},
{2305843009213693951, 2147483647},
{-2305843009213693951, -2147483648},
{4611686018427387903, 2147483647},
{-4611686018427387903, -2147483648},
{9223372036854775807, 2147483647},
{-9223372036854775808, -2147483648},
}
for _, test := range tests {
got := test.in.Int32()
if got != test.want {
t.Errorf("Int32: did not get expected value for %d - "+
"got %d, want %d", test.in, got, test.want)
continue
}
}
}

View file

@ -4,79 +4,7 @@
package txscript
import (
"encoding/hex"
"math/big"
)
// asInt converts a byte array to a bignum by treating it as a little endian
// number with sign bit.
func asInt(v []byte) (*big.Int, error) {
// Only 32bit numbers allowed.
if len(v) > 4 {
return nil, ErrStackNumberTooBig
}
if len(v) == 0 {
return big.NewInt(0), nil
}
negative := false
origlen := len(v)
msb := v[len(v)-1]
if msb&0x80 == 0x80 {
negative = true
// remove sign bit
msb &= 0x7f
}
// trim leading 0 bytes
for ; msb == 0; msb = v[len(v)-1] {
v = v[:len(v)-1]
if len(v) == 0 {
break
}
}
// reverse bytes with a copy since stack is immutable.
intArray := make([]byte, len(v))
for i := range v {
intArray[len(v)-i-1] = v[i]
}
// IFF the value is negative and no 0 bytes were trimmed,
// the leading byte needs to be sign corrected
if negative && len(intArray) == origlen {
intArray[0] &= 0x7f
}
num := new(big.Int).SetBytes(intArray)
if negative {
num = num.Neg(num)
}
return num, nil
}
// fromInt provies a Big.Int in little endian format with the high bit of the
// msb donating sign.
func fromInt(v *big.Int) []byte {
negative := false
if v.Sign() == -1 {
negative = true
}
// Int.Bytes() trims leading zeros for us, so we don't have to.
b := v.Bytes()
if len(b) == 0 {
return []byte{}
}
arr := make([]byte, len(b))
for i := range b {
arr[len(b)-i-1] = b[i]
}
// if would otherwise be negative, add a zero byte
if arr[len(arr)-1]&0x80 == 0x80 {
arr = append(arr, 0)
}
if negative {
arr[len(arr)-1] |= 0x80
}
return arr
}
import "encoding/hex"
// asBool gets the boolean value of the byte array.
func asBool(t []byte) bool {
@ -105,35 +33,9 @@ type stack struct {
verifyMinimalData bool
}
// checkMinimalData returns whether or not the passed byte array adheres to
// the minimal encoding requirements, if enabled.
func (s *stack) checkMinimalData(so []byte) error {
if !s.verifyMinimalData || len(so) == 0 {
return nil
}
// Check that the number is encoded with the minimum possible
// number of bytes.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if so[len(so)-1]&0x7f == 0 {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if len(so) == 1 || so[len(so)-2]&0x80 == 0 {
return ErrStackMinimalData
}
}
return nil
}
// Depth returns the number of items on the stack.
func (s *stack) Depth() int {
return len(s.stk)
func (s *stack) Depth() int32 {
return int32(len(s.stk))
}
// PushByteArray adds the given back array to the top of the stack.
@ -143,12 +45,12 @@ func (s *stack) PushByteArray(so []byte) {
s.stk = append(s.stk, so)
}
// PushInt converts the provided bignum to a suitable byte array then pushes
// PushInt converts the provided scriptNum to a suitable byte array then pushes
// it onto the top of the stack.
//
// Stack transformation: [... x1 x2] -> [... x1 x2 int]
func (s *stack) PushInt(val *big.Int) {
s.PushByteArray(fromInt(val))
func (s *stack) PushInt(val scriptNum) {
s.PushByteArray(val.Bytes())
}
// PushBool converts the provided boolean to a suitable byte array then pushes
@ -166,21 +68,18 @@ func (s *stack) PopByteArray() ([]byte, error) {
return s.nipN(0)
}
// PopInt pops the value off the top of the stack, converts it into a bignum and
// returns it.
// PopInt pops the value off the top of the stack, converts it into a script
// num, and returns it. The act of converting to a script num enforces the
// consensus rules imposed on data interpreted as numbers.
//
// Stack transformation: [... x1 x2 x3] -> [... x1 x2]
func (s *stack) PopInt() (*big.Int, error) {
func (s *stack) PopInt() (scriptNum, error) {
so, err := s.PopByteArray()
if err != nil {
return nil, err
return 0, err
}
if err := s.checkMinimalData(so); err != nil {
return nil, err
}
return asInt(so)
return makeScriptNum(so, s.verifyMinimalData)
}
// PopBool pops the value off the top of the stack, converts it into a bool, and
@ -196,9 +95,9 @@ func (s *stack) PopBool() (bool, error) {
return asBool(so), nil
}
// PeekByteArray returns the nth item on the stack without removing it.
func (s *stack) PeekByteArray(idx int) ([]byte, error) {
sz := len(s.stk)
// PeekByteArray returns the Nth item on the stack without removing it.
func (s *stack) PeekByteArray(idx int32) ([]byte, error) {
sz := int32(len(s.stk))
if idx < 0 || idx >= sz {
return nil, ErrStackUnderflow
}
@ -206,22 +105,20 @@ func (s *stack) PeekByteArray(idx int) ([]byte, error) {
return s.stk[sz-idx-1], nil
}
// PeekInt returns the Nth item on the stack as a bignum without removing it.
func (s *stack) PeekInt(idx int) (*big.Int, error) {
// PeekInt returns the Nth item on the stack as a script num without removing
// it. The act of converting to a script num enforces the consensus rules
// imposed on data interpreted as numbers.
func (s *stack) PeekInt(idx int32) (scriptNum, error) {
so, err := s.PeekByteArray(idx)
if err != nil {
return nil, err
return 0, err
}
if err := s.checkMinimalData(so); err != nil {
return nil, err
}
return asInt(so)
return makeScriptNum(so, s.verifyMinimalData)
}
// PeekBool returns the Nth item on the stack as a bool without removing it.
func (s *stack) PeekBool(idx int) (i bool, err error) {
func (s *stack) PeekBool(idx int32) (bool, error) {
so, err := s.PeekByteArray(idx)
if err != nil {
return false, err
@ -237,8 +134,8 @@ func (s *stack) PeekBool(idx int) (i bool, err error) {
// nipN(0): [... x1 x2 x3] -> [... x1 x2]
// nipN(1): [... x1 x2 x3] -> [... x1 x3]
// nipN(2): [... x1 x2 x3] -> [... x2 x3]
func (s *stack) nipN(idx int) ([]byte, error) {
sz := len(s.stk)
func (s *stack) nipN(idx int32) ([]byte, error) {
sz := int32(len(s.stk))
if idx < 0 || idx > sz-1 {
return nil, ErrStackUnderflow
}
@ -264,7 +161,7 @@ func (s *stack) nipN(idx int) ([]byte, error) {
// NipN(0): [... x1 x2 x3] -> [... x1 x2]
// NipN(1): [... x1 x2 x3] -> [... x1 x3]
// NipN(2): [... x1 x2 x3] -> [... x2 x3]
func (s *stack) NipN(idx int) error {
func (s *stack) NipN(idx int32) error {
_, err := s.nipN(idx)
return err
}
@ -294,7 +191,7 @@ func (s *stack) Tuck() error {
// Stack transformation:
// DropN(1): [... x1 x2] -> [... x1]
// DropN(2): [... x1 x2] -> [...]
func (s *stack) DropN(n int) error {
func (s *stack) DropN(n int32) error {
if n < 1 {
return ErrStackInvalidArgs
}
@ -313,7 +210,7 @@ func (s *stack) DropN(n int) error {
// Stack transformation:
// DupN(1): [... x1 x2] -> [... x1 x2 x2]
// DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
func (s *stack) DupN(n int) error {
func (s *stack) DupN(n int32) error {
if n < 1 {
return ErrStackInvalidArgs
}
@ -335,7 +232,7 @@ func (s *stack) DupN(n int) error {
// Stack transformation:
// RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
// RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
func (s *stack) RotN(n int) error {
func (s *stack) RotN(n int32) error {
if n < 1 {
return ErrStackInvalidArgs
}
@ -359,7 +256,7 @@ func (s *stack) RotN(n int) error {
// Stack transformation:
// SwapN(1): [... x1 x2] -> [... x2 x1]
// SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
func (s *stack) SwapN(n int) error {
func (s *stack) SwapN(n int32) error {
if n < 1 {
return ErrStackInvalidArgs
}
@ -382,7 +279,7 @@ func (s *stack) SwapN(n int) error {
// Stack transformation:
// OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
// OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
func (s *stack) OverN(n int) error {
func (s *stack) OverN(n int32) error {
if n < 1 {
return ErrStackInvalidArgs
}
@ -406,7 +303,7 @@ func (s *stack) OverN(n int) error {
// PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
// PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
// PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
func (s *stack) PickN(n int) error {
func (s *stack) PickN(n int32) error {
so, err := s.PeekByteArray(n)
if err != nil {
return err
@ -422,7 +319,7 @@ func (s *stack) PickN(n int) error {
// RollN(0): [x1 x2 x3] -> [x1 x2 x3]
// RollN(1): [x1 x2 x3] -> [x1 x3 x2]
// RollN(2): [x1 x2 x3] -> [x2 x3 x1]
func (s *stack) RollN(n int) error {
func (s *stack) RollN(n int32) error {
so, err := s.nipN(n)
if err != nil {
return err

View file

@ -8,7 +8,6 @@ import (
"bytes"
"errors"
"fmt"
"math/big"
"testing"
)
@ -180,7 +179,7 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Sign() != 0 {
if v != 0 {
return errors.New("0 != 0 on popInt")
}
return nil
@ -196,7 +195,7 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Sign() != 0 {
if v != 0 {
return errors.New("-0 != 0 on popInt")
}
return nil
@ -212,7 +211,7 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(1)) != 0 {
if v != 1 {
return errors.New("1 != 1 on popInt")
}
return nil
@ -228,8 +227,8 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(1)) != 0 {
fmt.Printf("%v != %v\n", v, big.NewInt(1))
if v != 1 {
fmt.Printf("%v != %v\n", v, 1)
return errors.New("1 != 1 on popInt")
}
return nil
@ -245,8 +244,8 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(-1)) != 0 {
return errors.New("1 != 1 on popInt")
if v != -1 {
return errors.New("-1 != -1 on popInt")
}
return nil
},
@ -261,8 +260,8 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(-1)) != 0 {
fmt.Printf("%v != %v\n", v, big.NewInt(-1))
if v != -1 {
fmt.Printf("%v != %v\n", v, -1)
return errors.New("-1 != -1 on popInt")
}
return nil
@ -279,8 +278,8 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(-513)) != 0 {
fmt.Printf("%v != %v\n", v, big.NewInt(-513))
if v != -513 {
fmt.Printf("%v != %v\n", v, -513)
return errors.New("1 != 1 on popInt")
}
return nil
@ -297,8 +296,8 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if v.Cmp(big.NewInt(-1)) != 0 {
fmt.Printf("%v != %v\n", v, big.NewInt(-1))
if v != -1 {
fmt.Printf("%v != %v\n", v, -1)
return errors.New("-1 != -1 on popInt")
}
return nil
@ -310,7 +309,7 @@ func TestStack(t *testing.T) {
"PushInt 0",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(0))
s.PushInt(scriptNum(0))
return nil
},
nil,
@ -320,7 +319,7 @@ func TestStack(t *testing.T) {
"PushInt 1",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(1))
s.PushInt(scriptNum(1))
return nil
},
nil,
@ -330,7 +329,7 @@ func TestStack(t *testing.T) {
"PushInt -1",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(-1))
s.PushInt(scriptNum(-1))
return nil
},
nil,
@ -340,7 +339,7 @@ func TestStack(t *testing.T) {
"PushInt two bytes",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(256))
s.PushInt(scriptNum(256))
return nil
},
nil,
@ -352,7 +351,7 @@ func TestStack(t *testing.T) {
[][]byte{},
func(s *stack) error {
// this will have the highbit set
s.PushInt(big.NewInt(128))
s.PushInt(scriptNum(128))
return nil
},
nil,
@ -518,7 +517,7 @@ func TestStack(t *testing.T) {
"PushInt PopBool",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(1))
s.PushInt(scriptNum(1))
val, err := s.PopBool()
if err != nil {
return err
@ -536,7 +535,7 @@ func TestStack(t *testing.T) {
"PushInt PopBool 2",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(0))
s.PushInt(scriptNum(0))
val, err := s.PopBool()
if err != nil {
return err
@ -554,7 +553,7 @@ func TestStack(t *testing.T) {
"PushInt PopBool 2",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(0))
s.PushInt(scriptNum(0))
val, err := s.PopBool()
if err != nil {
return err
@ -894,7 +893,7 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if val.Cmp(big.NewInt(1)) != 0 {
if val != 1 {
return errors.New("invalid result")
}
return nil
@ -912,7 +911,7 @@ func TestStack(t *testing.T) {
if err != nil {
return err
}
if val.Cmp(big.NewInt(0)) != 0 {
if val != 0 {
return errors.New("invalid result")
}
return nil
@ -924,14 +923,14 @@ func TestStack(t *testing.T) {
"pop int",
[][]byte{},
func(s *stack) error {
s.PushInt(big.NewInt(1))
s.PushInt(scriptNum(1))
// Peek int is otherwise pretty well tested,
// just check it works.
val, err := s.PopInt()
if err != nil {
return err
}
if val.Cmp(big.NewInt(1)) != 0 {
if val != 1 {
return errors.New("invalid result")
}
return nil
@ -968,14 +967,14 @@ func TestStack(t *testing.T) {
continue
}
if len(test.after) != s.Depth() {
if int32(len(test.after)) != s.Depth() {
t.Errorf("%s: stack depth doesn't match expected: %v "+
"vs %v", test.name, len(test.after),
s.Depth())
}
for i := range test.after {
val, err := s.PeekByteArray(s.Depth() - i - 1)
val, err := s.PeekByteArray(s.Depth() - int32(i) - 1)
if err != nil {
t.Errorf("%s: can't peek %dth stack entry: %v",
test.name, i, err)