def build

      if !self.change_address
        raise MissingChangeAddressError
      end

      result = Result.new

      add_input_for_utxo = proc do |utxo|
        raise ArgumentError, "Unspent output must contain index" if !utxo.index
        raise ArgumentError, "Unspent output must contain transaction_hash" if !utxo.transaction_hash

        if !self.internal_provider.consumed_unspent_output?(utxo)
          self.internal_provider.consume_unspent_output(utxo)

          result.inputs_amount += utxo.value
          txin = TransactionInput.new(
                                      previous_hash: utxo.transaction_hash,
                                      previous_index: utxo.index,
                                      # put the output script here so the signer knows which key to use.
                                      signature_script: utxo.script)
          txin.transaction_output = utxo
          result.transaction.add_input(txin)
        else
          # UTXO was already consumed possibly by another Tx Builder sharing the same provider.
        end
      end

      # If no outputs are specified, spend all utxos to a change address, minus the mining fee.
      if (self.outputs || []).size == 0
        result.inputs_amount = 0

        (self.prepended_unspent_outputs || []).each(&add_input_for_utxo)
        (self.unspent_outputs || []).each(&add_input_for_utxo)

        if result.transaction.inputs.size == 0
          raise MissingUnspentOutputsError, "Missing unspent outputs"
        end

        # Value will be determined after computing the fee
        change_output = TransactionOutput.new(value: 0, script: self.change_address.public_address.script)
        result.transaction.add_output(change_output)

        result.fee = compute_fee_for_transaction(result.transaction, self.fee_rate)
        result.outputs_amount = result.inputs_amount - result.fee
        result.change_amount = 0

        # Check if inputs cover the fees
        if result.outputs_amount < 0
          raise InsufficientFundsError.new(result)
        end

        # Warn if the output amount is relatively small.
        if result.outputs_amount < result.fee
          Diagnostics.current.add_message("BTC::TransactionBuilder: Warning! Spending all unspent outputs returns less than a mining fee. (#{result.outputs_amount} < #{result.fee})")
        end

        # Set the output value as needed
        result.transaction.outputs[0].value = result.outputs_amount

        # For each address that is a WIF, locate the matching input and sign it.
        attempt_to_sign_transaction(result)

        return result
      end # if no specific outputs required

      # We are having one or more outputs (e.g. normal payment)
      # Need to find appropriate unspents and compose a transaction.

      # Prepare all outputs.
      # result.outputs_amount will also contain a fee after it's calculated.
      (self.outputs || []).each do |txout|
        result.outputs_amount += txout.value
        result.transaction.add_output(txout)
      end

      # We'll determine final change value depending on inputs.
      # Setting default to MAX_MONEY will protect against a bug when we fail to update the amount and
      # spend unexpected amount on mining fees.
      change_output = TransactionOutput.new(value: MAX_MONEY, script: self.change_address.public_address.script)
      result.transaction.add_output(change_output)

      mandatory_utxos = (self.prepended_unspent_outputs || []).to_a.dup

      # We have specific outputs with specific amounts, so we need to select the best amount of coins.
      # To play nice with BIP32/BIP44 change addresses (that need to monitor an increasing amount of addresses),
      # we'll spend oldest outputs first and will add more and more newer outputs until we cover fees
      # and change output is either zero or above the dust limit.
      # If `confirmations` attribute is nil, order is preserved.
      utxos = self.unspent_outputs || []
      if self.keep_unspent_outputs_order
        sorted_utxos = utxos.to_a.dup
      else
        sorted_utxos = utxos.to_a.sort_by{|txout| -(txout.confirmations || -1) } # oldest first
      end
      
      if self.parent_transactions
        # Can repeat some outputs in mandatory_utxos or sorted_utxos, 
        # but double-spending will be prevented by provider.
        self.parent_transactions.each do |parenttx|
          sorted_utxos += parenttx.outputs
        end
      end
      
      all_utxos = (sorted_utxos + mandatory_utxos)

      while true
        if (sorted_utxos.size + mandatory_utxos.size) == 0
          raise InsufficientFundsError.new(result)
        end

        utxo = nil
        while (sorted_utxos.size + mandatory_utxos.size) > 0
          utxo = if mandatory_utxos.size > 0
            mandatory_utxos.shift
          else
            sorted_utxos.shift
          end 
          if utxo.value > 0 &&
            !utxo.script.op_return_script? &&
            !self.internal_provider.consumed_unspent_output?(utxo)
            self.internal_provider.consume_unspent_output(utxo)
            break
          else
            #puts "CONSUMED UTXO, SKIPPING: #{utxo.transaction_id}:#{utxo.index} (#{utxo.value})"
            # Continue reading utxos to find one that is not consumed yet.
            utxo = nil
          end
        end
        
        if !utxo
          # puts "ALL UTXOS:"
          # all_utxos.each do |utxo|
          #   puts "--> #{utxo.transaction_id}:#{utxo.index} (#{utxo.value})"
          # end
          raise InsufficientFundsError
        end
        
        result.inputs_amount += utxo.value

        raise ArgumentError, "Transaction hash must be non-nil in unspent output" if !utxo.transaction_hash
        raise ArgumentError, "Index must be non-nil in unspent output" if !utxo.index

        txin = TransactionInput.new(previous_hash: utxo.transaction_hash,
                                    previous_index: utxo.index,
                                    # put the output script here so the signer knows which key to use.
                                    signature_script: utxo.script)
        txin.transaction_output = utxo
        result.transaction.add_input(txin)

        # Do not check the result before we included all the mandatory utxos.
        if mandatory_utxos.size == 0
          # Before computing the fee, quick check if we have enough inputs to cover the outputs.
          # If not, go and add one more utxo before wasting time computing fees.
          if result.inputs_amount >= result.outputs_amount
            fee = compute_fee_for_transaction(result.transaction, self.fee_rate)

            change = result.inputs_amount - result.outputs_amount - fee

            if change >= self.minimum_change

              # We have a big enough change, set missing values and return.

              change_output.value = change
              result.change_amount = change
              result.outputs_amount += change
              result.fee = fee
              attempt_to_sign_transaction(result)
              return result

            elsif change > self.dust_change && change < self.minimum_change

              # We have a shitty change: not small enough to forgo, not big enough to be useful.
              # Try adding more utxos on the next cycle (or fail if no more utxos are available).

            elsif change >= 0 && change <= self.dust_change
              # This also includes the case when change is exactly zero satoshis.
              # Remove the change output, keep existing outputs_amount, set fee and try to sign.
              result.transaction.outputs = result.transaction.outputs[0, result.transaction.outputs.size - 1]
              result.change_amount = 0
              result.fee = fee
              attempt_to_sign_transaction(result)
              return result

            else
              # Change is negative, we need more funds for this transaction.
              # Try adding more utxos on the next cycle.
              result.fee = fee
            end

          end # if inputs_amount >= outputs_amount
        end # if no more mandatory outputs to add
      end # while true
    end
