diff --git a/bscript/interpreter/config.go b/bscript/interpreter/config.go index f07ce57b..1b12189b 100644 --- a/bscript/interpreter/config.go +++ b/bscript/interpreter/config.go @@ -3,6 +3,7 @@ package interpreter import "math" type config interface { + AfterGenesis() bool MaxOps() int MaxStackSize() int MaxScriptSize() int @@ -24,6 +25,14 @@ const ( type beforeGenesisConfig struct{} type afterGenesisConfig struct{} +func (a *afterGenesisConfig) AfterGenesis() bool { + return true +} + +func (b *beforeGenesisConfig) AfterGenesis() bool { + return false +} + func (a *afterGenesisConfig) MaxStackSize() int { return math.MaxInt32 } diff --git a/bscript/interpreter/number.go b/bscript/interpreter/number.go new file mode 100644 index 00000000..a651c2d9 --- /dev/null +++ b/bscript/interpreter/number.go @@ -0,0 +1,426 @@ +package interpreter + +import ( + "math" + "math/big" + + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" +) + +// scriptNumber 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 serialised representation (including values that overflow). +// +// Then, whenever data is interpreted as an integer, it is converted to this +// type by using the NewNumber function which will return an error if the +// number is out of range or not minimally encoded depending on parameters. +// Since all numeric opcodes involve pulling data from the stack and +// interpreting it as an integer, it provides the required behaviour. +type scriptNumber struct { + val *big.Int + afterGenesis bool +} + +var zero = big.NewInt(0) +var one = big.NewInt(1) + +// makeScriptNumber interprets the passed serialised bytes as an encoded integer +// and returns the result as a Number. +// +// Since the consensus rules dictate that serialised bytes interpreted as integers +// are only allowed to be in the range determined by a maximum number of bytes, +// on a per opcode basis, an error will be returned when the provided bytes +// would result in a number outside that range. In particular, the range for +// the vast majority of opcodes dealing with numeric values are limited to 4 +// bytes and therefore will pass that value to this function resulting in an +// allowed range of [-2^31 + 1, 2^31 - 1]. +// +// 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. +// +// The scriptNumLen is the maximum number of bytes the encoded value can be +// before an errs.ErrStackNumberTooBig is returned. This effectively limits the +// range of allowed values. +// WARNING: Great care should be taken if passing a value larger than +// defaultScriptNumLen, which could lead to addition and multiplication +// overflows. +// +// See the Bytes function documentation for example encodings. +func makeScriptNumber(bb []byte, scriptNumLen int, requireMinimal, afterGenesis bool) (*scriptNumber, error) { + // Interpreting data requires that it is not larger than the passed scriptNumLen value. + if len(bb) > scriptNumLen { + return &scriptNumber{val: big.NewInt(0), afterGenesis: false}, errs.NewError( + errs.ErrNumberTooBig, + "numeric value encoded as %x is %d bytes which exceeds the max allowed of %d", + bb, len(bb), scriptNumLen, + ) + } + + // Enforce minimal encoded if requested. + if requireMinimal { + if err := checkMinimalDataEncoding(bb); err != nil { + return &scriptNumber{ + val: big.NewInt(0), + afterGenesis: false, + }, err + } + } + + // Zero is encoded as an empty byte slice. + if len(bb) == 0 { + return &scriptNumber{ + afterGenesis: afterGenesis, + val: big.NewInt(0), + }, nil + } + + // Decode from little endian. + // + // The following is the equivalent of: + // var v int64 + // for i, b := range bb { + // v |= int64(b) << uint8(8*i) + // } + v := new(big.Int) + for i, b := range bb { + v.Or(v, new(big.Int).Lsh(new(big.Int).SetBytes([]byte{b}), uint(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. + // + // The following is the equivalent of: + // if bb[len(bb)-1]&0x80 != 0 { + // v &= ^(int64(0x80) << uint8(8*(len(bb)-1))) + // return -v, nil + // } + if bb[len(bb)-1]&0x80 != 0 { + // The maximum length of bb has already been determined to be 4 + // above, so uint8 is enough to cover the max possible shift + // value of 24. + shift := big.NewInt(int64(0x80)) + shift.Not(shift.Lsh(shift, uint(8*(len(bb)-1)))) + v.And(v, shift).Neg(v) + } + return &scriptNumber{ + val: v, + afterGenesis: afterGenesis, + }, nil +} + +// Add adds the receiver and the number, sets the result over the receiver and returns. +func (n *scriptNumber) Add(o *scriptNumber) *scriptNumber { + *n.val = *new(big.Int).Add(n.val, o.val) + return n +} + +// Sub subtracts the number from the receiver, sets the result over the receiver and returns. +func (n *scriptNumber) Sub(o *scriptNumber) *scriptNumber { + *n.val = *new(big.Int).Sub(n.val, o.val) + return n +} + +// Mul multiplies the receiver by the number, sets the result over the receiver and returns. +func (n *scriptNumber) Mul(o *scriptNumber) *scriptNumber { + *n.val = *new(big.Int).Mul(n.val, o.val) + return n +} + +// Div divides the receiver by the number, sets the result over the receiver and returns. +func (n *scriptNumber) Div(o *scriptNumber) *scriptNumber { + *n.val = *new(big.Int).Quo(n.val, o.val) + return n +} + +// Mod divides the receiver by the number, sets the remainder over the receiver and returns. +func (n *scriptNumber) Mod(o *scriptNumber) *scriptNumber { + *n.val = *new(big.Int).Rem(n.val, o.val) + return n +} + +// LessThanInt returns true if the receiver is smaller than the integer passed. +func (n *scriptNumber) LessThanInt(i int64) bool { + return n.LessThan(&scriptNumber{val: big.NewInt(i)}) +} + +// LessThan returns true if the receiver is smaller than the number passed. +func (n *scriptNumber) LessThan(o *scriptNumber) bool { + return n.val.Cmp(o.val) == -1 +} + +// LessThanOrEqual returns ture if the receiver is smaller or equal to the number passed. +func (n *scriptNumber) LessThanOrEqual(o *scriptNumber) bool { + return n.val.Cmp(o.val) < 1 +} + +// GreaterThanInt returns true if the receiver is larger than the integer passed. +func (n *scriptNumber) GreaterThanInt(i int64) bool { + return n.GreaterThan(&scriptNumber{val: big.NewInt(i)}) +} + +// GreaterThan returns true if the receiver is larger than the number passed. +func (n *scriptNumber) GreaterThan(o *scriptNumber) bool { + return n.val.Cmp(o.val) == 1 +} + +// GreaterThanOrEqual returns true if the receiver is larger or equal to the number passed. +func (n *scriptNumber) GreaterThanOrEqual(o *scriptNumber) bool { + return n.val.Cmp(o.val) > -1 +} + +// EqualInt returns true if the receiver is equal to the integer passed. +func (n *scriptNumber) EqualInt(i int64) bool { + return n.Equal(&scriptNumber{val: big.NewInt(i)}) +} + +// Equal returns true if the receiver is equal to the number passed. +func (n *scriptNumber) Equal(o *scriptNumber) bool { + return n.val.Cmp(o.val) == 0 +} + +// IsZero return strue if hte receiver equals zero. +func (n *scriptNumber) IsZero() bool { + return n.val.Cmp(zero) == 0 +} + +// Incr increment the receiver by one. +func (n *scriptNumber) Incr() *scriptNumber { + *n.val = *new(big.Int).Add(n.val, one) + return n +} + +// Decr decrement the receiver by one. +func (n *scriptNumber) Decr() *scriptNumber { + *n.val = *new(big.Int).Sub(n.val, one) + return n +} + +// Neg sets the receiver to the negative of the receiver. +func (n *scriptNumber) Neg() *scriptNumber { + *n.val = *new(big.Int).Neg(n.val) + return n +} + +// Abs sets the receiver to the absolute value of hte receiver. +func (n *scriptNumber) Abs() *scriptNumber { + *n.val = *new(big.Int).Abs(n.val) + return n +} + +// Int returns the receivers value as an int. +func (n *scriptNumber) Int() int { + return int(n.val.Int64()) +} + +// Int32 returns the 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 +// behaviour is different from a simple int32 cast because that truncates +// and the consensus rules dictate numbers which are directly cast to integers +// provide this behaviour. +// +// In practice, for most opcodes, the number should never be out of range since +// it will have been created with makeScriptNumber using the defaultScriptLen +// value, which rejects them. 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 behaviour. +func (n *scriptNumber) Int32() int32 { + v := n.val.Int64() + if v > math.MaxInt32 { + return math.MaxInt32 + } + if v < math.MinInt32 { + return math.MinInt32 + } + return int32(v) +} + +// Int64 returns the Number clamped to a valid int64. That is to say +// when the script number is higher than the max allowed int64, the max int64 +// value is returned and vice versa for the minimum value. Note that this +// behaviour is different from a simple int64 cast because that truncates +// and the consensus rules dictate numbers which are directly cast to integers +// provide this behaviour. +// +// In practice, for most opcodes, the number should never be out of range since +// it will have been created with makeScriptNumber using the defaultScriptLen +// value, which rejects them. 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 behaviour. +func (n *scriptNumber) Int64() int64 { + if n.GreaterThanInt(math.MaxInt64) { + return math.MaxInt64 + } + if n.LessThanInt(math.MinInt64) { + return math.MinInt64 + } + return n.val.Int64() +} + +// Set the value of the receiver. +func (n *scriptNumber) Set(i int64) *scriptNumber { + *n.val = *new(big.Int).SetInt64(i) + return n +} + +// Bytes returns the number serialised 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 *scriptNumber) Bytes() []byte { + // Zero encodes as an empty byte slice. + if n.IsZero() { + return []byte{} + } + + // Take the absolute value and keep track of whether it was originally + // negative. + isNegative := n.val.Cmp(zero) == -1 + if isNegative { + n.Neg() + } + + var bb []byte + if !n.afterGenesis { + v := n.val.Int64() + if v > math.MaxInt32 { + bb = big.NewInt(int64(math.MaxInt32)).Bytes() + } else if v < math.MinInt32 { + bb = big.NewInt(int64(math.MinInt32)).Bytes() + } + } + if bb == nil { + bb = n.val.Bytes() + } + + // Encode to little endian. The maximum number of encoded bytes is len(bb)+1 + // (8 bytes for max int64 plus a potential byte for sign extension). + // + // The following is the equivalent of: + // result := make([]byte, 0, len(bb)+1) + // for n > 0 { + // result = append(result, byte(n&0xff)) + // n >>= 8 + // } + result := make([]byte, 0, len(bb)+1) + cpy := new(big.Int).SetBytes(n.val.Bytes()) + for cpy.Cmp(zero) == 1 { + result = append(result, byte(cpy.Int64()&0xff)) + cpy.Rsh(cpy, 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 +} + +func minimallyEncode(data []byte) []byte { + if len(data) == 0 { + return data + } + + last := data[len(data)-1] + if last&0x7f != 0 { + return data + } + + if len(data) == 1 { + return []byte{} + } + + if data[len(data)-2]&0x80 != 0 { + return data + } + + for i := len(data) - 1; i > 0; i-- { + if data[i-1] != 0 { + if data[i-1]&0x80 != 0 { + data[i] = last + i++ + } else { + data[i-1] |= last + } + + return data[:i] + } + } + + return []byte{} +} + +// checkMinimalDataEncoding returns whether 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 errs.NewError(errs.ErrMinimalData, "numeric value encoded as %x is not minimally encoded", v) + } + } + + return nil +} diff --git a/bscript/interpreter/scriptnum_test.go b/bscript/interpreter/number_test.go similarity index 80% rename from bscript/interpreter/scriptnum_test.go rename to bscript/interpreter/number_test.go index 2e17d120..6b4b2375 100644 --- a/bscript/interpreter/scriptnum_test.go +++ b/bscript/interpreter/number_test.go @@ -7,6 +7,8 @@ package interpreter import ( "bytes" "encoding/hex" + "math" + "math/big" "testing" "github.com/libsv/go-bt/v2" @@ -33,7 +35,7 @@ func TestScriptNumBytes(t *testing.T) { t.Parallel() tests := []struct { - num scriptNum + num int64 serialised []byte }{ {0, nil}, @@ -81,11 +83,9 @@ func TestScriptNumBytes(t *testing.T) { } for _, test := range tests { - gotBytes := test.num.Bytes() - if !bytes.Equal(gotBytes, test.serialised) { - t.Errorf("Bytes: did not get expected bytes for %d - "+ - "got %x, want %x", test.num, gotBytes, - test.serialised) + n := &scriptNumber{val: big.NewInt(test.num)} + if !bytes.Equal(n.Bytes(), test.serialised) { + t.Errorf("Bytes: did not get expected bytes for %d - got %x, want %x", test.num, n.Bytes(), test.serialised) continue } } @@ -103,7 +103,7 @@ func TestMakeScriptNum(t *testing.T) { tests := []struct { serialised []byte - num scriptNum + num int numLen int minimalEncoding bool err error @@ -200,17 +200,15 @@ func TestMakeScriptNum(t *testing.T) { for _, test := range tests { // Ensure the error code is of the expected type and the error // code matches the value specified in the test instance. - gotNum, err := makeScriptNum(test.serialised, test.minimalEncoding, - test.numLen) + gotNum, err := makeScriptNumber(test.serialised, test.numLen, test.minimalEncoding, true) if e := tstCheckScriptError(err, test.err); e != nil { - t.Errorf("makeScriptNum(%#x): %v", test.serialised, e) + t.Errorf("makeScriptNumber(%#x): %v", test.serialised, e) continue } - if gotNum != test.num { - t.Errorf("makeScriptNum(%#x): did not get expected "+ - "number - got %d, want %d", test.serialised, - gotNum, test.num) + if gotNum.Int() != test.num { + t.Errorf("makeScriptNumber(%#x): did not get expected number - got %d, want %d", + test.serialised, gotNum.Int64(), test.num) continue } } @@ -222,7 +220,7 @@ func TestScriptNumInt32(t *testing.T) { t.Parallel() tests := []struct { - in scriptNum + in int64 want int32 }{ // Values inside the valid int32 range are just the values @@ -268,10 +266,69 @@ func TestScriptNumInt32(t *testing.T) { } 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) + n := &scriptNumber{val: big.NewInt(test.in)} + if n.Int32() != test.want { + t.Errorf("Int32: did not get expected value for %d - got %d, want %d", test.in, n.Int32(), test.want) + continue + } + } +} + +func TestScriptNumInt64(t *testing.T) { + t.Parallel() + + tests := []struct { + in *big.Int + want int64 + }{ + // Values inside the valid int64 range are just the values + // themselves cast to an int64. + {big.NewInt(0), 0}, + {big.NewInt(1), 1}, + {big.NewInt(-1), -1}, + {big.NewInt(127), 127}, + {big.NewInt(-127), -127}, + {big.NewInt(128), 128}, + {big.NewInt(-128), -128}, + {big.NewInt(129), 129}, + {big.NewInt(-129), -129}, + {big.NewInt(256), 256}, + {big.NewInt(-256), -256}, + {big.NewInt(32767), 32767}, + {big.NewInt(-32767), -32767}, + {big.NewInt(32768), 32768}, + {big.NewInt(-32768), -32768}, + {big.NewInt(65535), 65535}, + {big.NewInt(-65535), -65535}, + {big.NewInt(524288), 524288}, + {big.NewInt(-524288), -524288}, + {big.NewInt(7340032), 7340032}, + {big.NewInt(-7340032), -7340032}, + {big.NewInt(8388608), 8388608}, + {big.NewInt(-8388608), -8388608}, + {big.NewInt(2147483647), 2147483647}, + {big.NewInt(-2147483647), -2147483647}, + {big.NewInt(-2147483648), -2147483648}, + {big.NewInt(2147483648), 2147483648}, + {big.NewInt(-2147483649), -2147483649}, + {big.NewInt(1152921504606846975), 1152921504606846975}, + {big.NewInt(-1152921504606846975), -1152921504606846975}, + {big.NewInt(2305843009213693951), 2305843009213693951}, + {big.NewInt(-2305843009213693951), -2305843009213693951}, + {big.NewInt(4611686018427387903), 4611686018427387903}, + {big.NewInt(-4611686018427387903), -4611686018427387903}, + {big.NewInt(9223372036854775807), 9223372036854775807}, + {big.NewInt(-9223372036854775808), -9223372036854775808}, + + // Values outside of the valid int64 range are limited to int64. + {new(big.Int).Add(big.NewInt(9223372036854775807), big.NewInt(5)), math.MaxInt64}, + {new(big.Int).Sub(big.NewInt(-9223372036854775808), big.NewInt(600)), math.MinInt64}, + } + + for _, test := range tests { + n := &scriptNumber{val: test.in} + if n.Int64() != test.want { + t.Errorf("Int64: did not get expected value for %d - got %d, want %d", test.in, n.Int64(), test.want) continue } } diff --git a/bscript/interpreter/operations.go b/bscript/interpreter/operations.go index 4c089fa4..487e25ce 100644 --- a/bscript/interpreter/operations.go +++ b/bscript/interpreter/operations.go @@ -5,6 +5,7 @@ import ( "crypto/sha1" //nolint:gosec // OP_SHA1 support requires this "crypto/sha256" "hash" + "math/big" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/crypto" @@ -365,7 +366,10 @@ func opcodePushData(op *ParsedOpcode, t *thread) error { // opcode1Negate pushes -1, encoded as a number, to the data stack. func opcode1Negate(op *ParsedOpcode, t *thread) error { - t.dstack.PushInt(-1) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(-1), + afterGenesis: t.afterGenesis, + }) return nil } @@ -375,7 +379,7 @@ func opcode1Negate(op *ParsedOpcode, t *thread) error { func opcodeN(op *ParsedOpcode, t *thread) error { // The opcodes are all defined consecutively, so the numeric value is // the difference. - t.dstack.PushInt(scriptNum((op.op.val - (bscript.Op1 - 1)))) + t.dstack.PushByteArray([]byte{(op.op.val - (bscript.Op1 - 1))}) return nil } @@ -638,7 +642,7 @@ func opcodeCheckLockTimeVerify(op *ParsedOpcode, t *thread) error { if err != nil { return err } - lockTime, err := makeScriptNum(so, t.dstack.verifyMinimalData, 5) + lockTime, err := makeScriptNumber(so, 5, t.dstack.verifyMinimalData, t.afterGenesis) if err != nil { return err } @@ -646,15 +650,15 @@ func opcodeCheckLockTimeVerify(op *ParsedOpcode, t *thread) error { // In the rare event that the argument needs to be < 0 due to some // arithmetic being done first, you can always use // 0 bscript.OpMAX bscript.OpCHECKLOCKTIMEVERIFY. - if lockTime < 0 { - return errs.NewError(errs.ErrNegativeLockTime, "negative lock time: %d", lockTime) + if lockTime.LessThanInt(0) { + return errs.NewError(errs.ErrNegativeLockTime, "negative lock time: %d", lockTime.Int64()) } // The lock time field of a transaction is either a block height at // which the transaction is finalised or a timestamp depending on if the // value is before the interpreter.LockTimeThreshold. When it is under the // threshold it is a block height. - if err = verifyLockTime(int64(t.tx.LockTime), LockTimeThreshold, int64(lockTime)); err != nil { + if err = verifyLockTime(int64(t.tx.LockTime), LockTimeThreshold, lockTime.Int64()); err != nil { return err } @@ -708,7 +712,7 @@ func opcodeCheckSequenceVerify(op *ParsedOpcode, t *thread) error { if err != nil { return err } - stackSequence, err := makeScriptNum(so, t.dstack.verifyMinimalData, 5) + stackSequence, err := makeScriptNumber(so, 5, t.dstack.verifyMinimalData, t.afterGenesis) if err != nil { return err } @@ -716,11 +720,11 @@ func opcodeCheckSequenceVerify(op *ParsedOpcode, t *thread) error { // In the rare event that the argument needs to be < 0 due to some // arithmetic being done first, you can always use // 0 bscript.OpMAX bscript.OpCHECKSEQUENCEVERIFY. - if stackSequence < 0 { - return errs.NewError(errs.ErrNegativeLockTime, "negative sequence: %d", stackSequence) + if stackSequence.LessThanInt(0) { + return errs.NewError(errs.ErrNegativeLockTime, "negative sequence: %d", stackSequence.Int64()) } - sequence := int64(stackSequence) + sequence := stackSequence.Int64() // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, @@ -851,7 +855,10 @@ func opcodeIfDup(op *ParsedOpcode, t *thread) 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, t *thread) error { - t.dstack.PushInt(scriptNum(t.dstack.Depth())) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(int64(t.dstack.Depth())), + afterGenesis: t.afterGenesis, + }) return nil } @@ -978,12 +985,12 @@ func opcodeSplit(op *ParsedOpcode, t *thread) error { if n.Int32() > int32(len(c)) { return errs.NewError(errs.ErrNumberTooBig, "n is larger than length of array") } - if n < 0 { + if n.LessThanInt(0) { return errs.NewError(errs.ErrNumberTooSmall, "n is negative") } - a := c[:n] - b := c[n:] + a := c[:n.Int()] + b := c[n.Int():] t.dstack.PushByteArray(a) t.dstack.PushByteArray(b) @@ -1006,34 +1013,33 @@ func opcodeNum2bin(op *ParsedOpcode, t *thread) error { return err } - size := int(n.Int32()) - if size > t.cfg.MaxScriptElementSize() { + if n.GreaterThanInt(int64(t.cfg.MaxScriptElementSize())) { return errs.NewError(errs.ErrNumberTooBig, "n is larger than the max of %d", t.cfg.MaxScriptElementSize()) } // encode a as a script num so that we we take the bytes it // will be minimally encoded. - sn, err := makeScriptNum(a, false, len(a)) + sn, err := makeScriptNumber(a, len(a), false, t.afterGenesis) if err != nil { return err } b := sn.Bytes() - if len(b) > size { + if n.LessThanInt(int64(len(b))) { return errs.NewError(errs.ErrNumberTooSmall, "cannot fit it into n sized array") } - if len(b) == size { + if n.EqualInt(int64(len(b))) { t.dstack.PushByteArray(b) return nil } signbit := byte(0x00) if len(b) > 0 { - signbit = b[0] & 0x80 + signbit = b[len(b)-1] & 0x80 b[len(b)-1] &= 0x7f } - for len(b) < size-1 { + for n.GreaterThanInt(int64(len(b) + 1)) { b = append(b, 0x00) } @@ -1054,15 +1060,12 @@ func opcodeBin2num(op *ParsedOpcode, t *thread) error { return err } - n, err := makeScriptNum(a, false, len(a)) - if err != nil { - return err - } - if len(n.Bytes()) > t.cfg.MaxScriptNumberLength() { + b := minimallyEncode(a) + if len(b) > t.cfg.MaxScriptNumberLength() { return errs.NewError(errs.ErrNumberTooBig, "script numbers are limited to %d bytes", t.cfg.MaxScriptNumberLength()) } - t.dstack.PushInt(n) + t.dstack.PushByteArray(b) return nil } @@ -1076,7 +1079,10 @@ func opcodeSize(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(scriptNum(len(so))) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(int64(len(so))), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1221,7 +1227,7 @@ func opcode1Add(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(m + 1) + t.dstack.PushInt(m.Incr()) return nil } @@ -1235,7 +1241,7 @@ func opcode1Sub(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(m - 1) + t.dstack.PushInt(m.Decr()) return nil } @@ -1249,7 +1255,7 @@ func opcodeNegate(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(-m) + t.dstack.PushInt(m.Neg()) return nil } @@ -1263,11 +1269,7 @@ func opcodeAbs(op *ParsedOpcode, t *thread) error { return err } - if m < 0 { - m = -m - } - - t.dstack.PushInt(m) + t.dstack.PushInt(m.Abs()) return nil } @@ -1289,12 +1291,15 @@ func opcodeNot(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if m == 0 { + var n int64 + if m.IsZero() { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1310,8 +1315,8 @@ func opcode0NotEqual(op *ParsedOpcode, t *thread) error { return err } - if m != 0 { - m = 1 + if !m.IsZero() { + m.Set(1) } t.dstack.PushInt(m) @@ -1333,7 +1338,7 @@ func opcodeAdd(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(v0 + v1) + t.dstack.PushInt(v0.Add(v1)) return nil } @@ -1353,7 +1358,7 @@ func opcodeSub(op *ParsedOpcode, t *thread) error { return err } - t.dstack.PushInt(v1 - v0) + t.dstack.PushInt(v1.Sub(v0)) return nil } @@ -1371,9 +1376,7 @@ func opcodeMul(op *ParsedOpcode, t *thread) error { return err } - n3 := n1.Int64() * n2.Int64() - - t.dstack.PushInt(scriptNum(n3)) + t.dstack.PushInt(n1.Mul(n2)) return nil } @@ -1392,11 +1395,11 @@ func opcodeDiv(op *ParsedOpcode, t *thread) error { return err } - if b == 0 { + if b.IsZero() { return errs.NewError(errs.ErrDivideByZero, "divide by zero") } - t.dstack.PushInt(a / b) + t.dstack.PushInt(a.Div(b)) return nil } @@ -1415,21 +1418,22 @@ func opcodeMod(op *ParsedOpcode, t *thread) error { return err } - if b == 0 { + if b.IsZero() { return errs.NewError(errs.ErrDivideByZero, "mod by zero") } - t.dstack.PushInt(a % b) + t.dstack.PushInt(a.Mod(b)) return nil } func opcodeLShift(op *ParsedOpcode, t *thread) error { - n, err := t.dstack.PopInt() + num, err := t.dstack.PopInt() if err != nil { return err } + n := num.Int() - if n.Int32() < 0 { + if n < 0 { return errs.NewError(errs.ErrNumberTooSmall, "n less than 0") } @@ -1449,12 +1453,13 @@ func opcodeLShift(op *ParsedOpcode, t *thread) error { } func opcodeRShift(op *ParsedOpcode, t *thread) error { - n, err := t.dstack.PopInt() + num, err := t.dstack.PopInt() if err != nil { return err } + n := num.Int() - if n.Int32() < 0 { + if n < 0 { return errs.NewError(errs.ErrNumberTooSmall, "n less than 0") } @@ -1491,12 +1496,15 @@ func opcodeBoolAnd(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v0 != 0 && v1 != 0 { + var n int64 + if !v0.IsZero() && !v1.IsZero() { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1518,12 +1526,15 @@ func opcodeBoolOr(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v0 != 0 || v1 != 0 { + var n int64 + if !v0.IsZero() || !v1.IsZero() { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1543,12 +1554,15 @@ func opcodeNumEqual(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v0 == v1 { + var n int64 + if v0.Equal(v1) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1584,12 +1598,15 @@ func opcodeNumNotEqual(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v0 != v1 { + var n int64 + if !v0.Equal(v1) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1609,12 +1626,15 @@ func opcodeLessThan(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v1 < v0 { + var n int64 + if v1.LessThan(v0) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1634,12 +1654,15 @@ func opcodeGreaterThan(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v1 > v0 { + var n int64 + if v1.GreaterThan(v0) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1659,12 +1682,15 @@ func opcodeLessThanOrEqual(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v1 <= v0 { + var n int64 + if v1.LessThanOrEqual(v0) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1684,12 +1710,15 @@ func opcodeGreaterThanOrEqual(op *ParsedOpcode, t *thread) error { return err } - var n scriptNum - if v1 >= v0 { + var n int64 + if v1.GreaterThanOrEqual(v0) { n = 1 } - t.dstack.PushInt(n) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -1709,7 +1738,7 @@ func opcodeMin(op *ParsedOpcode, t *thread) error { } n := v0 - if v1 < v0 { + if v1.LessThan(v0) { n = v1 } @@ -1733,7 +1762,7 @@ func opcodeMax(op *ParsedOpcode, t *thread) error { } n := v0 - if v1 > v0 { + if v1.GreaterThan(v0) { n = v1 } @@ -1765,12 +1794,15 @@ func opcodeWithin(op *ParsedOpcode, t *thread) error { return err } - var n int - if minVal <= x && x < maxVal { + var n int64 + if minVal.LessThanOrEqual(x) && x.LessThan(maxVal) { n = 1 } - t.dstack.PushInt(scriptNum(n)) + t.dstack.PushInt(&scriptNumber{ + val: big.NewInt(n), + afterGenesis: t.afterGenesis, + }) return nil } @@ -2019,7 +2051,7 @@ func opcodeCheckMultiSig(op *ParsedOpcode, t *thread) error { return err } - numPubKeys := int(numKeys.Int32()) + numPubKeys := numKeys.Int() if numPubKeys < 0 { return errs.NewError(errs.ErrInvalidPubKeyCount, "number of pubkeys %d is negative", numPubKeys) } @@ -2049,7 +2081,7 @@ func opcodeCheckMultiSig(op *ParsedOpcode, t *thread) error { return err } - numSignatures := int(numSigs.Int32()) + numSignatures := numSigs.Int() if numSignatures < 0 { return errs.NewError(errs.ErrInvalidSignatureCount, "number of signatures %d is negative", numSignatures) } diff --git a/bscript/interpreter/reference_test.go b/bscript/interpreter/reference_test.go index b4a206ac..59267169 100644 --- a/bscript/interpreter/reference_test.go +++ b/bscript/interpreter/reference_test.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io/ioutil" + "math/big" "strconv" "strings" "testing" @@ -92,7 +93,8 @@ func parseShortForm(script string) (*bscript.Script, error) { } else if num == -1 || (1 <= num && num <= 16) { scr.AppendOpCode((bscript.Op1 - 1) + byte(num)) } else { - scr.AppendPushData(scriptNum(num).Bytes()) + n := &scriptNumber{val: big.NewInt(num)} + scr.AppendPushData(n.Bytes()) } continue } else if bts, err := parseHex(tok); err == nil { diff --git a/bscript/interpreter/scriptnum.go b/bscript/interpreter/scriptnum.go deleted file mode 100644 index 8cbc59e8..00000000 --- a/bscript/interpreter/scriptnum.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2015-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package interpreter - -import ( - "github.com/libsv/go-bt/v2/bscript/interpreter/errs" -) - -const ( - maxInt32 = 1<<31 - 1 - minInt32 = -1 << 31 -) - -// 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 serialised 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 parameters. -// Since all numeric opcodes involve pulling data from the stack and -// interpreting it as an integer, it provides the required behaviour. -type scriptNum int64 - -// checkMinimalDataEncoding returns whether 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 errs.NewError(errs.ErrMinimalData, "numeric value encoded as %x is not minimally encoded", v) - } - } - - return nil -} - -// Bytes returns the number serialised 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 -// behaviour is different from a simple int32 cast because that truncates -// and the consensus rules dictate numbers which are directly cast to integers -// provide this behaviour. -// -// In practice, for most opcodes, the number should never be out of range since -// it will have been created with makeScriptNum using the defaultScriptLen -// value, which rejects them. 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 behaviour. -func (n scriptNum) Int32() int32 { - if n > maxInt32 { - return maxInt32 - } - - if n < minInt32 { - return minInt32 - } - - return int32(n) -} - -func (n scriptNum) Int64() int64 { - return int64(n.Int32()) -} - -// makeScriptNum interprets the passed serialised bytes as an encoded integer -// and returns the result as a script number. -// -// Since the consensus rules dictate that serialised bytes interpreted as integers -// are only allowed to be in the range determined by a maximum number of bytes, -// on a per opcode basis, an error will be returned when the provided bytes -// would result in a number outside that range. In particular, the range for -// the vast majority of opcodes dealing with numeric values are limited to 4 -// bytes and therefore will pass that value to this function resulting in an -// allowed range of [-2^31 + 1, 2^31 - 1]. -// -// 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. -// -// The scriptNumLen is the maximum number of bytes the encoded value can be -// before an errs.ErrStackNumberTooBig is returned. This effectively limits the -// range of allowed values. -// WARNING: Great care should be taken if passing a value larger than -// defaultScriptNumLen, which could lead to addition and multiplication -// overflows. -// -// See the Bytes function documentation for example encodings. -func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum, error) { - // Interpreting data requires that it is not larger than - // the passed scriptNumLen value. - if len(v) > scriptNumLen { - return 0, errs.NewError( - errs.ErrNumberTooBig, - "numeric value encoded as %x is %d bytes which exceeds the max allowed of %d", - v, len(v), scriptNumLen, - ) - } - - // 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/bscript/interpreter/stack.go b/bscript/interpreter/stack.go index ec2e838f..7560c41f 100644 --- a/bscript/interpreter/stack.go +++ b/bscript/interpreter/stack.go @@ -39,6 +39,7 @@ func fromBool(v bool) []byte { type stack struct { stk [][]byte maxNumLength int + afterGenesis bool verifyMinimalData bool debug Debugger sh StateHandler @@ -47,6 +48,7 @@ type stack struct { func newStack(cfg config, verifyMinimalData bool) stack { return stack{ maxNumLength: cfg.MaxScriptNumberLength(), + afterGenesis: cfg.AfterGenesis(), verifyMinimalData: verifyMinimalData, debug: &nopDebugger{}, sh: &nopStateHandler{}, @@ -67,12 +69,12 @@ func (s *stack) PushByteArray(so []byte) { s.stk = append(s.stk, so) } -// PushInt converts the provided scriptNum to a suitable byte array then pushes +// PushInt converts the provided scriptNumber 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 scriptNum) { - s.PushByteArray(val.Bytes()) +func (s *stack) PushInt(n *scriptNumber) { + s.PushByteArray(n.Bytes()) } // PushBool converts the provided boolean to a suitable byte array then pushes @@ -96,18 +98,18 @@ func (s *stack) PopByteArray() ([]byte, error) { return data, nil } -// 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 +// PopInt pops the value off the top of the stack, converts it into a scriptNumber, +// 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() (scriptNum, error) { +func (s *stack) PopInt() (*scriptNumber, error) { so, err := s.PopByteArray() if err != nil { - return 0, err + return nil, err } - return makeScriptNum(so, s.verifyMinimalData, s.maxNumLength) + return makeScriptNumber(so, s.maxNumLength, s.verifyMinimalData, s.afterGenesis) } // PopBool pops the value off the top of the stack, converts it into a bool, and @@ -136,13 +138,13 @@ func (s *stack) PeekByteArray(idx int32) ([]byte, 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) { +func (s *stack) PeekInt(idx int32) (*scriptNumber, error) { so, err := s.PeekByteArray(idx) if err != nil { - return 0, err + return nil, err } - return makeScriptNum(so, s.verifyMinimalData, s.maxNumLength) + return makeScriptNumber(so, s.maxNumLength, s.verifyMinimalData, s.afterGenesis) } // PeekBool returns the Nth item on the stack as a bool without removing it. diff --git a/bscript/interpreter/stack_test.go b/bscript/interpreter/stack_test.go index 58253408..81d11060 100644 --- a/bscript/interpreter/stack_test.go +++ b/bscript/interpreter/stack_test.go @@ -8,6 +8,7 @@ import ( "bytes" "errors" "fmt" + "math/big" "reflect" "testing" @@ -192,7 +193,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != 0 { + if v.Int() != 0 { return errors.New("0 != 0 on popInt") } return nil @@ -208,7 +209,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != 0 { + if v.Int() != 0 { return errors.New("-0 != 0 on popInt") } return nil @@ -224,7 +225,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != 1 { + if v.Int() != 1 { return errors.New("1 != 1 on popInt") } return nil @@ -240,8 +241,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != 1 { - fmt.Printf("%v != %v\n", v, 1) + if v.Int() != 1 { return errors.New("1 != 1 on popInt") } return nil @@ -257,7 +257,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != -1 { + if v.Int() != -1 { return errors.New("-1 != -1 on popInt") } return nil @@ -273,8 +273,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != -1 { - fmt.Printf("%v != %v\n", v, -1) + if v.Int() != -1 { return errors.New("-1 != -1 on popInt") } return nil @@ -291,9 +290,8 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != -513 { - fmt.Printf("%v != %v\n", v, -513) - return errors.New("1 != 1 on popInt") + if v.Int() != -513 { + return errors.New("-513 != -513 on popInt") } return nil }, @@ -309,8 +307,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if v != -1 { - fmt.Printf("%v != %v\n", v, -1) + if v.Int() != -1 { return errors.New("-1 != -1 on popInt") } return nil @@ -322,7 +319,7 @@ func TestStack(t *testing.T) { "PushInt 0", nil, func(s *stack) error { - s.PushInt(scriptNum(0)) + s.PushInt(&scriptNumber{val: big.NewInt(0)}) return nil }, nil, @@ -332,7 +329,7 @@ func TestStack(t *testing.T) { "PushInt 1", nil, func(s *stack) error { - s.PushInt(scriptNum(1)) + s.PushInt(&scriptNumber{val: big.NewInt(1)}) return nil }, nil, @@ -342,7 +339,7 @@ func TestStack(t *testing.T) { "PushInt -1", nil, func(s *stack) error { - s.PushInt(scriptNum(-1)) + s.PushInt(&scriptNumber{val: big.NewInt(-1)}) return nil }, nil, @@ -352,7 +349,7 @@ func TestStack(t *testing.T) { "PushInt two bytes", nil, func(s *stack) error { - s.PushInt(scriptNum(256)) + s.PushInt(&scriptNumber{val: big.NewInt(256)}) return nil }, nil, @@ -364,7 +361,7 @@ func TestStack(t *testing.T) { nil, func(s *stack) error { // this will have the highbit set - s.PushInt(scriptNum(128)) + s.PushInt(&scriptNumber{val: big.NewInt(128)}) return nil }, nil, @@ -486,7 +483,7 @@ func TestStack(t *testing.T) { "PushInt PopBool", nil, func(s *stack) error { - s.PushInt(scriptNum(1)) + s.PushInt(&scriptNumber{val: big.NewInt(1)}) val, err := s.PopBool() if err != nil { return err @@ -504,7 +501,7 @@ func TestStack(t *testing.T) { "PushInt PopBool 2", nil, func(s *stack) error { - s.PushInt(scriptNum(0)) + s.PushInt(&scriptNumber{val: big.NewInt(0)}) val, err := s.PopBool() if err != nil { return err @@ -844,7 +841,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if val != 1 { + if val.Int() != 1 { return errors.New("invalid result") } return nil @@ -862,7 +859,7 @@ func TestStack(t *testing.T) { if err != nil { return err } - if val != 0 { + if val.Int() != 0 { return errors.New("invalid result") } return nil @@ -874,14 +871,14 @@ func TestStack(t *testing.T) { "pop int", nil, func(s *stack) error { - s.PushInt(scriptNum(1)) + s.PushInt(&scriptNumber{val: big.NewInt(1)}) // Peek int is otherwise pretty well tested, // just check it works. val, err := s.PopInt() if err != nil { return err } - if val != 1 { + if val.Int() != 1 { return errors.New("invalid result") } return nil diff --git a/bscript/interpreter/thread.go b/bscript/interpreter/thread.go index 5a414554..b7c0ef64 100644 --- a/bscript/interpreter/thread.go +++ b/bscript/interpreter/thread.go @@ -241,6 +241,7 @@ func (t *thread) CheckErrorCondition(finalScript bool) error { if finalScript { t.afterSuccess() } + return nil }