1
// Copyright © 2017-2020 Trust Wallet.
2
//
3
// This file is part of Trust. The full Trust copyright notice, including
4
// terms governing use, modification, and redistribution, is contained in the
5
// file LICENSE at the root of the source code distribution tree.
6

7
#include "TransactionBuilder.h"
8
#include "TransactionSigner.h"
9

10
#include "../Coin.h"
11

12
#include <algorithm>
13
#include <cassert>
14

15
namespace TW::Bitcoin {
16

17
/// Estimate encoded size by simple formula
18 3
int64_t estimateSimpleFee(const FeeCalculator& feeCalculator, const TransactionPlan& plan, int outputSize, int64_t byteFee) {
19 3
    return feeCalculator.calculate(plan.utxos.size(), outputSize, byteFee);
20
}
21

22
/// Estimate encoded size by invoking sign(sizeOnly), get actual size
23 3
int64_t estimateSegwitFee(const FeeCalculator& feeCalculator, const TransactionPlan& plan, int outputSize, const Bitcoin::Proto::SigningInput& input) {
24 3
    TWPurpose coinPurpose = TW::purpose(static_cast<TWCoinType>(input.coin_type()));
25 3
    if (coinPurpose != TWPurposeBIP84) {
26
        // not segwit, return default simple estimate
27 3
        return estimateSimpleFee(feeCalculator, plan, outputSize, input.byte_fee());
28
    }
29

30
    // duplicate input, with the current plan
31 3
    auto inputWithPlan = std::move(input);
32 3
    *inputWithPlan.mutable_plan() = plan.proto();
33

34 3
    auto signer = TransactionSigner<Transaction, TransactionBuilder>(std::move(inputWithPlan), true);
35 3
    auto result = signer.sign();
36 3
    if (!result) {
37
        // signing failed; return default simple estimate
38 3
        return estimateSimpleFee(feeCalculator, plan, outputSize, input.byte_fee());
39
    }
40

41
    // Obtain the encoded size
42 3
    auto transaction = result.payload();
43 3
    Data dataNonSegwit;
44 3
    transaction.encode(dataNonSegwit, Transaction::SegwitFormatMode::NonSegwit);
45 3
    int64_t sizeNonSegwit = dataNonSegwit.size();
46 3
    uint64_t vSize = 0;
47
    // Check if there is segwit
48 3
    if (!transaction.hasWitness()) {
49
        // no segwit, virtual size is defined as non-segwit size
50 3
        vSize = sizeNonSegwit;
51 3
    } else {
52 3
        Data dataWitness;
53 3
        transaction.encodeWitness(dataWitness);
54 3
        int64_t witnessSize = 2 + dataWitness.size();
55
        // compute virtual size:  (smaller) non-segwit + 1/4 of the diff (witness-only)
56
        // (in other way: 3/4 of (smaller) non-segwit + 1/4 of segwit size)
57 3
        vSize = sizeNonSegwit + witnessSize/4 + (witnessSize % 4 != 0);
58
    }
59 3
    uint64_t fee = input.byte_fee() * vSize;
60

61 3
    return fee;
62
}
63

64 3
TransactionPlan TransactionBuilder::plan(const Bitcoin::Proto::SigningInput& input) {
65 3
    auto plan = TransactionPlan();
66 3
    plan.amount = input.amount();
67

68 3
    const auto& feeCalculator = getFeeCalculator(static_cast<TWCoinType>(input.coin_type()));
69 3
    auto unspentSelector = UnspentSelector(feeCalculator);
70 3
    bool maxAmount = input.use_max_amount();
71

72
    // select UTXOs
73 3
    auto output_size = 2;
74 3
    if (!maxAmount) {
75 3
        output_size = 2; // output + change
76 3
        plan.utxos = unspentSelector.select(input.utxo(), plan.amount, input.byte_fee(), output_size);
77 3
    } else {
78 3
        output_size = 1; // no change
79 3
        plan.utxos = unspentSelector.selectMaxAmount(input.utxo(), input.byte_fee());
80
    }
81
    // Note: if utxos.size() == 0, all fields will be computed to 0
82 3
    plan.availableAmount = UnspentSelector::sum(plan.utxos);
83

84
    // Compute fee.
85
    // must preliminary set change so that there is a second output
86 3
    if (!maxAmount) {
87 3
        plan.amount = input.amount();
88 3
        plan.fee = 0;
89 3
        plan.change = plan.availableAmount - plan.amount;
90 3
    } else {
91 3
        plan.amount = plan.availableAmount;
92 3
        plan.fee = 0;
93 3
        plan.change = 0;
94
    }
95 3
    plan.fee = estimateSegwitFee(feeCalculator, plan, output_size, input);
96
    // If fee is larger then availableAmount (can happen in special maxAmount case), we reduce it (and hope it will go through)
97 3
    plan.fee = std::min(plan.availableAmount, plan.fee);
98 3
    assert(plan.fee >= 0 && plan.fee <= plan.availableAmount);
99
    
100
    // adjust/compute amount
101 3
    if (!maxAmount) {
102
        // reduce amount if needed
103 3
        plan.amount = std::max(Amount(0), std::min(plan.amount, plan.availableAmount - plan.fee));
104 3
    } else {
105
        // max available amount
106 3
        plan.amount = std::max(Amount(0), plan.availableAmount - plan.fee);
107
    }
108 3
    assert(plan.amount >= 0 && plan.amount <= plan.availableAmount);
109

110
    // compute change
111 3
    plan.change = plan.availableAmount - plan.amount - plan.fee;
112 3
    assert(plan.change >= 0 && plan.change <= plan.availableAmount);
113 3
    assert(!maxAmount || plan.change == 0); // change is 0 in max amount case
114

115 3
    assert(plan.amount + plan.change + plan.fee == plan.availableAmount);
116

117 3
    return plan;
118
}
119

120
} // namespace TW::Bitcoin

Read our documentation on viewing source code .

Loading