def build

      if !self.change_address
        raise MissingChangeAddressError
      end

      result = Result.new

      add_input_for_utxo = proc do |utxo|
        raise ArgumentError, "Unspent output must contain index" if !utxo.index
        raise ArgumentError, "Unspent output must contain transaction_hash" if !utxo.transaction_hash

        if !self.internal_provider.consumed_unspent_output?(utxo)
          self.internal_provider.consume_unspent_output(utxo)

          result.inputs_amount += utxo.value
          txin = TransactionInput.new(
                                      previous_hash: utxo.transaction_hash,
                                      previous_index: utxo.index,
                                      # put the output script here so the signer knows which key to use.
                                      signature_script: utxo.script)
          txin.transaction_output = utxo
          result.transaction.add_input(txin)
        else
          # UTXO was already consumed possibly by another Tx Builder sharing the same provider.
        end
      end

      # If no outputs are specified, spend all utxos to a change address, minus the mining fee.
      if (self.outputs || []).size == 0
        result.inputs_amount = 0

        (self.prepended_unspent_outputs || []).each(&add_input_for_utxo)
        (self.unspent_outputs || []).each(&add_input_for_utxo)

        if result.transaction.inputs.size == 0
          raise MissingUnspentOutputsError, "Missing unspent outputs"
        end

        # Value will be determined after computing the fee
        change_output = TransactionOutput.new(value: 0, script: self.change_address.public_address.script)
        result.transaction.add_output(change_output)

        result.fee = compute_fee_for_transaction(result.transaction, self.fee_rate)
        result.outputs_amount = result.inputs_amount - result.fee
        result.change_amount = 0

        # Check if inputs cover the fees
        if result.outputs_amount < 0
          raise InsufficientFundsError.new(result)
        end

        # Warn if the output amount is relatively small.
        if result.outputs_amount < result.fee
          Diagnostics.current.add_message("BTC::TransactionBuilder: Warning! Spending all unspent outputs returns less than a mining fee. (#{result.outputs_amount} < #{result.fee})")
        end

        # Set the output value as needed
        result.transaction.outputs[0].value = result.outputs_amount

        # For each address that is a WIF, locate the matching input and sign it.
        attempt_to_sign_transaction(result)

        return result
      end # if no specific outputs required

      # We are having one or more outputs (e.g. normal payment)
      # Need to find appropriate unspents and compose a transaction.

      # Prepare all outputs.
      # result.outputs_amount will also contain a fee after it's calculated.
      (self.outputs || []).each do |txout|
        result.outputs_amount += txout.value
        result.transaction.add_output(txout)
      end

      # We'll determine final change value depending on inputs.
      # Setting default to MAX_MONEY will protect against a bug when we fail to update the amount and
      # spend unexpected amount on mining fees.
      change_output = TransactionOutput.new(value: MAX_MONEY, script: self.change_address.public_address.script)
      result.transaction.add_output(change_output)

      mandatory_utxos = (self.prepended_unspent_outputs || []).to_a.dup

      # We have specific outputs with specific amounts, so we need to select the best amount of coins.
      # To play nice with BIP32/BIP44 change addresses (that need to monitor an increasing amount of addresses),
      # we'll spend oldest outputs first and will add more and more newer outputs until we cover fees
      # and change output is either zero or above the dust limit.
      # If `confirmations` attribute is nil, order is preserved.
      utxos = self.unspent_outputs || []
      if self.keep_unspent_outputs_order
        sorted_utxos = utxos.to_a.dup
      else
        sorted_utxos = utxos.to_a.sort_by{|txout| -(txout.confirmations || -1) } # oldest first
      end
      
      if self.parent_transactions
        # Can repeat some outputs in mandatory_utxos or sorted_utxos, 
        # but double-spending will be prevented by provider.
        self.parent_transactions.each do |parenttx|
          sorted_utxos += parenttx.outputs
        end
      end
      
      all_utxos = (sorted_utxos + mandatory_utxos)

      while true
        if (sorted_utxos.size + mandatory_utxos.size) == 0
          raise InsufficientFundsError.new(result)
        end

        utxo = nil
        while (sorted_utxos.size + mandatory_utxos.size) > 0
          utxo = if mandatory_utxos.size > 0
            mandatory_utxos.shift
          else
            sorted_utxos.shift
          end 
          if utxo.value > 0 &&
            !utxo.script.op_return_script? &&
            !self.internal_provider.consumed_unspent_output?(utxo)
            self.internal_provider.consume_unspent_output(utxo)
            break
          else
            #puts "CONSUMED UTXO, SKIPPING: #{utxo.transaction_id}:#{utxo.index} (#{utxo.value})"
            # Continue reading utxos to find one that is not consumed yet.
            utxo = nil
          end
        end
        
        if !utxo
          # puts "ALL UTXOS:"
          # all_utxos.each do |utxo|
          #   puts "--> #{utxo.transaction_id}:#{utxo.index} (#{utxo.value})"
          # end
          raise InsufficientFundsError
        end
        
        result.inputs_amount += utxo.value

        raise ArgumentError, "Transaction hash must be non-nil in unspent output" if !utxo.transaction_hash
        raise ArgumentError, "Index must be non-nil in unspent output" if !utxo.index

        txin = TransactionInput.new(previous_hash: utxo.transaction_hash,
                                    previous_index: utxo.index,
                                    # put the output script here so the signer knows which key to use.
                                    signature_script: utxo.script)
        txin.transaction_output = utxo
        result.transaction.add_input(txin)

        # Do not check the result before we included all the mandatory utxos.
        if mandatory_utxos.size == 0
          # Before computing the fee, quick check if we have enough inputs to cover the outputs.
          # If not, go and add one more utxo before wasting time computing fees.
          if result.inputs_amount >= result.outputs_amount
            fee = compute_fee_for_transaction(result.transaction, self.fee_rate)

            change = result.inputs_amount - result.outputs_amount - fee

            if change >= self.minimum_change

              # We have a big enough change, set missing values and return.

              change_output.value = change
              result.change_amount = change
              result.outputs_amount += change
              result.fee = fee
              attempt_to_sign_transaction(result)
              return result

            elsif change > self.dust_change && change < self.minimum_change

              # We have a shitty change: not small enough to forgo, not big enough to be useful.
              # Try adding more utxos on the next cycle (or fail if no more utxos are available).

            elsif change >= 0 && change <= self.dust_change
              # This also includes the case when change is exactly zero satoshis.
              # Remove the change output, keep existing outputs_amount, set fee and try to sign.
              result.transaction.outputs = result.transaction.outputs[0, result.transaction.outputs.size - 1]
              result.change_amount = 0
              result.fee = fee
              attempt_to_sign_transaction(result)
              return result

            else
              # Change is negative, we need more funds for this transaction.
              # Try adding more utxos on the next cycle.
              result.fee = fee
            end

          end # if inputs_amount >= outputs_amount
        end # if no more mandatory outputs to add
      end # while true
    end
Attempts to build a transaction