Skip to content

Commit

Permalink
Add Decred fee calculation (#613)
Browse files Browse the repository at this point in the history
* Add decred fee estimation

* Fix old issue template

* Fix codacy warning
  • Loading branch information
hewigovens authored and vikmeup committed Sep 1, 2019
1 parent ad355b3 commit 4689c20
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 47 deletions.
10 changes: 8 additions & 2 deletions .github/ISSUE_TEMPLATE/new_blockchain.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<!--- Thank you for requesting new blockchain support -->
---
name: New blockchain
about: Request a new blockchain support
title: ''
labels: chain-integration
assignees: ''

<!--- Before submitting please check to see if this coin was already requested -->
---

<!--- Before submitting please check to see if this coin was already requested -->
<!--- Provide as many relevant details about the coin -->
## Description

Expand Down
3 changes: 2 additions & 1 deletion src/Bitcoin/TransactionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

namespace TW::Bitcoin {

struct TransactionBuilder {
class TransactionBuilder {
public:
/// Plans a transaction by selecting UTXOs and calculating fees.
static TransactionPlan plan(const Bitcoin::Proto::SigningInput& input) {
auto plan = TransactionPlan();
Expand Down
10 changes: 10 additions & 0 deletions src/Bitcoin/UnspentCalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ UnspentCalculator UnspentCalculator::getCalculator(TWCoinType coinType) {
auto calcInput = [](int64_t byteFee) -> int64_t { return 0; };
return UnspentCalculator(calc, calcInput);
}
case TWCoinTypeDecred: {
auto calc = [](size_t inputs, size_t outputs, int64_t byteFee) -> int64_t {
const auto txsize = ((166 * inputs) + (38 * outputs) + 12);
return int64_t(txsize) * byteFee;
};
auto calcInput = [](int64_t byteFee) -> int64_t {
return int64_t(166) * byteFee;
};
return UnspentCalculator(calc, calcInput);
}
default:
return UnspentCalculator();
}
Expand Down
5 changes: 4 additions & 1 deletion src/Decred/Signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ class Signer {
Signer() = default;

/// Initializes a transaction signer with signing input.
explicit Signer(Bitcoin::Proto::SigningInput&& input) : input(input) {}
explicit Signer(Bitcoin::Proto::SigningInput&& input)
: input(input), plan(TransactionBuilder::plan(input)) {
transaction = TransactionBuilder::build(plan, input.to_address(), input.change_address());
}

/// Initializes a transaction signer with signing input, a transaction, and
/// a hash type.
Expand Down
41 changes: 2 additions & 39 deletions src/Decred/TransactionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "Transaction.h"
#include "../Bitcoin/TransactionPlan.h"
#include "../Bitcoin/TransactionBuilder.h"
#include "../Bitcoin/UnspentSelector.h"
#include "../proto/Bitcoin.pb.h"
#include "../proto/Decred.pb.h"
Expand All @@ -21,45 +22,7 @@ namespace TW::Decred {
struct TransactionBuilder {
/// Plans a transaction by selecting UTXOs and calculating fees.
static Bitcoin::TransactionPlan plan(const Bitcoin::Proto::SigningInput& input) {
auto plan = Bitcoin::TransactionPlan();
plan.amount = input.amount();

auto output_size = 2;
auto calculator =
Bitcoin::UnspentCalculator::getCalculator(static_cast<TWCoinType>(input.coin_type()));
auto unspentSelector = Bitcoin::UnspentSelector(calculator);
if (input.use_max_amount() && Bitcoin::UnspentSelector::sum(input.utxo()) == plan.amount) {
output_size = 1;
int64_t newAmount = 0;
auto input_size = 0;

for (auto utxo : input.utxo()) {
if (utxo.amount() >
unspentSelector.calculator.calculateSingleInput(input.byte_fee())) {
input_size++;
newAmount += utxo.amount();
}
}

plan.amount = newAmount - unspentSelector.calculator.calculate(input_size, output_size,
input.byte_fee());
plan.amount = std::max(Bitcoin::Amount(0), plan.amount);
}

plan.utxos =
unspentSelector.select(input.utxo(), plan.amount, input.byte_fee(), output_size);
plan.fee =
unspentSelector.calculator.calculate(plan.utxos.size(), output_size, input.byte_fee());

plan.availableAmount = Bitcoin::UnspentSelector::sum(plan.utxos);

if (plan.amount > plan.availableAmount - plan.fee) {
plan.amount = std::max(Bitcoin::Amount(0), plan.availableAmount - plan.fee);
}

plan.change = plan.availableAmount - plan.amount - plan.fee;

return plan;
return Bitcoin::TransactionBuilder::plan(input);
}

/// Builds a transaction by selecting UTXOs and calculating fees.
Expand Down
23 changes: 19 additions & 4 deletions tests/Bitcoin/TransactionPlanTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Bitcoin/TransactionPlan.h"
#include "Bitcoin/TransactionBuilder.h"
#include "proto/Bitcoin.pb.h"
#include <TrustWalletCore/TWCoinType.h>

#include <gtest/gtest.h>

Expand All @@ -34,11 +35,12 @@ inline auto buildUTXO(const OutPoint& outPoint, Amount amount) {
return utxo;
}

inline auto buildSigningInput(Amount amount, int byteFee, const std::vector<Proto::UnspentTransaction> utxos, bool useMaxAmount) {
inline auto buildSigningInput(Amount amount, int byteFee, const std::vector<Proto::UnspentTransaction>& utxos, bool useMaxAmount, enum TWCoinType coin) {
Proto::SigningInput input;
input.set_amount(amount);
input.set_byte_fee(byteFee);
input.set_use_max_amount(useMaxAmount);
input.set_coin_type(coin);
*input.mutable_utxo() = { utxos.begin(), utxos.end() };
return input;
}
Expand All @@ -52,7 +54,7 @@ TEST(TransactionPlan, NonMaxAmount) {
utxos.push_back(buildUTXO(txOutPoint, 50000));
utxos.push_back(buildUTXO(txOutPoint, 120000));

auto sigingInput = buildSigningInput(10000, 1, utxos, false);
auto sigingInput = buildSigningInput(10000, 1, utxos, false, TWCoinTypeBitcoin);
auto txPlan = TransactionBuilder::plan(sigingInput);

ASSERT_EQ(txPlan.amount, 10000);
Expand All @@ -70,7 +72,7 @@ TEST(TransactionPlan, MaxAmount) {

ASSERT_EQ(sum(utxos), 39200);

auto sigingInput = buildSigningInput(39200, 32, utxos, true);
auto sigingInput = buildSigningInput(39200, 32, utxos, true, TWCoinTypeBitcoin);
auto txPlan = TransactionBuilder::plan(sigingInput);

ASSERT_EQ(txPlan.availableAmount, 30000);
Expand All @@ -87,11 +89,24 @@ TEST(TransactionPlan, MaxAmountDoge) {

ASSERT_EQ(sum(utxos), Amount(2300000000));

auto sigingInput = buildSigningInput(Amount(2300000000), 100, utxos, true);
auto sigingInput = buildSigningInput(Amount(2300000000), 100, utxos, true, TWCoinTypeDogecoin);
auto txPlan = TransactionBuilder::plan(sigingInput);

ASSERT_EQ(txPlan.availableAmount, Amount(2300000000));
ASSERT_EQ(txPlan.amount, Amount(2299951200));
ASSERT_EQ(txPlan.change, 0);
ASSERT_EQ(txPlan.fee, 48800);
}

TEST(TransactionPlan, AmountDecred) {
auto utxos = std::vector<Proto::UnspentTransaction>();
utxos.push_back(buildUTXO(txOutPoint, Amount(39900000)));

auto sigingInput = buildSigningInput(Amount(10000000), 10, utxos, false, TWCoinTypeDecred);
auto txPlan = TransactionBuilder::plan(sigingInput);

ASSERT_EQ(txPlan.availableAmount, Amount(39900000));
ASSERT_EQ(txPlan.amount, Amount(10000000));
ASSERT_EQ(txPlan.change, 29897460);
ASSERT_EQ(txPlan.fee, 2540);
}

0 comments on commit 4689c20

Please sign in to comment.