123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- // Copyright (c) 2013-2017 The btcsuite developers
- // Use of this source code is governed by an ISC
- // license that can be found in the LICENSE file.
- package txscript
- import (
- "encoding/hex"
- "fmt"
- "github.com/pkt-cash/pktd/btcutil/er"
- "github.com/pkt-cash/pktd/txscript/scriptnum"
- "github.com/pkt-cash/pktd/txscript/txscripterr"
- )
- // asBool gets the boolean value of the byte array.
- func asBool(t []byte) bool {
- for i := range t {
- if t[i] != 0 {
- // Negative 0 is also considered false.
- if i == len(t)-1 && t[i] == 0x80 {
- return false
- }
- return true
- }
- }
- return false
- }
- // fromBool converts a boolean into the appropriate byte array.
- func fromBool(v bool) []byte {
- if v {
- return []byte{1}
- }
- return nil
- }
- // 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
- verifyMinimalData bool
- }
- // Depth returns the number of items on the stack.
- func (s *stack) Depth() int32 {
- return int32(len(s.stk))
- }
- // PushByteArray adds the given back array to the top of the stack.
- //
- // Stack transformation: [... x1 x2] -> [... x1 x2 data]
- func (s *stack) PushByteArray(so []byte) {
- s.stk = append(s.stk, so)
- }
- // PushInt converts the provided scriptNum to a suitable byte array then pushes
- // it onto the top of the stack.
- //
- // Stack transformation: [... x1 x2] -> [... x1 x2 int]
- func (s *stack) PushInt(val scriptnum.ScriptNum) {
- s.PushByteArray(val.Bytes())
- }
- // PushBool converts the provided boolean to a suitable byte array then pushes
- // it onto the top of the stack.
- //
- // Stack transformation: [... x1 x2] -> [... x1 x2 bool]
- func (s *stack) PushBool(val bool) {
- s.PushByteArray(fromBool(val))
- }
- // PopByteArray pops the value off the top of the stack and returns it.
- //
- // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
- func (s *stack) PopByteArray() ([]byte, er.R) {
- return s.nipN(0)
- }
- // PopInt pops the value off the top of the stack, converts it into a script
- // num, and returns it. The act of converting to a script num enforces the
- // consensus rules imposed on data interpreted as numbers.
- //
- // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
- func (s *stack) PopInt() (scriptnum.ScriptNum, er.R) {
- so, err := s.PopByteArray()
- if err != nil {
- return 0, err
- }
- return scriptnum.MakeScriptNum(so, s.verifyMinimalData, scriptnum.DefaultScriptNumLen)
- }
- // PopBool pops the value off the top of the stack, converts it into a bool, and
- // returns it.
- //
- // Stack transformation: [... x1 x2 x3] -> [... x1 x2]
- func (s *stack) PopBool() (bool, er.R) {
- 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 int32) ([]byte, er.R) {
- sz := int32(len(s.stk))
- if idx < 0 || idx >= sz {
- str := fmt.Sprintf("index %d is invalid for stack size %d", idx,
- sz)
- return nil, txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- return s.stk[sz-idx-1], nil
- }
- // PeekInt returns the Nth item on the stack as a script num without removing
- // it. The act of converting to a script num enforces the consensus rules
- // imposed on data interpreted as numbers.
- func (s *stack) PeekInt(idx int32) (scriptnum.ScriptNum, er.R) {
- so, err := s.PeekByteArray(idx)
- if err != nil {
- return 0, err
- }
- return scriptnum.MakeScriptNum(so, s.verifyMinimalData, scriptnum.DefaultScriptNumLen)
- }
- // PeekBool returns the Nth item on the stack as a bool without removing it.
- func (s *stack) PeekBool(idx int32) (bool, er.R) {
- 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.
- //
- // Stack transformation:
- // nipN(0): [... x1 x2 x3] -> [... x1 x2]
- // nipN(1): [... x1 x2 x3] -> [... x1 x3]
- // nipN(2): [... x1 x2 x3] -> [... x2 x3]
- func (s *stack) nipN(idx int32) ([]byte, er.R) {
- sz := int32(len(s.stk))
- if idx < 0 || idx > sz-1 {
- str := fmt.Sprintf("index %d is invalid for stack size %d", idx,
- sz)
- return nil, txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- 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)
- 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 so, nil
- }
- // NipN removes the Nth object on the stack
- //
- // Stack transformation:
- // NipN(0): [... x1 x2 x3] -> [... x1 x2]
- // NipN(1): [... x1 x2 x3] -> [... x1 x3]
- // NipN(2): [... x1 x2 x3] -> [... x2 x3]
- func (s *stack) NipN(idx int32) er.R {
- _, 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.
- //
- // Stack transformation: [... x1 x2] -> [... x2 x1 x2]
- func (s *stack) Tuck() er.R {
- so2, err := s.PopByteArray()
- if err != nil {
- return err
- }
- so1, err := s.PopByteArray()
- if err != nil {
- return err
- }
- s.PushByteArray(so2) // stack [... x2]
- s.PushByteArray(so1) // stack [... x2 x1]
- s.PushByteArray(so2) // stack [... x2 x1 x2]
- return nil
- }
- // DropN removes the top N items from the stack.
- //
- // Stack transformation:
- // DropN(1): [... x1 x2] -> [... x1]
- // DropN(2): [... x1 x2] -> [...]
- func (s *stack) DropN(n int32) er.R {
- if n < 1 {
- str := fmt.Sprintf("attempt to drop %d items from stack", n)
- return txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- for ; n > 0; n-- {
- _, err := s.PopByteArray()
- if err != nil {
- return err
- }
- }
- return nil
- }
- // DupN duplicates the top N items on the stack.
- //
- // Stack transformation:
- // DupN(1): [... x1 x2] -> [... x1 x2 x2]
- // DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
- func (s *stack) DupN(n int32) er.R {
- if n < 1 {
- str := fmt.Sprintf("attempt to dup %d stack items", n)
- return txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- // Iteratively duplicate the value n-1 down the stack n times.
- // This leaves 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 N times.
- //
- // Stack transformation:
- // RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
- // RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
- func (s *stack) RotN(n int32) er.R {
- if n < 1 {
- str := fmt.Sprintf("attempt to rotate %d stack items", n)
- return txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- // Nip the 3n-1th item from the stack to the top n times to rotate
- // them up to the head of the stack.
- entry := 3*n - 1
- 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.
- //
- // Stack transformation:
- // SwapN(1): [... x1 x2] -> [... x2 x1]
- // SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
- func (s *stack) SwapN(n int32) er.R {
- if n < 1 {
- str := fmt.Sprintf("attempt to swap %d stack items", n)
- return txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- entry := 2*n - 1
- for i := n; i > 0; i-- {
- // Swap 2n-1th entry to top.
- so, err := s.nipN(entry)
- if err != nil {
- return err
- }
- s.PushByteArray(so)
- }
- return nil
- }
- // OverN copies N items N items back to the top of the stack.
- //
- // Stack transformation:
- // OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
- // OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
- func (s *stack) OverN(n int32) er.R {
- if n < 1 {
- str := fmt.Sprintf("attempt to perform over on %d stack items",
- n)
- return txscripterr.ScriptError(txscripterr.ErrInvalidStackOperation, str)
- }
- // 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)
- }
- return nil
- }
- // PickN copies the item N items back in the stack to the top.
- //
- // Stack transformation:
- // PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
- // PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
- // PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
- func (s *stack) PickN(n int32) er.R {
- 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.
- //
- // Stack transformation:
- // RollN(0): [x1 x2 x3] -> [x1 x2 x3]
- // RollN(1): [x1 x2 x3] -> [x1 x3 x2]
- // RollN(2): [x1 x2 x3] -> [x2 x3 x1]
- func (s *stack) RollN(n int32) er.R {
- so, err := s.nipN(n)
- if err != nil {
- return err
- }
- s.PushByteArray(so)
- return nil
- }
- // String returns the stack in a readable format.
- func (s *stack) String() string {
- var result string
- for _, stack := range s.stk {
- if len(stack) == 0 {
- result += "00000000 <empty>\n"
- }
- result += hex.Dump(stack)
- }
- return result
- }
|