def run_script(script)

      if script.data.bytesize > @max_script_size
        return set_error(SCRIPT_ERR_SCRIPT_SIZE)
      end

      # Altstack is not shared between individual runs, but we still store it in ivar to make available to the extensions.
      @altstack = []
      @run_script_chunks = script.chunks.to_a.dup # can be modified by `insert_script`
      
      number_zero  = BTC::ScriptNumber.new(integer: 0)
      number_one   = BTC::ScriptNumber.new(integer: 1)
      zero_value = "".b
      false_value = "".b
      true_value = "\x01".b

      opcount = 0
      require_minimal = flag?(SCRIPT_VERIFY_MINIMALDATA)
      condition_flags = []
      index_after_codeseparator = 0
      chunk_index = -1 # start with -1 because we increment it in the beginning of the loop.
      
      while chunk = @run_script_chunks.shift
        chunk_index += 1
        
        opcode = chunk.opcode
        should_execute = !condition_flags.include?(false)

        if chunk.pushdata? && chunk.pushdata.bytesize > @max_pushdata_size
          return set_error(SCRIPT_ERR_PUSH_SIZE)
        end

        # Note how OP_RESERVED does not count towards the opcode limit.
        if opcode > OP_16
          if (opcount += 1) > @max_op_count
            return set_error(SCRIPT_ERR_OP_COUNT)
          end
        end
        
        # Check if there is a extension for this opcode before we check for disabled opcodes.
        opcode_extension = extension_to_handle_opcode(opcode)
        
        if !opcode_extension
          if opcode == OP_CAT ||
             opcode == OP_SUBSTR ||
             opcode == OP_LEFT ||
             opcode == OP_RIGHT ||
             opcode == OP_INVERT ||
             opcode == OP_AND ||
             opcode == OP_OR ||
             opcode == OP_XOR ||
             opcode == OP_2MUL ||
             opcode == OP_2DIV ||
             opcode == OP_MUL ||
             opcode == OP_DIV ||
             opcode == OP_MOD ||
             opcode == OP_LSHIFT ||
             opcode == OP_RSHIFT

            return set_error(SCRIPT_ERR_DISABLED_OPCODE)
          end
        end

        if should_execute && 0 <= opcode && opcode <= OP_PUSHDATA4
          # Pushdata (including OP_0).
          if require_minimal && !check_minimal_push(chunk.pushdata, opcode)
            return set_error(SCRIPT_ERR_MINIMALDATA)
          end
          stack_push(chunk.pushdata)

        elsif should_execute && opcode_extension
          
          if !opcode_extension.handle_opcode(interpreter: self, opcode: opcode)
            # error is set already
            return false
          end

        elsif should_execute || (OP_IF <= opcode && opcode <= OP_ENDIF)

          case opcode
          when OP_1NEGATE, OP_1..OP_16
            # ( -- value)
            num = BTC::ScriptNumber.new(integer: opcode - (OP_1 - 1))
            stack_push(num.data)
            # The result of these opcodes should always be the minimal way to push the data
            # they push, so no need for a CheckMinimalPush here.


          # Control Operators
          # -----------------

          when OP_NOP
            # nothing
            
          # See CLTVExtension
          # when OP_CHECKLOCKTIMEVERIFY
          #   begin
          #     if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
          #       # not enabled; treat as a NOP2
          #       if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
          #         return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
          #       end
          #       break # breaks out of begin ... end while false
          #     end
          # 
          #     if @stack.size < 1
          #       return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
          #     end
          # 
          #     # Note that elsewhere numeric opcodes are limited to
          #     # operands in the range -2**31+1 to 2**31-1, however it is
          #     # legal for opcodes to produce results exceeding that
          #     # range. This limitation is implemented by CScriptNum's
          #     # default 4-byte limit.
          #     #
          #     # If we kept to that limit we'd have a year 2038 problem,
          #     # even though the nLockTime field in transactions
          #     # themselves is uint32 which only becomes meaningless
          #     # after the year 2106.
          #     #
          #     # Thus as a special case we tell CScriptNum to accept up
          #     # to 5-byte bignums, which are good until 2**39-1, well
          #     # beyond the 2**32-1 limit of the nLockTime field itself.
          #     locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
          # 
          #     # In the rare event that the argument may be < 0 due to
          #     # some arithmetic being done first, you can always use
          #     # 0 MAX CHECKLOCKTIMEVERIFY.
          #     if locktime < 0
          #       return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
          #     end
          # 
          #     # Actually compare the specified lock time with the transaction.
          #     if !signature_checker.check_lock_time(locktime)
          #       return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
          #     end
          #   end while false

          when OP_NOP1..OP_NOP10
            if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
              return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
            end

          when OP_IF, OP_NOTIF
            # <expression> if [statements] [else [statements]] endif
            flag = false
            if should_execute
              if @stack.size < 1
                return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
              end
              flag = cast_to_bool(@stack.last)
              if opcode == OP_NOTIF
                flag = !flag
              end
              stack_pop
            end
            condition_flags.push(flag)

          when OP_ELSE
            if condition_flags.empty?
              return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
            end
            condition_flags[-1] = !condition_flags.last

          when OP_ENDIF
            if condition_flags.empty?
              return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
            end
            condition_flags.pop

          when OP_VERIFY
            # (true -- ) or
            # (false -- false) and return
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            flag = cast_to_bool(@stack.last)
            if flag
              stack_pop
            else
              return set_error(SCRIPT_ERR_VERIFY)
            end

          when OP_RETURN
            return set_error(SCRIPT_ERR_OP_RETURN)


          # Stack Operations
          # ----------------

          when OP_TOALTSTACK
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            @altstack.push(stack_pop)

          when OP_FROMALTSTACK
            if @altstack.size < 1
              return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION)
            end
            stack_push(@altstack.pop)

          when OP_2DROP
            # (x1 x2 -- )
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_pop
            stack_pop

          when OP_2DUP
            # (x1 x2 -- x1 x2 x1 x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-2]
            b = @stack[-1]
            stack_push(a)
            stack_push(b)

          when OP_3DUP
            # (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-3]
            b = @stack[-2]
            c = @stack[-1]
            stack_push(a)
            stack_push(b)
            stack_push(c)

          when OP_2OVER
            # (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
            if @stack.size < 4
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-4]
            b = @stack[-3]
            stack_push(a)
            stack_push(b)

          when OP_2ROT
            # (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
            if @stack.size < 6
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-6]
            b = @stack[-5]
            @stack[-6...-4] = []
            stack_push(a)
            stack_push(b)

          when OP_2SWAP
            # (x1 x2 x3 x4 -- x3 x4 x1 x2)
            if @stack.size < 4
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1x2 = @stack[-4...-2]
            @stack[-4...-2] = []
            @stack += x1x2

          when OP_IFDUP
            # (x - 0 | x x)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack.last
            if cast_to_bool(item)
              stack_push(item)
            end

          when OP_DEPTH
            # -- stacksize
            sn = BTC::ScriptNumber.new(integer: @stack.size)
            stack_push(sn.data)

          when OP_DROP
            # (x -- )
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_pop

          when OP_DUP
            # (x -- x x)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_push(@stack.last)

          when OP_NIP
            # (x1 x2 -- x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            @stack.delete_at(-2)

          when OP_OVER
            # (x1 x2 -- x1 x2 x1)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_push(@stack[-2])

          when OP_PICK, OP_ROLL
            # (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
            # (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            n = cast_to_number(@stack.last).to_i
            stack_pop
            if n < 0 || n >= @stack.size
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-n-1]
            if opcode == OP_ROLL
              @stack.delete_at(-n-1)
            end
            stack_push(item)

          when OP_ROT
            # (x1 x2 x3 -- x2 x3 x1)
            #  x2 x1 x3  after first swap
            #  x2 x3 x1  after second swap
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1 = @stack[-3]
            @stack.delete_at(-3)
            stack_push(x1)

          when OP_SWAP
            # (x1 x2 -- x2 x1)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1 = @stack[-2]
            @stack.delete_at(-2)
            stack_push(x1)

          when OP_TUCK
            # (x1 x2 -- x2 x1 x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-1]
            @stack.insert(-3, item) # -1 inserts in the end, -2 before the last item.

          when OP_SIZE
            # (in -- in size)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            sn = BTC::ScriptNumber.new(integer: @stack.last.size)
            stack_push(sn.data)


          # Bitwise Logic
          # -------------

          when OP_EQUAL, OP_EQUALVERIFY
          # there is no OP_NOTEQUAL, use OP_NUMNOTEQUAL instead
            # (x1 x2 - bool)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-2]
            b = @stack[-1]
            equal = (a == b)
            # OP_NOTEQUAL is disabled because it would be too easy to say
            # something like n != 1 and have some wiseguy pass in 1 with extra
            # zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
            # if opcode == OP_NOTEQUAL
            #   equal = !equal
            # end
            stack_pop
            stack_pop
            stack_push(equal ? true_value : false_value)
            if opcode == OP_EQUALVERIFY
              if equal
                stack_pop
              else
                return set_error(SCRIPT_ERR_EQUALVERIFY)
              end
            end


          # Numeric
          # -------

          when OP_1ADD,
               OP_1SUB,
               OP_NEGATE,
               OP_ABS,
               OP_NOT,
               OP_0NOTEQUAL
            # (in -- out)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            bn = cast_to_number(@stack.last)
            case opcode
            when OP_1ADD
              bn += 1
            when OP_1SUB
              bn -= 1
            when OP_NEGATE
              bn = -bn
            when OP_ABS
              bn = -bn if bn < 0
            when OP_NOT
              bn = BTC::ScriptNumber.new(boolean: (bn == 0))
            when OP_0NOTEQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn != 0))
            else
              raise "invalid opcode"
            end
            stack_pop
            stack_push(bn.data);


          when OP_ADD, OP_SUB, OP_BOOLAND..OP_MAX
            # (x1 x2 -- out)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            bn1 = cast_to_number(@stack[-2])
            bn2 = cast_to_number(@stack[-1])
            bn = BTC::ScriptNumber.new(integer: 0)

            case opcode
            when OP_ADD
              bn = bn1 + bn2
            when OP_SUB
              bn = bn1 - bn2
            when OP_BOOLAND
              bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
            when OP_BOOLOR
              bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
            when OP_NUMEQUAL, OP_NUMEQUALVERIFY
              bn = BTC::ScriptNumber.new(boolean: (bn1 == bn2))
            when OP_NUMNOTEQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 != bn2))
            when OP_LESSTHAN
              bn = BTC::ScriptNumber.new(boolean: (bn1 < bn2))
            when OP_GREATERTHAN
              bn = BTC::ScriptNumber.new(boolean: (bn1 > bn2))
            when OP_LESSTHANOREQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 <= bn2))
            when OP_GREATERTHANOREQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 >= bn2))
            when OP_MIN
              bn = (bn1 < bn2 ? bn1 : bn2)
            when OP_MAX
              bn = (bn1 > bn2 ? bn1 : bn2)
            else
              raise "Invalid opcode"
            end
            stack_pop
            stack_pop
            stack_push(bn.data)

            if opcode == OP_NUMEQUALVERIFY
              if cast_to_bool(@stack[-1])
                stack_pop
              else
                return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
              end
            end

          when OP_WITHIN
            # (x min max -- out)
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            bn1 = cast_to_number(@stack[-3])
            bn2 = cast_to_number(@stack[-2])
            bn3 = cast_to_number(@stack[-1])
            flag = ((bn2 <= bn1) && (bn1 < bn3))
            stack_pop
            stack_pop
            stack_pop
            stack_push(flag ? true_value : false_value)

          # Crypto
          # ------

          when OP_RIPEMD160..OP_HASH256
            # (in -- hash)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-1]
            hash = case opcode
            when OP_RIPEMD160
              BTC.ripemd160(item)
            when OP_SHA1
              BTC.sha1(item)
            when OP_SHA256
              BTC.sha256(item)
            when OP_HASH160
              BTC.hash160(item)
            when OP_HASH256
              BTC.hash256(item)
            end
            stack_pop
            stack_push(hash)

          when OP_CODESEPARATOR
            # Hash starts after the code separator
            index_after_codeseparator = chunk_index + 1

          when OP_CHECKSIG, OP_CHECKSIGVERIFY
            # (sig pubkey -- bool)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            sig    = @stack[-2]
            pubkey = @stack[-1]

            # Subset of script starting at the most recent codeseparator
            subscript = script.subscript(index_after_codeseparator..-1)

            # Drop the signature, since there's no way for a signature to sign itself
            # Consensus-critical code: must replace sig with minimal encoding.
            subscript = subscript.find_and_delete(BTC::Script.new << sig)

            if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
              # error is set already
              return false
            end

            success = signature_checker.check_signature(
              script_signature: sig,
              public_key:       pubkey,
              script:           subscript
            )

            stack_pop
            stack_pop
            stack_push(success ? true_value : false_value)

            if opcode == OP_CHECKSIGVERIFY
              if success
                stack_pop
              else
                return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
              end
            end


          when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
            # ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)

            i = 1
            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            keys_count = cast_to_number(@stack[-i]).to_i
            if keys_count < 0 || keys_count > 20
              return set_error(SCRIPT_ERR_PUBKEY_COUNT)
            end
            opcount += keys_count
            if opcount > @max_op_count
              return set_error(SCRIPT_ERR_OP_COUNT)
            end
            ikey = (i+=1)
            i += keys_count
            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            sigs_count = cast_to_number(@stack[-i]).to_i
            if sigs_count < 0 || sigs_count > keys_count
              return set_error(SCRIPT_ERR_SIG_COUNT)
            end

            isig = (i+=1)
            i += sigs_count

            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            # Subset of script starting at the most recent codeseparator

            # Subset of script starting at the most recent codeseparator
            subscript = script.subscript(index_after_codeseparator..-1)

            # Drop the signatures, since there's no way for a signature to sign itself
            # Consensus-critical code: must replace sig with minimal encoding.
            sigs_count.times do |k|
              sig = @stack[-isig-k]
              subscript = subscript.find_and_delete(BTC::Script.new << sig)
            end

            success = true
            while success && sigs_count > 0
              sig = @stack[-isig]
              pubkey = @stack[-ikey]
              # Note how this makes the exact order of pubkey/signature evaluation
              # distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
              # See the script_(in)valid tests for details.
              if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
                # error is set already
                return false
              end

              # Check signature
              ok = signature_checker.check_signature(
                script_signature: sig,
                public_key:       pubkey,
                script:           subscript
              )
              if ok
                isig += 1
                sigs_count -= 1
              end
              ikey += 1
              keys_count -= 1

              # If there are more signatures left than keys left,
              # then too many signatures have failed. Exit early,
              # without checking any further signatures.
              if sigs_count > keys_count
                success = false
              end
            end

            # Clean up stack of actual arguments
            while (i-=1) > 0  # Bitcoin Core: while (i-- > 1)
              stack_pop
            end

            # A bug causes CHECKMULTISIG to consume one extra argument
            # whose contents were not checked in any way.
            #
            # Unfortunately this is a potential source of mutability,
            # so optionally verify it is exactly equal to zero prior
            # to removing it from the stack.
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            if flag?(SCRIPT_VERIFY_NULLDUMMY) && @stack[-1].size > 0
              return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
            end
            stack_pop
            stack_push(success ? true_value : false_value)
            if opcode == OP_CHECKMULTISIGVERIFY
              if success
                stack_pop
              else
                return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
              end
            end

          else # unknown opcode
            return set_error(SCRIPT_ERR_BAD_OPCODE, "Unknown opcode 0x#{opcode.to_s(16)}")

          end # case opcode
        end # within IF scope

        # Size limits
        if @stack.size + @altstack.size > @max_stack_size
          return set_error(SCRIPT_ERR_STACK_SIZE)
        end

      end # each chunk

      if !condition_flags.empty?
        return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
      end

      return true
    rescue BTC::ScriptNumberError => e
      return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
    end
