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