Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qa/rpc-tests/mempool_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def run_test(self):
txid = self.nodes[0].sendrawtransaction(txFS['hex'])

relayfee = self.nodes[0].getnetworkinfo()['relayfee']
base_fee = relayfee*100
base_fee = relayfee*100 * TIMES_GREATER_PRECISION
for i in xrange (4):
txids.append([])
txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee)
Expand Down
4 changes: 2 additions & 2 deletions qa/rpc-tests/smartfees.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True):
all_estimates = [ node.estimatefee(i) for i in range(1,26) ]
if print_estimates:
print([str(all_estimates[e-1]) for e in [1,2,3,6,15,25]])
delta = 1.0e-6 # account for rounding error
delta = (1.0e-6) / TIMES_GREATER_PRECISION # account for rounding error
last_e = max(fees_seen)
for e in filter(lambda x: x >= 0, all_estimates):
# Estimates should be within the bounds of what transactions fees actually were:
Expand Down Expand Up @@ -219,7 +219,7 @@ def transact_and_mine(self, numblocks, mining_node):
from_index = random.randint(1,2)
(txhex, fee) = small_txpuzzle_randfee(self.nodes[from_index], self.confutxo,
self.memutxo, Decimal("0.005"), min_fee, min_fee)
tx_kbytes = (len(txhex)/2)/1000.0
tx_kbytes = (len(txhex)/2)/(1000.0 * TIMES_GREATER_PRECISION)
self.fees_per_kb.append(float(fee)/tx_kbytes)
sync_mempools(self.nodes[0:3],.1)
mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"]
Expand Down
6 changes: 6 additions & 0 deletions qa/rpc-tests/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException

KB = 1000
# TODO inline the next two lines in PRECISION_MULTIPLIER
TIMES_GREATER_PRECISION = 2
DEPRECATED_IMPLICIT_CALLER_PRECISSION = KB
PRECISION_MULTIPLIER = DEPRECATED_IMPLICIT_CALLER_PRECISSION * TIMES_GREATER_PRECISION

COVERAGE_DIR = None

#Set Mocktime default to OFF.
Expand Down
9 changes: 5 additions & 4 deletions qa/rpc-tests/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ class WalletTest (BitcoinTestFramework):

def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
"""Return curr_balance after asserting the fee was in range"""
precision_fee = fee_per_byte * TIMES_GREATER_PRECISION
fee = balance_with_fee - curr_balance
target_fee = fee_per_byte * tx_size
target_fee = precision_fee * tx_size
if fee < target_fee:
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)"%(str(fee), str(target_fee)))
# allow the node's estimation to be at most 2 bytes off
if fee > fee_per_byte * (tx_size + 2):
if fee > precision_fee * (tx_size + 2):
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee)))
return curr_balance

Expand Down Expand Up @@ -107,8 +108,8 @@ def run_test (self):

# Send 10 BTC normal
address = self.nodes[0].getnewaddress("test")
fee_per_byte = Decimal('0.001') / 1000
self.nodes[2].settxfee(fee_per_byte * 1000)
fee_per_byte = Decimal('0.001') / KB
self.nodes[2].settxfee(fee_per_byte * KB)
txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
self.nodes[2].generate(1)
self.sync_all()
Expand Down
39 changes: 32 additions & 7 deletions src/amount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,47 @@ const std::string CURRENCY_UNIT = "BTC";
CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize)
{
if (nSize > 0)
nSatoshisPerK = nFeePaid*1000/nSize;
nSatoshisPerK = nFeePaid * PRECISION_MULTIPLIER / nSize;
else
nSatoshisPerK = 0;
}

