// Copyright (c) 2013 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package btcscript import ( "math/big" ) // asInt converts a byte array to a bignum by treating it as a little endian // number with sign bit. func asInt(v []byte) *big.Int { if len(v) == 0 { return big.NewInt(0) } negative := false msb := v[len(v)-1] if msb&0x80 == 0x80 { negative = true // remove sign bit v[len(v)-1] &= 0x7f } // trim leading 0 bytes for ; msb == 0; msb = v[len(v)-1] { v = v[:len(v)-1] if len(v) == 0 { break } } // reverse bytes with a copy since stack is immutable. intArray := make([]byte, len(v)) for i := range v { intArray[len(v)-i-1] = v[i] } num := new(big.Int).SetBytes(intArray) if negative { num = num.Neg(num) } return num } // fromInt provies a Big.Int in little endian format with the high bit of the // msb donating sign. func fromInt(v *big.Int) []byte { negative := false if v.Sign() == -1 { negative = true } // Int.Bytes() trims leading zeros for us, so we don't have to. b := v.Bytes() if len(b) == 0 { return []byte{} } arr := make([]byte, len(b)) for i := range b { arr[len(b)-i-1] = b[i] } // if would otherwise be negative, add a zero byte if arr[len(arr)-1]&0x80 == 0x80 { arr = append(arr, 0) } if negative { arr[len(arr)-1] |= 0x80 } return arr } // asBool gets the boolean value of the byte array. func asBool(t []byte) bool { for i := range t { if t[i] != 0 { return true } } return false } // fromBool converts a boolean into the appropriate byte array. func fromBool(v bool) []byte { if v { return []byte{1} } return []byte{0} } // Stack represents a stack of immutable objects to be used with bitcoin scripts // Objects may be shared, therefore in usage if a value is to be changed it // *must* be deep-copied first to avoid changing other values on the stack. type Stack struct { stk [][]byte } // PushByteArray adds the given back array to the top of the stack. func (s *Stack) PushByteArray(so []byte) { s.stk = append(s.stk, so) } // PushInt converts the provided bignum to a suitable byte array then pushes // it onto the top of the stack. func (s *Stack) PushInt(val *big.Int) { s.PushByteArray(fromInt(val)) } // PushBool converts the provided boolean to a suitable byte array then pushes // it onto the top of the stack. func (s *Stack) PushBool(val bool) { s.PushByteArray(fromBool(val)) } // PopByteArray pops the value off the top of the stack and returns it. func (s *Stack) PopByteArray() ([]byte, error) { return s.nipN(0) } // PopInt pops the value off the top of the stack, converts it into a bignum and // returns it. func (s *Stack) PopInt() (*big.Int, error) { so, err := s.PopByteArray() if err != nil { return nil, err } return asInt(so), nil } // PopBool pops the value off the top of the stack, converts it into a bool and // returns it. func (s *Stack) PopBool() (bool, error) { so, err := s.PopByteArray() if err != nil { return false, err } return asBool(so), nil } // PeekByteArray returns the nth item on the stack without removing it. func (s *Stack) PeekByteArray(idx int) (so []byte, err error) { sz := len(s.stk) if idx < 0 || idx >= sz { return nil, StackErrUnderflow } return s.stk[sz-idx-1], nil } // PeekInt returns the nth item on the stack as a bignum without removing it. func (s *Stack) PeekInt(idx int) (i *big.Int, err error) { so, err := s.PeekByteArray(idx) if err != nil { return nil, err } return asInt(so), nil } // PeekBool returns the nth item on the stack as a bool without removing it. func (s *Stack) PeekBool(idx int) (i bool, err error) { so, err := s.PeekByteArray(idx) if err != nil { return false, err } return asBool(so), nil } // nipN is an internal function that removes the nth item on the stack and // returns it. func (s *Stack) nipN(idx int) (so []byte, err error) { sz := len(s.stk) if idx < 0 || idx > sz-1 { err = StackErrUnderflow return } so = s.stk[sz-idx-1] if idx == 0 { s.stk = s.stk[:sz-1] } else if idx == sz-1 { s1 := make([][]byte, sz-1, sz-1) copy(s1, s.stk[1:]) s.stk = s1 } else { s1 := s.stk[sz-idx : sz] s.stk = s.stk[:sz-idx-1] s.stk = append(s.stk, s1...) } return } // NipN removes the Nth object on the stack func (s *Stack) NipN(idx int) error { _, err := s.nipN(idx) return err } // Tuck copies the item at the top of the stack and inserts it before the 2nd // to top item. e.g.: 2,1 -> 2,1,2 func (s *Stack) Tuck() error { so2, err := s.PopByteArray() if err != nil { return err } so1, err := s.PopByteArray() if err != nil { return err } s.PushByteArray(so2) // stack 2 s.PushByteArray(so1) // stack 1,2 s.PushByteArray(so2) // stack 2,1,2 return nil } // Depth returns the number of items on the stack. func (s *Stack) Depth() (sz int) { sz = len(s.stk) return } // DropN removes the top N items from the stack. // e.g. // DropN(1): 1,2,3 -> 1,2 // DropN(2): 1,2,3 -> 1 func (s *Stack) DropN(n int) error { if n < 1 { return StackErrInvalidArgs } for ; n > 0; n-- { _, err := s.PopByteArray() if err != nil { return err } } return nil } // DupN duplicates the top N items on the stack. // e.g. // DupN(1): 1,2,3 -> 1,2,3,3 // DupN(2): 1,2,3 -> 1,2,3,2,3 func (s *Stack) DupN(n int) error { if n < 1 { return StackErrInvalidArgs } // Iteratively duplicate the value n-1 down the stack n times. // this leaves us with an in-order duplicate of the top N items on the // stack. for i := n; i > 0; i-- { so, err := s.PeekByteArray(n - 1) if err != nil { return err } s.PushByteArray(so) } return nil } // RotN rotates the top 3N items on the stack to the left // e.g. // RotN(1): 1,2,3 -> 2,3,1 func (s *Stack) RotN(n int) error { if n < 1 { return StackErrInvalidArgs } entry := 3*n - 1 // Nip the 3n-1th item from the stack to the top n times to rotate // them up to the head of the stack. for i := n; i > 0; i-- { so, err := s.nipN(entry) if err != nil { return err } s.PushByteArray(so) } return nil } // SwapN swaps the top N items on the stack with those below them. // E.g.: // SwapN(1): 1,2 -> 2,1 // SwapN(2): 1,2,3,4 -> 3,4,1,2 func (s *Stack) SwapN(n int) error { if n < 1 { return StackErrInvalidArgs } entry := 2*n - 1 for i := n; i > 0; i-- { // swap 2n-1th entry to topj so, err := s.nipN(entry) if err != nil { return err } s.PushByteArray(so) } return nil } // OverN copies N items N spaces back to the top of the stack. // e.g.: // OverN(1): 1,2 -> 1,2,1 // OverN(2): 1,2,3,4 -> 1,2,3,4,1,2 func (s *Stack) OverN(n int) error { if n < 1 { return StackErrInvalidArgs } // Copy 2n-1th entry to top of the stack entry := 2*n - 1 for ; n > 0; n-- { so, err := s.PeekByteArray(entry) if err != nil { return err } s.PushByteArray(so) // 4,1,2,3,4, now code original 3rd entry to top. } return nil } // PickN copies the item N items back in the stack to the top. // e.g.: // PickN(1): 1,2,3 -> 1,2,3,2 // PickN(2): 1,2,3 -> 1,2,3,1 func (s *Stack) PickN(n int) error { so, err := s.PeekByteArray(n) if err != nil { return err } s.PushByteArray(so) return nil } // RollN moves the item N items back in the stack to the top. // e.g.: // RollN(1): 1,2,3 -> 1,3,2 // RollN(2): 1,2,3 -> 2,3,1 func (s *Stack) RollN(n int) error { so, err := s.nipN(n) if err != nil { return err } s.PushByteArray(so) return nil }