bitcoin/BRWallet.c
// returns an unsigned transaction that satisifes the given transaction outputs
// result must be freed using BRTransactionFree()
// use feePerKb UINT64_MAX to indicate that the wallet feePerKb should be used
returns an unsigned transaction that satisifes the given transaction outputs
result must be freed using BRTransactionFree()
use feePerKb UINT64_MAX to indicate that the wallet feePerKb should be used
returns an unsigned transaction that satisifes the given transaction outputs result must be freed using BRTransactionFree () use feePerKb UINT64_MAX to indicate that the wallet feePerKb should be used
BRTransaction *BRWalletCreateTxForOutputsWithFeePerKb(BRWallet *wallet, uint64_t feePerKb, const BRTxOutput outputs[], size_t outCount)
{
    BRTransaction *tx, *transaction = BRTransactionNew();
    uint64_t feeAmount, amount = 0, balance = 0, minAmount;
    size_t i, j, cpfpSize = 0;
    BRUTXO *o;
    BRAddress addr = BR_ADDRESS_NONE;
    assert(wallet != NULL);
    assert(outputs != NULL && outCount > 0);
    for (i = 0; outputs && i < outCount; i++) {
        assert(outputs[i].script != NULL && outputs[i].scriptLen > 0);
        BRTransactionAddOutput(transaction, outputs[i].amount, outputs[i].script, outputs[i].scriptLen);
        amount += outputs[i].amount;
    }
    minAmount = BRWalletMinOutputAmountWithFeePerKb(wallet, feePerKb);
    pthread_mutex_lock(&wallet->lock);
    feePerKb = UINT64_MAX == feePerKb ? wallet->feePerKb : feePerKb;
    feeAmount = _txFee(feePerKb, BRTransactionVSize(transaction) + TX_OUTPUT_SIZE);
    for (i = 0; i < array_count(wallet->utxos); i++) {
        o = &wallet->utxos[i];
        tx = BRSetGet(wallet->allTx, o);
        if (! tx || o->n >= tx->outCount) continue;
        BRTransactionAddInput(transaction, tx->txHash, o->n, tx->outputs[o->n].amount,
                              tx->outputs[o->n].script, tx->outputs[o->n].scriptLen, NULL, 0, NULL, 0, TXIN_SEQUENCE);
        if (BRTransactionVSize(transaction) + TX_OUTPUT_SIZE > TX_MAX_SIZE) { 
            BRTransactionFree(transaction);
            transaction = NULL;
            if (wallet->balance < amount + _txFee(feePerKb, 10 + array_count(wallet->utxos)*TX_INPUT_SIZE +
                                                  (outCount + 1)*TX_OUTPUT_SIZE + cpfpSize)) break;
            pthread_mutex_unlock(&wallet->lock);
            if (outputs[outCount - 1].amount > amount + feeAmount + minAmount - balance) {
                BRTxOutput newOutputs[outCount];
                for (j = 0; j < outCount; j++) {
                    newOutputs[j] = outputs[j];
                }
                newOutputs[outCount - 1].amount -= amount + feeAmount - balance; 
                transaction = BRWalletCreateTxForOutputsWithFeePerKb(wallet, feePerKb, newOutputs, outCount);
            }
            else transaction = BRWalletCreateTxForOutputsWithFeePerKb(wallet, feePerKb, outputs, outCount - 1); 
            balance = amount = feeAmount = 0;
            pthread_mutex_lock(&wallet->lock);
            break;
        }
        balance += tx->outputs[o->n].amount;
        feeAmount = _txFee(feePerKb, BRTransactionVSize(transaction) + TX_OUTPUT_SIZE + cpfpSize);
        if (wallet->balance > amount + feeAmount) feeAmount += (wallet->balance - (amount + feeAmount)) % 100;
        if (balance == amount + feeAmount || balance >= amount + feeAmount + minAmount) break;
    }
    pthread_mutex_unlock(&wallet->lock);
    if (transaction && (outCount < 1 || balance < amount + feeAmount)) { 
        BRTransactionFree(transaction);
        transaction = NULL;
    }
    else if (transaction && balance - (amount + feeAmount) > minAmount) { 
        BRWalletUnusedAddrs(wallet, &addr, 1, 1);
        uint8_t script[BRAddressScriptPubKey(NULL, 0, wallet->addrParams, addr.s)];
        size_t scriptLen = BRAddressScriptPubKey(script, sizeof(script), wallet->addrParams, addr.s);
        BRTransactionAddOutput(transaction, balance - (amount + feeAmount), script, scriptLen);
        BRTransactionShuffleOutputs(transaction);
    }
    return transaction;
}
BRTransaction * BRWalletCreateTxForOutputsWithFeePerKb ( BRWallet * wallet , uint64_t feePerKb , const BRTxOutput outputs [ ] , size_t outCount ) { BRTransaction * tx , * transaction = BRTransactionNew ( ) ; uint64_t feeAmount , amount = 0 , balance = 0 , minAmount ; size_t i , j , cpfpSize = 0 ; BRUTXO * o ; BRAddress addr = BR_ADDRESS_NONE ; assert ( wallet != NULL ) ; assert ( outputs != NULL && outCount > 0 ) ; for ( i = 0 ; outputs && i < outCount ; i ++ ) { assert ( outputs [ i ] . script != NULL && outputs [ i ] . scriptLen > 0 ) ; BRTransactionAddOutput ( transaction , outputs [ i ] . amount , outputs [ i ] . script , outputs [ i ] . scriptLen ) ; amount += outputs [ i ] . amount ; } minAmount = BRWalletMinOutputAmountWithFeePerKb ( wallet , feePerKb ) ; pthread_mutex_lock ( & wallet -> lock ) ; feePerKb = UINT64_MAX == feePerKb ? wallet -> feePerKb : feePerKb ; feeAmount = _txFee ( feePerKb , BRTransactionVSize ( transaction ) + TX_OUTPUT_SIZE ) ; for ( i = 0 ; i < array_count ( wallet -> utxos ) ; i ++ ) { o = & wallet -> utxos [ i ] ; tx = BRSetGet ( wallet -> allTx , o ) ; if ( ! tx || o -> n >= tx -> outCount ) continue ; BRTransactionAddInput ( transaction , tx -> txHash , o -> n , tx -> outputs [ o -> n ] . amount , tx -> outputs [ o -> n ] . script , tx -> outputs [ o -> n ] . scriptLen , NULL , 0 , NULL , 0 , TXIN_SEQUENCE ) ; if ( BRTransactionVSize ( transaction ) + TX_OUTPUT_SIZE > TX_MAX_SIZE ) { BRTransactionFree ( transaction ) ; transaction = NULL ; if ( wallet -> balance < amount + _txFee ( feePerKb , 10 + array_count ( wallet -> utxos ) * TX_INPUT_SIZE + ( outCount + 1 ) * TX_OUTPUT_SIZE + cpfpSize ) ) break ; pthread_mutex_unlock ( & wallet -> lock ) ; if ( outputs [ outCount - 1 ] . amount > amount + feeAmount + minAmount - balance ) { BRTxOutput newOutputs [ outCount ] ; for ( j = 0 ; j < outCount ; j ++ ) { newOutputs [ j ] = outputs [ j ] ; } newOutputs [ outCount - 1 ] . amount -= amount + feeAmount - balance ; transaction = BRWalletCreateTxForOutputsWithFeePerKb ( wallet , feePerKb , newOutputs , outCount ) ; } else transaction = BRWalletCreateTxForOutputsWithFeePerKb ( wallet , feePerKb , outputs , outCount - 1 ) ; balance = amount = feeAmount = 0 ; pthread_mutex_lock ( & wallet -> lock ) ; break ; } balance += tx -> outputs [ o -> n ] . amount ; feeAmount = _txFee ( feePerKb , BRTransactionVSize ( transaction ) + TX_OUTPUT_SIZE + cpfpSize ) ; if ( wallet -> balance > amount + feeAmount ) feeAmount += ( wallet -> balance - ( amount + feeAmount ) ) % 100 ; if ( balance == amount + feeAmount || balance >= amount + feeAmount + minAmount ) break ; } pthread_mutex_unlock ( & wallet -> lock ) ; if ( transaction && ( outCount < 1 || balance < amount + feeAmount ) ) { BRTransactionFree ( transaction ) ; transaction = NULL ; } else if ( transaction && balance - ( amount + feeAmount ) > minAmount ) { BRWalletUnusedAddrs ( wallet , & addr , 1 , 1 ) ; uint8_t script [ BRAddressScriptPubKey ( NULL , 0 , wallet -> addrParams , addr . s ) ] ; size_t scriptLen = BRAddressScriptPubKey ( script , sizeof ( script ) , wallet -> addrParams , addr . s ) ; BRTransactionAddOutput ( transaction , balance - ( amount + feeAmount ) , script , scriptLen ) ; BRTransactionShuffleOutputs ( transaction ) ; } return transaction ; }
returns an unsigned transaction that satisifes the given transaction outputs
result must be freed using BRTransactionFree()
use feePerKb UINT64_MAX to indicate that the wallet feePerKb should be used
returns an unsigned transaction that satisifes the given transaction outputs result must be freed using BRTransactionFree () use feePerKb UINT64_MAX to indicate that the wallet feePerKb should be used
// TODO: use up all UTXOs for all used addresses to avoid leaving funds in addresses whose public key is revealed // TODO: avoid combining addresses in a single transaction when possible to reduce information leakage // TODO: use up UTXOs received from any of the output scripts that this transaction sends funds to, to mitigate an //       attacker double spending and requesting a refund // transaction size-in-bytes too large // check for sufficient total funds before building a smaller transaction // reduce last output amount // remove last output //        // size of unconfirmed, non-change inputs for child-pays-for-parent fee //        // don't include parent tx with more than 10 inputs or 10 outputs //        if (tx->blockHeight == TX_UNCONFIRMED && tx->inCount <= 10 && tx->outCount <= 10 && //            ! _BRWalletTxIsSend(wallet, tx)) cpfpSize += BRTransactionVSize(tx); // fee amount after adding a change output // increase fee to round off remaining wallet balance to nearest 100 satoshi // no outputs/insufficient funds // add change output