github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/txscript/stack.go (about)

     1  // Copyright (c) 2013-2015 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 "encoding/hex"
     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
    32  // scripts.  Objects may be shared, therefore in usage if a value is to be
    33  // changed it *must* be deep-copied first to avoid changing other values on the
    34  // stack.
    35  type stack struct {
    36  	stk               [][]byte
    37  	verifyMinimalData bool
    38  }
    39  
    40  // Depth returns the number of items on the stack.
    41  func (s *stack) Depth() int32 {
    42  	return int32(len(s.stk))
    43  }
    44  
    45  // PushByteArray adds the given back array to the top of the stack.
    46  //
    47  // Stack transformation: [... x1 x2] -> [... x1 x2 data]
    48  func (s *stack) PushByteArray(so []byte) {
    49  	s.stk = append(s.stk, so)
    50  }
    51  
    52  // PushInt converts the provided scriptNum to a suitable byte array then pushes
    53  // it onto the top of the stack.
    54  //
    55  // Stack transformation: [... x1 x2] -> [... x1 x2 int]
    56  func (s *stack) PushInt(val scriptNum) {
    57  	s.PushByteArray(val.Bytes())
    58  }
    59  
    60  // PushBool converts the provided boolean to a suitable byte array then pushes
    61  // it onto the top of the stack.
    62  //
    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
    76  // num, and returns it.  The act of converting to a script num enforces the
    77  // consensus rules imposed on data interpreted as numbers.
    78  //
    79  // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
    80  func (s *stack) PopInt() (scriptNum, error) {
    81  	so, err := s.PopByteArray()
    82  	if err != nil {
    83  		return 0, err
    84  	}
    85  
    86  	return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
    87  }
    88  
    89  // PopBool pops the value off the top of the stack, converts it into a bool, and
    90  // returns it.
    91  //
    92  // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
    93  func (s *stack) PopBool() (bool, error) {
    94  	so, err := s.PopByteArray()
    95  	if err != nil {
    96  		return false, err
    97  	}
    98  
    99  	return asBool(so), nil
   100  }
   101  
   102  // PeekByteArray returns the Nth item on the stack without removing it.
   103  func (s *stack) PeekByteArray(idx int32) ([]byte, error) {
   104  	sz := int32(len(s.stk))
   105  	if idx < 0 || idx >= sz {
   106  		return nil, ErrStackUnderflow
   107  	}
   108  
   109  	return s.stk[sz-idx-1], nil
   110  }
   111  
   112  // PeekInt returns the Nth item on the stack as a script num without removing
   113  // it.  The act of converting to a script num enforces the consensus rules
   114  // imposed on data interpreted as numbers.
   115  func (s *stack) PeekInt(idx int32) (scriptNum, error) {
   116  	so, err := s.PeekByteArray(idx)
   117  	if err != nil {
   118  		return 0, err
   119  	}
   120  
   121  	return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
   122  }
   123  
   124  // PeekBool returns the Nth item on the stack as a bool without removing it.
   125  func (s *stack) PeekBool(idx int32) (bool, error) {
   126  	so, err := s.PeekByteArray(idx)
   127  	if err != nil {
   128  		return false, err
   129  	}
   130  
   131  	return asBool(so), nil
   132  }
   133  
   134  // nipN is an internal function that removes the nth item on the stack and
   135  // returns it.
   136  //
   137  // Stack transformation:
   138  // nipN(0): [... x1 x2 x3] -> [... x1 x2]
   139  // nipN(1): [... x1 x2 x3] -> [... x1 x3]
   140  // nipN(2): [... x1 x2 x3] -> [... x2 x3]
   141  func (s *stack) nipN(idx int32) ([]byte, error) {
   142  	sz := int32(len(s.stk))
   143  	if idx < 0 || idx > sz-1 {
   144  		return nil, ErrStackUnderflow
   145  	}
   146  
   147  	so := s.stk[sz-idx-1]
   148  	if idx == 0 {
   149  		s.stk = s.stk[:sz-1]
   150  	} else if idx == sz-1 {
   151  		s1 := make([][]byte, sz-1, sz-1)
   152  		copy(s1, s.stk[1:])
   153  		s.stk = s1
   154  	} else {
   155  		s1 := s.stk[sz-idx : sz]
   156  		s.stk = s.stk[:sz-idx-1]
   157  		s.stk = append(s.stk, s1...)
   158  	}
   159  	return so, nil
   160  }
   161  
   162  // NipN removes the Nth object on the stack
   163  //
   164  // Stack transformation:
   165  // NipN(0): [... x1 x2 x3] -> [... x1 x2]
   166  // NipN(1): [... x1 x2 x3] -> [... x1 x3]
   167  // NipN(2): [... x1 x2 x3] -> [... x2 x3]
   168  func (s *stack) NipN(idx int32) error {
   169  	_, err := s.nipN(idx)
   170  	return err
   171  }
   172  
   173  // Tuck copies the item at the top of the stack and inserts it before the 2nd
   174  // to top item.
   175  //
   176  // Stack transformation: [... x1 x2] -> [... x2 x1 x2]
   177  func (s *stack) Tuck() error {
   178  	so2, err := s.PopByteArray()
   179  	if err != nil {
   180  		return err
   181  	}
   182  	so1, err := s.PopByteArray()
   183  	if err != nil {
   184  		return err
   185  	}
   186  	s.PushByteArray(so2) // stack [... x2]
   187  	s.PushByteArray(so1) // stack [... x2 x1]
   188  	s.PushByteArray(so2) // stack [... x2 x1 x2]
   189  
   190  	return nil
   191  }
   192  
   193  // DropN removes the top N items from the stack.
   194  //
   195  // Stack transformation:
   196  // DropN(1): [... x1 x2] -> [... x1]
   197  // DropN(2): [... x1 x2] -> [...]
   198  func (s *stack) DropN(n int32) error {
   199  	if n < 1 {
   200  		return ErrStackInvalidArgs
   201  	}
   202  
   203  	for ; n > 0; n-- {
   204  		_, err := s.PopByteArray()
   205  		if err != nil {
   206  			return err
   207  		}
   208  	}
   209  	return nil
   210  }
   211  
   212  // DupN duplicates the top N items on the stack.
   213  //
   214  // Stack transformation:
   215  // DupN(1): [... x1 x2] -> [... x1 x2 x2]
   216  // DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
   217  func (s *stack) DupN(n int32) error {
   218  	if n < 1 {
   219  		return ErrStackInvalidArgs
   220  	}
   221  
   222  	// Iteratively duplicate the value n-1 down the stack n times.
   223  	// This leaves an in-order duplicate of the top n items on the stack.
   224  	for i := n; i > 0; i-- {
   225  		so, err := s.PeekByteArray(n - 1)
   226  		if err != nil {
   227  			return err
   228  		}
   229  		s.PushByteArray(so)
   230  	}
   231  	return nil
   232  }
   233  
   234  // RotN rotates the top 3N items on the stack to the left N times.
   235  //
   236  // Stack transformation:
   237  // RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
   238  // RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
   239  func (s *stack) RotN(n int32) error {
   240  	if n < 1 {
   241  		return ErrStackInvalidArgs
   242  	}
   243  
   244  	// Nip the 3n-1th item from the stack to the top n times to rotate
   245  	// them up to the head of the stack.
   246  	entry := 3*n - 1
   247  	for i := n; i > 0; i-- {
   248  		so, err := s.nipN(entry)
   249  		if err != nil {
   250  			return err
   251  		}
   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  // SwapN(1): [... x1 x2] -> [... x2 x1]
   262  // SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
   263  func (s *stack) SwapN(n int32) error {
   264  	if n < 1 {
   265  		return ErrStackInvalidArgs
   266  	}
   267  
   268  	entry := 2*n - 1
   269  	for i := n; i > 0; i-- {
   270  		// Swap 2n-1th entry to top.
   271  		so, err := s.nipN(entry)
   272  		if err != nil {
   273  			return err
   274  		}
   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  // OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
   285  // OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
   286  func (s *stack) OverN(n int32) error {
   287  	if n < 1 {
   288  		return ErrStackInvalidArgs
   289  	}
   290  
   291  	// Copy 2n-1th entry to top of the stack.
   292  	entry := 2*n - 1
   293  	for ; n > 0; n-- {
   294  		so, err := s.PeekByteArray(entry)
   295  		if err != nil {
   296  			return err
   297  		}
   298  		s.PushByteArray(so)
   299  	}
   300  
   301  	return nil
   302  }
   303  
   304  // PickN copies the item N items back in the stack to the top.
   305  //
   306  // Stack transformation:
   307  // PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
   308  // PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
   309  // PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
   310  func (s *stack) PickN(n int32) error {
   311  	so, err := s.PeekByteArray(n)
   312  	if err != nil {
   313  		return err
   314  	}
   315  	s.PushByteArray(so)
   316  
   317  	return nil
   318  }
   319  
   320  // RollN moves the item N items back in the stack to the top.
   321  //
   322  // Stack transformation:
   323  // RollN(0): [x1 x2 x3] -> [x1 x2 x3]
   324  // RollN(1): [x1 x2 x3] -> [x1 x3 x2]
   325  // RollN(2): [x1 x2 x3] -> [x2 x3 x1]
   326  func (s *stack) RollN(n int32) error {
   327  	so, err := s.nipN(n)
   328  	if err != nil {
   329  		return err
   330  	}
   331  
   332  	s.PushByteArray(so)
   333  
   334  	return nil
   335  }
   336  
   337  // String returns the stack in a readable format.
   338  func (s *stack) String() string {
   339  	var result string
   340  	for _, stack := range s.stk {
   341  		if len(stack) == 0 {
   342  			result += "00000000  <empty>\n"
   343  		}
   344  		result += hex.Dump(stack)
   345  	}
   346  
   347  	return result
   348  }