1 package txscript
2 3 import (
4 "encoding/hex"
5 "fmt"
6 "sync"
7 )
8 9 // asBool gets the boolean value of the byte array.
10 func asBool(t []byte) bool {
11 for i := range t {
12 if t[i] != 0 {
13 // Negative 0 is also considered false.
14 if i == len(t)-1 && t[i] == 0x80 {
15 return false
16 }
17 return true
18 }
19 }
20 return false
21 }
22 23 // fromBool converts a boolean into the appropriate byte array.
24 func fromBool(v bool) []byte {
25 if v {
26 return []byte{1}
27 }
28 return nil
29 }
30 31 // stack represents a stack of immutable objects to be used with bitcoin scripts. Objects may be shared, therefore in
32 // usage if a value is to be changed it *must* be deep-copied first to avoid changing other values on the stack.
33 type stack struct {
34 stk [][]byte
35 stkMutex sync.Mutex
36 verifyMinimalData bool
37 }
38 39 // Depth returns the number of items on the stack.
40 func (s *stack) Depth() int32 {
41 s.stkMutex.Lock()
42 defer s.stkMutex.Unlock()
43 return int32(len(s.stk))
44 }
45 46 // PushByteArray adds the given back array to the top of the stack.
47 // Stack transformation: [... x1 x2] -> [... x1 x2 data]
48 func (s *stack) PushByteArray(so []byte) {
49 s.stkMutex.Lock()
50 {
51 s.stk = append(s.stk, so)
52 s.stkMutex.Unlock()
53 }
54 }
55 56 // PushInt converts the provided scriptNum to a suitable byte array then pushes it onto the top of the stack. Stack
57 // transformation: [... x1 x2] -> [... x1 x2 int]
58 func (s *stack) PushInt(val scriptNum) {
59 s.PushByteArray(val.Bytes())
60 }
61 62 // PushBool converts the provided boolean to a suitable byte array then pushes it onto the top of the stack.
63 // Stack transformation: [... x1 x2] -> [... x1 x2 bool]
64 func (s *stack) PushBool(val bool) {
65 s.PushByteArray(fromBool(val))
66 }
67 68 // PopByteArray pops the value off the top of the stack and returns it.
69 //
70 // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
71 func (s *stack) PopByteArray() ([]byte, error) {
72 return s.nipN(0)
73 }
74 75 // PopInt pops the value off the top of the stack, converts it into a script num, and returns it. The act of converting
76 // to a script num enforces the consensus rules imposed on data interpreted as numbers.
77 //
78 // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
79 func (s *stack) PopInt() (scriptNum, error) {
80 so, e := s.PopByteArray()
81 if e != nil {
82 return 0, e
83 }
84 return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
85 }
86 87 // PopBool pops the value off the top of the stack, converts it into a bool, and returns it.
88 //
89 // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
90 func (s *stack) PopBool() (bool, error) {
91 so, e := s.PopByteArray()
92 if e != nil {
93 return false, e
94 }
95 return asBool(so), nil
96 }
97 98 // PeekByteArray returns the Nth item on the stack without removing it.
99 func (s *stack) PeekByteArray(idx int32) ([]byte, error) {
100 sz := int32(len(s.stk))
101 if idx < 0 || idx >= sz {
102 str := fmt.Sprintf("index %d is invalid for stack size %d", idx,
103 sz,
104 )
105 return nil, scriptError(ErrInvalidStackOperation, str)
106 }
107 return s.stk[sz-idx-1], nil
108 }
109 110 // PeekInt returns the Nth item on the stack as a script num without removing it. The act of converting to a script num
111 // enforces the consensus rules imposed on data interpreted as numbers.
112 func (s *stack) PeekInt(idx int32) (scriptNum, error) {
113 so, e := s.PeekByteArray(idx)
114 if e != nil {
115 return 0, e
116 }
117 return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
118 }
119 120 // PeekBool returns the Nth item on the stack as a bool without removing it.
121 func (s *stack) PeekBool(idx int32) (bool, error) {
122 so, e := s.PeekByteArray(idx)
123 if e != nil {
124 return false, e
125 }
126 return asBool(so), nil
127 }
128 129 // nipN is an internal function that removes the nth item on the stack and returns it.
130 //
131 // Stack transformation:
132 // nipN(0): [... x1 x2 x3] -> [... x1 x2]
133 // nipN(1): [... x1 x2 x3] -> [... x1 x3]
134 // nipN(2): [... x1 x2 x3] -> [... x2 x3]
135 func (s *stack) nipN(idx int32) ([]byte, error) {
136 s.stkMutex.Lock()
137 {
138 sz := int32(len(s.stk))
139 if idx < 0 || idx > sz-1 {
140 str := fmt.Sprintf("index %d is invalid for stack size %d", idx,
141 sz,
142 )
143 return nil, scriptError(ErrInvalidStackOperation, str)
144 }
145 so := s.stk[sz-idx-1]
146 if idx == 0 {
147 s.stk = s.stk[:sz-1]
148 } else if idx == sz-1 {
149 s1 := make([][]byte, sz-1)
150 copy(s1, s.stk[1:])
151 s.stk = s1
152 } else {
153 s1 := s.stk[sz-idx : sz]
154 s.stk = s.stk[:sz-idx-1]
155 s.stk = append(s.stk, s1...)
156 }
157 s.stkMutex.Unlock()
158 return so, nil
159 }
160 }
161 162 // NipN removes the Nth object on the stack
163 //
164 // Stack transformation:
165 //
166 // NipN(0): [... x1 x2 x3] -> [... x1 x2]
167 // NipN(1): [... x1 x2 x3] -> [... x1 x3]
168 // NipN(2): [... x1 x2 x3] -> [... x2 x3]
169 func (s *stack) NipN(idx int32) (e error) {
170 _, e = s.nipN(idx)
171 return e
172 }
173 174 // Tuck copies the item at the top of the stack and inserts it before the 2nd to top item.
175 //
176 // Stack transformation: [... x1 x2] -> [... x2 x1 x2]
177 func (s *stack) Tuck() (e error) {
178 so2, e := s.PopByteArray()
179 if e != nil {
180 return e
181 }
182 so1, e := s.PopByteArray()
183 if e != nil {
184 return e
185 }
186 s.PushByteArray(so2) // stack [... x2]
187 s.PushByteArray(so1) // stack [... x2 x1]
188 s.PushByteArray(so2) // stack [... x2 x1 x2]
189 return nil
190 }
191 192 // DropN removes the top N items from the stack.
193 //
194 // Stack transformation:
195 //
196 // DropN(1): [... x1 x2] -> [... x1]
197 // DropN(2): [... x1 x2] -> [...]
198 func (s *stack) DropN(n int32) (e error) {
199 if n < 1 {
200 str := fmt.Sprintf("attempt to drop %d items from stack", n)
201 return scriptError(ErrInvalidStackOperation, str)
202 }
203 for ; n > 0; n-- {
204 _, e := s.PopByteArray()
205 if e != nil {
206 return e
207 }
208 }
209 return nil
210 }
211 212 // DupN duplicates the top N items on the stack.
213 //
214 // Stack transformation:
215 //
216 // DupN(1): [... x1 x2] -> [... x1 x2 x2]
217 // DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
218 func (s *stack) DupN(n int32) (e error) {
219 if n < 1 {
220 str := fmt.Sprintf("attempt to dup %d stack items", n)
221 return scriptError(ErrInvalidStackOperation, str)
222 }
223 // Iteratively duplicate the value n-1 down the stack n times. This leaves an in-order duplicate of the top n items
224 // on the stack.
225 for i := n; i > 0; i-- {
226 so, e := s.PeekByteArray(n - 1)
227 if e != nil {
228 return e
229 }
230 s.PushByteArray(so)
231 }
232 return nil
233 }
234 235 // RotN rotates the top 3N items on the stack to the left N times.
236 //
237 // Stack transformation:
238 //
239 // RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
240 // RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
241 func (s *stack) RotN(n int32) (e error) {
242 if n < 1 {
243 str := fmt.Sprintf("attempt to rotate %d stack items", n)
244 return scriptError(ErrInvalidStackOperation, str)
245 }
246 // Nip the 3n-1th item from the stack to the top n times to rotate them up to the head of the stack.
247 entry := 3*n - 1
248 for i := n; i > 0; i-- {
249 so, e := s.nipN(entry)
250 if e != nil {
251 return e
252 }
253 s.PushByteArray(so)
254 }
255 return nil
256 }
257 258 // SwapN swaps the top N items on the stack with those below them.
259 //
260 // Stack transformation:
261 //
262 // SwapN(1): [... x1 x2] -> [... x2 x1]
263 // SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
264 func (s *stack) SwapN(n int32) (e error) {
265 if n < 1 {
266 str := fmt.Sprintf("attempt to swap %d stack items", n)
267 return scriptError(ErrInvalidStackOperation, str)
268 }
269 entry := 2*n - 1
270 for i := n; i > 0; i-- {
271 // Swap 2n-1th entry to top.
272 so, e := s.nipN(entry)
273 if e != nil {
274 return e
275 }
276 s.PushByteArray(so)
277 }
278 return nil
279 }
280 281 // OverN copies N items N items back to the top of the stack.
282 //
283 // Stack transformation:
284 //
285 // OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
286 // OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
287 func (s *stack) OverN(n int32) (e error) {
288 if n < 1 {
289 str := fmt.Sprintf("attempt to perform over on %d stack items",
290 n,
291 )
292 return scriptError(ErrInvalidStackOperation, str)
293 }
294 // Copy 2n-1th entry to top of the stack.
295 entry := 2*n - 1
296 for ; n > 0; n-- {
297 so, e := s.PeekByteArray(entry)
298 if e != nil {
299 return e
300 }
301 s.PushByteArray(so)
302 }
303 return nil
304 }
305 306 // PickN copies the item N items back in the stack to the top.
307 //
308 // Stack transformation:
309 //
310 // PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
311 // PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
312 // PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
313 func (s *stack) PickN(n int32) (e error) {
314 so, e := s.PeekByteArray(n)
315 if e != nil {
316 return e
317 }
318 s.PushByteArray(so)
319 return nil
320 }
321 322 // RollN moves the item N items back in the stack to the top.
323 //
324 // Stack transformation:
325 //
326 // RollN(0): [x1 x2 x3] -> [x1 x2 x3]
327 // RollN(1): [x1 x2 x3] -> [x1 x3 x2]
328 // RollN(2): [x1 x2 x3] -> [x2 x3 x1]
329 func (s *stack) RollN(n int32) (e error) {
330 so, e := s.nipN(n)
331 if e != nil {
332 return e
333 }
334 s.PushByteArray(so)
335 return nil
336 }
337 338 // String returns the stack in a readable format.
339 func (s *stack) String() string {
340 s.stkMutex.Lock()
341 {
342 var result string
343 for _, stack := range s.stk {
344 if len(stack) == 0 {
345 result += "00000000 <empty>\n"
346 }
347 result += hex.Dump(stack)
348 }
349 s.stkMutex.Unlock()
350 return result
351 }
352 }
353