stack.go raw

   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