// 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 txbuilder 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 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 parameters. // Since all numeric opcodes involve pulling data from the stack and // interpreting it as an integer, it provides the required behavior. type scriptNum int64 // Bytes returns the number serialized as a little endian with a sign bit. 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, 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 behavior. func (n scriptNum) Int32() int32 { if n > maxInt32 { return maxInt32 } if n < minInt32 { return minInt32 } return int32(n) }