def run_script(script)

      if script.data.bytesize > @max_script_size
        return set_error(SCRIPT_ERR_SCRIPT_SIZE)
      end

      # Altstack is not shared between individual runs, but we still store it in ivar to make available to the extensions.
      @altstack = []
      @run_script_chunks = script.chunks.to_a.dup # can be modified by `insert_script`
      
      number_zero  = BTC::ScriptNumber.new(integer: 0)
      number_one   = BTC::ScriptNumber.new(integer: 1)
      zero_value = "".b
      false_value = "".b
      true_value = "\x01".b

      opcount = 0
      require_minimal = flag?(SCRIPT_VERIFY_MINIMALDATA)
      condition_flags = []
      index_after_codeseparator = 0
      chunk_index = -1 # start with -1 because we increment it in the beginning of the loop.
      
      while chunk = @run_script_chunks.shift
        chunk_index += 1
        
        opcode = chunk.opcode
        should_execute = !condition_flags.include?(false)

        if chunk.pushdata? && chunk.pushdata.bytesize > @max_pushdata_size
          return set_error(SCRIPT_ERR_PUSH_SIZE)
        end

        # Note how OP_RESERVED does not count towards the opcode limit.
        if opcode > OP_16
          if (opcount += 1) > @max_op_count
            return set_error(SCRIPT_ERR_OP_COUNT)
          end
        end
        
        # Check if there is a extension for this opcode before we check for disabled opcodes.
        opcode_extension = extension_to_handle_opcode(opcode)
        
        if !opcode_extension
          if opcode == OP_CAT ||
             opcode == OP_SUBSTR ||
             opcode == OP_LEFT ||
             opcode == OP_RIGHT ||
             opcode == OP_INVERT ||
             opcode == OP_AND ||
             opcode == OP_OR ||
             opcode == OP_XOR ||
             opcode == OP_2MUL ||
             opcode == OP_2DIV ||
             opcode == OP_MUL ||
             opcode == OP_DIV ||
             opcode == OP_MOD ||
             opcode == OP_LSHIFT ||
             opcode == OP_RSHIFT

            return set_error(SCRIPT_ERR_DISABLED_OPCODE)
          end
        end

        if should_execute && 0 <= opcode && opcode <= OP_PUSHDATA4
          # Pushdata (including OP_0).
          if require_minimal && !check_minimal_push(chunk.pushdata, opcode)
            return set_error(SCRIPT_ERR_MINIMALDATA)
          end
          stack_push(chunk.pushdata)

        elsif should_execute && opcode_extension
          
          if !opcode_extension.handle_opcode(interpreter: self, opcode: opcode)
            # error is set already
            return false
          end

        elsif should_execute || (OP_IF <= opcode && opcode <= OP_ENDIF)

          case opcode
          when OP_1NEGATE, OP_1..OP_16
            # ( -- value)
            num = BTC::ScriptNumber.new(integer: opcode - (OP_1 - 1))
            stack_push(num.data)
            # The result of these opcodes should always be the minimal way to push the data
            # they push, so no need for a CheckMinimalPush here.


          # Control Operators
          # -----------------

          when OP_NOP
            # nothing
            
          # See CLTVExtension
          # when OP_CHECKLOCKTIMEVERIFY
          #   begin
          #     if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
          #       # not enabled; treat as a NOP2
          #       if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
          #         return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
          #       end
          #       break # breaks out of begin ... end while false
          #     end
          # 
          #     if @stack.size < 1
          #       return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
          #     end
          # 
          #     # Note that elsewhere numeric opcodes are limited to
          #     # operands in the range -2**31+1 to 2**31-1, however it is
          #     # legal for opcodes to produce results exceeding that
          #     # range. This limitation is implemented by CScriptNum's
          #     # default 4-byte limit.
          #     #
          #     # If we kept to that limit we'd have a year 2038 problem,
          #     # even though the nLockTime field in transactions
          #     # themselves is uint32 which only becomes meaningless
          #     # after the year 2106.
          #     #
          #     # Thus as a special case we tell CScriptNum to accept up
          #     # to 5-byte bignums, which are good until 2**39-1, well
          #     # beyond the 2**32-1 limit of the nLockTime field itself.
          #     locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
          # 
          #     # In the rare event that the argument may be < 0 due to
          #     # some arithmetic being done first, you can always use
          #     # 0 MAX CHECKLOCKTIMEVERIFY.
          #     if locktime < 0
          #       return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
          #     end
          # 
          #     # Actually compare the specified lock time with the transaction.
          #     if !signature_checker.check_lock_time(locktime)
          #       return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
          #     end
          #   end while false

          when OP_NOP1..OP_NOP10
            if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
              return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
            end

          when OP_IF, OP_NOTIF
            # <expression> if [statements] [else [statements]] endif
            flag = false
            if should_execute
              if @stack.size < 1
                return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
              end
              flag = cast_to_bool(@stack.last)
              if opcode == OP_NOTIF
                flag = !flag
              end
              stack_pop
            end
            condition_flags.push(flag)

          when OP_ELSE
            if condition_flags.empty?
              return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
            end
            condition_flags[-1] = !condition_flags.last

          when OP_ENDIF
            if condition_flags.empty?
              return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
            end
            condition_flags.pop

          when OP_VERIFY
            # (true -- ) or
            # (false -- false) and return
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            flag = cast_to_bool(@stack.last)
            if flag
              stack_pop
            else
              return set_error(SCRIPT_ERR_VERIFY)
            end

          when OP_RETURN
            return set_error(SCRIPT_ERR_OP_RETURN)


          # Stack Operations
          # ----------------

          when OP_TOALTSTACK
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            @altstack.push(stack_pop)

          when OP_FROMALTSTACK
            if @altstack.size < 1
              return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION)
            end
            stack_push(@altstack.pop)

          when OP_2DROP
            # (x1 x2 -- )
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_pop
            stack_pop

          when OP_2DUP
            # (x1 x2 -- x1 x2 x1 x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-2]
            b = @stack[-1]
            stack_push(a)
            stack_push(b)

          when OP_3DUP
            # (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-3]
            b = @stack[-2]
            c = @stack[-1]
            stack_push(a)
            stack_push(b)
            stack_push(c)

          when OP_2OVER
            # (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
            if @stack.size < 4
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-4]
            b = @stack[-3]
            stack_push(a)
            stack_push(b)

          when OP_2ROT
            # (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
            if @stack.size < 6
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-6]
            b = @stack[-5]
            @stack[-6...-4] = []
            stack_push(a)
            stack_push(b)

          when OP_2SWAP
            # (x1 x2 x3 x4 -- x3 x4 x1 x2)
            if @stack.size < 4
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1x2 = @stack[-4...-2]
            @stack[-4...-2] = []
            @stack += x1x2

          when OP_IFDUP
            # (x - 0 | x x)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack.last
            if cast_to_bool(item)
              stack_push(item)
            end

          when OP_DEPTH
            # -- stacksize
            sn = BTC::ScriptNumber.new(integer: @stack.size)
            stack_push(sn.data)

          when OP_DROP
            # (x -- )
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_pop

          when OP_DUP
            # (x -- x x)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_push(@stack.last)

          when OP_NIP
            # (x1 x2 -- x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            @stack.delete_at(-2)

          when OP_OVER
            # (x1 x2 -- x1 x2 x1)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            stack_push(@stack[-2])

          when OP_PICK, OP_ROLL
            # (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
            # (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            n = cast_to_number(@stack.last).to_i
            stack_pop
            if n < 0 || n >= @stack.size
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-n-1]
            if opcode == OP_ROLL
              @stack.delete_at(-n-1)
            end
            stack_push(item)

          when OP_ROT
            # (x1 x2 x3 -- x2 x3 x1)
            #  x2 x1 x3  after first swap
            #  x2 x3 x1  after second swap
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1 = @stack[-3]
            @stack.delete_at(-3)
            stack_push(x1)

          when OP_SWAP
            # (x1 x2 -- x2 x1)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            x1 = @stack[-2]
            @stack.delete_at(-2)
            stack_push(x1)

          when OP_TUCK
            # (x1 x2 -- x2 x1 x2)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-1]
            @stack.insert(-3, item) # -1 inserts in the end, -2 before the last item.

          when OP_SIZE
            # (in -- in size)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            sn = BTC::ScriptNumber.new(integer: @stack.last.size)
            stack_push(sn.data)


          # Bitwise Logic
          # -------------

          when OP_EQUAL, OP_EQUALVERIFY
          # there is no OP_NOTEQUAL, use OP_NUMNOTEQUAL instead
            # (x1 x2 - bool)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            a = @stack[-2]
            b = @stack[-1]
            equal = (a == b)
            # OP_NOTEQUAL is disabled because it would be too easy to say
            # something like n != 1 and have some wiseguy pass in 1 with extra
            # zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
            # if opcode == OP_NOTEQUAL
            #   equal = !equal
            # end
            stack_pop
            stack_pop
            stack_push(equal ? true_value : false_value)
            if opcode == OP_EQUALVERIFY
              if equal
                stack_pop
              else
                return set_error(SCRIPT_ERR_EQUALVERIFY)
              end
            end


          # Numeric
          # -------

          when OP_1ADD,
               OP_1SUB,
               OP_NEGATE,
               OP_ABS,
               OP_NOT,
               OP_0NOTEQUAL
            # (in -- out)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            bn = cast_to_number(@stack.last)
            case opcode
            when OP_1ADD
              bn += 1
            when OP_1SUB
              bn -= 1
            when OP_NEGATE
              bn = -bn
            when OP_ABS
              bn = -bn if bn < 0
            when OP_NOT
              bn = BTC::ScriptNumber.new(boolean: (bn == 0))
            when OP_0NOTEQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn != 0))
            else
              raise "invalid opcode"
            end
            stack_pop
            stack_push(bn.data);


          when OP_ADD, OP_SUB, OP_BOOLAND..OP_MAX
            # (x1 x2 -- out)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            bn1 = cast_to_number(@stack[-2])
            bn2 = cast_to_number(@stack[-1])
            bn = BTC::ScriptNumber.new(integer: 0)

            case opcode
            when OP_ADD
              bn = bn1 + bn2
            when OP_SUB
              bn = bn1 - bn2
            when OP_BOOLAND
              bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
            when OP_BOOLOR
              bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
            when OP_NUMEQUAL, OP_NUMEQUALVERIFY
              bn = BTC::ScriptNumber.new(boolean: (bn1 == bn2))
            when OP_NUMNOTEQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 != bn2))
            when OP_LESSTHAN
              bn = BTC::ScriptNumber.new(boolean: (bn1 < bn2))
            when OP_GREATERTHAN
              bn = BTC::ScriptNumber.new(boolean: (bn1 > bn2))
            when OP_LESSTHANOREQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 <= bn2))
            when OP_GREATERTHANOREQUAL
              bn = BTC::ScriptNumber.new(boolean: (bn1 >= bn2))
            when OP_MIN
              bn = (bn1 < bn2 ? bn1 : bn2)
            when OP_MAX
              bn = (bn1 > bn2 ? bn1 : bn2)
            else
              raise "Invalid opcode"
            end
            stack_pop
            stack_pop
            stack_push(bn.data)

            if opcode == OP_NUMEQUALVERIFY
              if cast_to_bool(@stack[-1])
                stack_pop
              else
                return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
              end
            end

          when OP_WITHIN
            # (x min max -- out)
            if @stack.size < 3
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            bn1 = cast_to_number(@stack[-3])
            bn2 = cast_to_number(@stack[-2])
            bn3 = cast_to_number(@stack[-1])
            flag = ((bn2 <= bn1) && (bn1 < bn3))
            stack_pop
            stack_pop
            stack_pop
            stack_push(flag ? true_value : false_value)

          # Crypto
          # ------

          when OP_RIPEMD160..OP_HASH256
            # (in -- hash)
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end
            item = @stack[-1]
            hash = case opcode
            when OP_RIPEMD160
              BTC.ripemd160(item)
            when OP_SHA1
              BTC.sha1(item)
            when OP_SHA256
              BTC.sha256(item)
            when OP_HASH160
              BTC.hash160(item)
            when OP_HASH256
              BTC.hash256(item)
            end
            stack_pop
            stack_push(hash)

          when OP_CODESEPARATOR
            # Hash starts after the code separator
            index_after_codeseparator = chunk_index + 1

          when OP_CHECKSIG, OP_CHECKSIGVERIFY
            # (sig pubkey -- bool)
            if @stack.size < 2
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            sig    = @stack[-2]
            pubkey = @stack[-1]

            # Subset of script starting at the most recent codeseparator
            subscript = script.subscript(index_after_codeseparator..-1)

            # Drop the signature, since there's no way for a signature to sign itself
            # Consensus-critical code: must replace sig with minimal encoding.
            subscript = subscript.find_and_delete(BTC::Script.new << sig)

            if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
              # error is set already
              return false
            end

            success = signature_checker.check_signature(
              script_signature: sig,
              public_key:       pubkey,
              script:           subscript
            )

            stack_pop
            stack_pop
            stack_push(success ? true_value : false_value)

            if opcode == OP_CHECKSIGVERIFY
              if success
                stack_pop
              else
                return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
              end
            end


          when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
            # ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)

            i = 1
            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            keys_count = cast_to_number(@stack[-i]).to_i
            if keys_count < 0 || keys_count > 20
              return set_error(SCRIPT_ERR_PUBKEY_COUNT)
            end
            opcount += keys_count
            if opcount > @max_op_count
              return set_error(SCRIPT_ERR_OP_COUNT)
            end
            ikey = (i+=1)
            i += keys_count
            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            sigs_count = cast_to_number(@stack[-i]).to_i
            if sigs_count < 0 || sigs_count > keys_count
              return set_error(SCRIPT_ERR_SIG_COUNT)
            end

            isig = (i+=1)
            i += sigs_count

            if @stack.size < i
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            # Subset of script starting at the most recent codeseparator

            # Subset of script starting at the most recent codeseparator
            subscript = script.subscript(index_after_codeseparator..-1)

            # Drop the signatures, since there's no way for a signature to sign itself
            # Consensus-critical code: must replace sig with minimal encoding.
            sigs_count.times do |k|
              sig = @stack[-isig-k]
              subscript = subscript.find_and_delete(BTC::Script.new << sig)
            end

            success = true
            while success && sigs_count > 0
              sig = @stack[-isig]
              pubkey = @stack[-ikey]
              # Note how this makes the exact order of pubkey/signature evaluation
              # distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
              # See the script_(in)valid tests for details.
              if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
                # error is set already
                return false
              end

              # Check signature
              ok = signature_checker.check_signature(
                script_signature: sig,
                public_key:       pubkey,
                script:           subscript
              )
              if ok
                isig += 1
                sigs_count -= 1
              end
              ikey += 1
              keys_count -= 1

              # If there are more signatures left than keys left,
              # then too many signatures have failed. Exit early,
              # without checking any further signatures.
              if sigs_count > keys_count
                success = false
              end
            end

            # Clean up stack of actual arguments
            while (i-=1) > 0  # Bitcoin Core: while (i-- > 1)
              stack_pop
            end

            # A bug causes CHECKMULTISIG to consume one extra argument
            # whose contents were not checked in any way.
            #
            # Unfortunately this is a potential source of mutability,
            # so optionally verify it is exactly equal to zero prior
            # to removing it from the stack.
            if @stack.size < 1
              return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
            end

            if flag?(SCRIPT_VERIFY_NULLDUMMY) && @stack[-1].size > 0
              return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
            end
            stack_pop
            stack_push(success ? true_value : false_value)
            if opcode == OP_CHECKMULTISIGVERIFY
              if success
                stack_pop
              else
                return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
              end
            end

          else # unknown opcode
            return set_error(SCRIPT_ERR_BAD_OPCODE, "Unknown opcode 0x#{opcode.to_s(16)}")

          end # case opcode
        end # within IF scope

        # Size limits
        if @stack.size + @altstack.size > @max_stack_size
          return set_error(SCRIPT_ERR_STACK_SIZE)
        end

      end # each chunk

      if !condition_flags.empty?
        return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
      end

      return true
    rescue BTC::ScriptNumberError => e
      return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
    end
Returns true if succeeded or false in case of failure.
 If fails, sets the error attribute.
 Used internally in `verify_script` and also in unit tests.