-
Notifications
You must be signed in to change notification settings - Fork 0
/
scriptnum.go
238 lines (214 loc) · 8.58 KB
/
scriptnum.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// 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 txscript
import (
"fmt"
)
const (
maxInt32 = 1<<31 - 1
minInt32 = -1 << 31
// maxScriptNumLen is the maximum number of bytes data being interpreted
// as an integer may be for the majority of op codes.
maxScriptNumLen = 4
// cltvMaxScriptNumLen is the maximum number of bytes data being interpreted
// as an integer may be for by-time and by-height locks as interpreted by
// CHECKLOCKTIMEVERIFY.
//
// The value comes from the fact that the current transaction locktime
// is a uint32 resulting in a maximum locktime of 2^32-1 (the year
// 2106). However, scriptNums are signed and therefore a standard
// 4-byte scriptNum would only support up to a maximum of 2^31-1 (the
// year 2038). Thus, a 5-byte scriptNum is needed since it will support
// up to 2^39-1 which allows dates beyond the current locktime limit.
cltvMaxScriptNumLen = 5
)
// 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
// 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 {
str := fmt.Sprintf("numeric value encoded as %x is "+
"not minimally encoded", v)
return scriptError(ErrMinimalData, str)
}
}
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, 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)
}
// MakeScriptNum interprets the passed serialized bytes as an encoded integer
// and returns the result as a script number.
//
// Since the consensus rules dictate that serialized bytes interpreted as ints
// 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 of 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 ErrStackNumberTooBig is returned. This effectively limits the
// range of allowed values.
// WARNING: Great care should be taken if passing a value larger than
// maxScriptNumLen, 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 {
str := fmt.Sprintf("numeric value encoded as %x is %d bytes "+
"which exceeds the max allowed of %d", v, len(v),
scriptNumLen)
return 0, scriptError(ErrNumberTooBig, str)
}
// 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
}