diff --git a/mining.go b/mining.go index 178cae3f..4af91138 100644 --- a/mining.go +++ b/mining.go @@ -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 diff --git a/txscript/engine.go b/txscript/engine.go index aa62cc16..0decebc2 100644 --- a/txscript/engine.go +++ b/txscript/engine.go @@ -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 } diff --git a/txscript/opcode.go b/txscript/opcode.go index ffecbf59..8245151b 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -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) diff --git a/txscript/scriptbuilder.go b/txscript/scriptbuilder.go index c32304cc..bf5e91f7 100644 --- a/txscript/scriptbuilder.go +++ b/txscript/scriptbuilder.go @@ -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. diff --git a/txscript/scriptbuilder_test.go b/txscript/scriptbuilder_test.go index 2e099f05..b3f1cd75 100644 --- a/txscript/scriptbuilder_test.go +++ b/txscript/scriptbuilder_test.go @@ -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") diff --git a/txscript/scriptnum.go b/txscript/scriptnum.go new file mode 100644 index 00000000..c901fe2c --- /dev/null +++ b/txscript/scriptnum.go @@ -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 +} diff --git a/txscript/scriptnum_test.go b/txscript/scriptnum_test.go new file mode 100644 index 00000000..5bdd4a3a --- /dev/null +++ b/txscript/scriptnum_test.go @@ -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 + } + } +} diff --git a/txscript/stack.go b/txscript/stack.go index 35cde562..a2d69893 100644 --- a/txscript/stack.go +++ b/txscript/stack.go @@ -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 diff --git a/txscript/stack_test.go b/txscript/stack_test.go index 293413b4..72e31b11 100644 --- a/txscript/stack_test.go +++ b/txscript/stack_test.go @@ -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)