CAmount CFeeRate::GetFee(size_t nSize) const
CAmount CFeeRate::GetFee(size_t nSize, size_t nPerByteDivisor) const
{
CAmount nFee = nSatoshisPerK * nSize / 1000;

if (nFee == 0 && nSize != 0 && nSatoshisPerK > 0)
nFee = CAmount(1);
size_t extra_divisor = 1;
size_t extra_multiplier = 1;

// Example for thought: PRECISION_MULTIPLIER == KB, but nPerByteDivisor=MB
// We divide less as requested by the caller
if (nPerByteDivisor > PRECISION_MULTIPLIER) {
assert(false); // TODO Unittest required for this execution branch to be executed
assert(nPerByteDivisor % PRECISION_MULTIPLIER == 0); // nPerByteDivisor must be multiple of PRECISION_MULTIPLIER
extra_multiplier = nPerByteDivisor / PRECISION_MULTIPLIER;
}
// Example for thought: PRECISION_MULTIPLIER == MB, but nPerByteDivisor=KB
// In this case we truncate more of what wasn't truncated previously
else if (nPerByteDivisor < PRECISION_MULTIPLIER) {
assert(PRECISION_MULTIPLIER % nPerByteDivisor == 0); // PRECISION_MULTIPLIER must be multiple of nPerByteDivisor
extra_divisor = PRECISION_MULTIPLIER / nPerByteDivisor;
}

CAmount nFee = nSatoshisPerK * nSize / (PRECISION_MULTIPLIER * extra_multiplier / extra_divisor);

// Removing this special case and handling zeroes uniformingly
// from the caller would allow some simplifications in this function
if (nFee == 0 && nSize != 0) {
const size_t nSatoshisPerByteDivisor = nSatoshisPerK * extra_multiplier / extra_divisor;
if (nSatoshisPerByteDivisor > 0)
nFee = CAmount(1);
}
// if (nFee == 0 && nSize != 0 && nSatoshisPerK > 0)
// nFee = CAmount(1);

return nFee;
}

std::string CFeeRate::ToString() const
{
return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
CAmount nFee = GetFee(KB, KB);
return strprintf("%d.%08d %s/kB", nFee / COIN, nFee % COIN, CURRENCY_UNIT);
}
14 changes: 11 additions & 3 deletions src/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ typedef int64_t CAmount;

static const CAmount COIN = 100000000;
static const CAmount CENT = 1000000;
static const size_t KB = 1000;
static const size_t PRECISION_MULTIPLIER = KB*2;

extern const std::string CURRENCY_UNIT;

Expand All @@ -36,20 +38,26 @@ inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <=
class CFeeRate
{
private:
CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
CAmount nSatoshisPerK; //! unit is satoshis-per-PRECISION_MULTIPLIER-bytes
public:
CFeeRate() : nSatoshisPerK(0) { }
explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
CFeeRate(const CAmount& nFeePaid, size_t nSize);
CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; }
/**
* Return the fee in satoshis for the given size in bytes.
* @param nPerByteDivisor Can be different from PRECISION_MULTIPLIER to get
* bigger numbers (for example) Default: KB.
*/
CAmount GetFee(size_t size) const;
CAmount GetFee(size_t size, size_t nPerByteDivisor=KB) const;
/**
* Return the fee in satoshis for a size of 1000 bytes
*/
CAmount GetFeePerK() const { return GetFee(1000); }
CAmount GetFeePerK() const { return GetFee(KB, KB); }
/**
* Return the fee in satoshis for a size of PRECISION_MULTIPLIER bytes
*/
CAmount GetInternalRate() const { return GetFee(KB, PRECISION_MULTIPLIER); }
friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
Expand Down
12 changes: 10 additions & 2 deletions src/test/mempool_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,19 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8);
// ... with a 1/4 halflife when mempool is < 1/4 its target size

SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
unsigned i = 6;
while (pool.GetMinFee(1).GetFeePerK() > (int)(2 * KB)) {
SetMockTime(42 + (++i)*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
}
// test increased stored precission in CFeeRate
SetMockTime(42 + i * CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1554); // Change this value when changing PRECISION_MULTIPLIER

SetMockTime(42 + (i+1)*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
// ... but feerate should never drop below 1000

SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
SetMockTime(42 + (i+2)*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
// ... unless it has gone all the way to 0 (after getting past 1000/2)

Expand Down