Merge pull request #2415 from rnicoll/1.14.4-rbf-fee

Reduce BIP125 replace by fee increment value
This commit is contained in:
Patrick Lodder 2021-08-04 22:10:44 +02:00 committed by GitHub
commit a88d53be2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 43 deletions

View file

@ -155,7 +155,7 @@ testScripts = [
'import-rescan.py', 'import-rescan.py',
# While fee bumping should work in Doge, these tests depend on free transactions, which we don't support. # While fee bumping should work in Doge, these tests depend on free transactions, which we don't support.
# Disable until we can do a full rewrite of the tests (possibly upstream), or revise fee schedule, or something # Disable until we can do a full rewrite of the tests (possibly upstream), or revise fee schedule, or something
# 'bumpfee.py', 'bumpfee.py',
'rpcnamedargs.py', 'rpcnamedargs.py',
'listsinceblock.py', 'listsinceblock.py',
'p2p-leaktests.py', 'p2p-leaktests.py',

View file

@ -45,16 +45,16 @@ class BumpFeeTest(BitcoinTestFramework):
peer_node, rbf_node = self.nodes peer_node, rbf_node = self.nodes
rbf_node_address = rbf_node.getnewaddress() rbf_node_address = rbf_node.getnewaddress()
# fund rbf node with 10 coins of 0.001 btc (100,000 satoshis) # fund rbf node with 25 outputs of 10 DOGE
print("Mining blocks...") print("Mining blocks...")
peer_node.generate(70) peer_node.generate(70)
self.sync_all() self.sync_all()
for i in range(25): for i in range(25):
peer_node.sendtoaddress(rbf_node_address, 1.0000) peer_node.sendtoaddress(rbf_node_address, 10.0000)
self.sync_all() self.sync_all()
peer_node.generate(1) peer_node.generate(1)
self.sync_all() self.sync_all()
assert_equal(rbf_node.getbalance(), Decimal("25")) assert_equal(rbf_node.getbalance(), Decimal("250"))
print("Running tests") print("Running tests")
dest_address = peer_node.getnewaddress() dest_address = peer_node.getnewaddress()
@ -75,7 +75,7 @@ class BumpFeeTest(BitcoinTestFramework):
def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address): def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {dest_address: 8.00000000})
rbftx = rbf_node.gettransaction(rbfid) rbftx = rbf_node.gettransaction(rbfid)
sync_mempools((rbf_node, peer_node)) sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
@ -99,7 +99,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
# Create a transaction with segwit output, then create an RBF transaction # Create a transaction with segwit output, then create an RBF transaction
# which spends it, and make sure bumpfee can be called on it. # which spends it, and make sure bumpfee can be called on it.
segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001")) segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("10.0000000"))
segwit_out = rbf_node.validateaddress(rbf_node.getnewaddress()) segwit_out = rbf_node.validateaddress(rbf_node.getnewaddress())
rbf_node.addwitnessaddress(segwit_out["address"]) rbf_node.addwitnessaddress(segwit_out["address"])
segwitid = send_to_witness( segwitid = send_to_witness(
@ -108,15 +108,15 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
utxo=segwit_in, utxo=segwit_in,
pubkey=segwit_out["pubkey"], pubkey=segwit_out["pubkey"],
encode_p2sh=False, encode_p2sh=False,
amount=Decimal("0.0009"), amount=Decimal("9.00000000"),
sign=True) sign=True)
rbfraw = rbf_node.createrawtransaction([{ rbfraw = rbf_node.createrawtransaction([{
'txid': segwitid, 'txid': segwitid,
'vout': 0, 'vout': 0,
"sequence": BIP125_SEQUENCE_NUMBER "sequence": BIP125_SEQUENCE_NUMBER
}], {dest_address: Decimal("0.0005"), }], {dest_address: Decimal("5.00000000"),
get_change_address(rbf_node): Decimal("0.0003")}) get_change_address(rbf_node): Decimal("3.00000000")})
rbfsigned = rbf_node.signrawtransaction(rbfraw) rbfsigned = rbf_node.signrawtransaction(rbfraw)
rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"]) rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"])
assert rbfid in rbf_node.getrawmempool() assert rbfid in rbf_node.getrawmempool()
@ -128,7 +128,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
def test_nonrbf_bumpfee_fails(peer_node, dest_address): def test_nonrbf_bumpfee_fails(peer_node, dest_address):
# cannot replace a non RBF transaction (from node which did not enable RBF) # cannot replace a non RBF transaction (from node which did not enable RBF)
not_rbfid = create_fund_sign_send(peer_node, {dest_address: 0.90000000}) not_rbfid = create_fund_sign_send(peer_node, {dest_address: 9.00000000})
assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
@ -144,7 +144,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
"address": utxo["address"], "address": utxo["address"],
"sequence": BIP125_SEQUENCE_NUMBER "sequence": BIP125_SEQUENCE_NUMBER
} for utxo in utxos] } for utxo in utxos]
output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("0.001") output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("10.00000000")
rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val}) rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val})
signedtx = rbf_node.signrawtransaction(rawtx) signedtx = rbf_node.signrawtransaction(rawtx)
signedtx = peer_node.signrawtransaction(signedtx["hex"]) signedtx = peer_node.signrawtransaction(signedtx["hex"])
@ -156,8 +156,8 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address): def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
# cannot bump fee if the transaction has a descendant # cannot bump fee if the transaction has a descendant
# parent is send-to-self, so we don't have to check which output is change when creating the child tx # parent is send-to-self, so we don't have to check which output is change when creating the child tx
parent_id = create_fund_sign_send(rbf_node, {rbf_node_address: 0.00050000}) parent_id = create_fund_sign_send(rbf_node, {rbf_node_address: 5.00000000})
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 2.00000000})
tx = rbf_node.signrawtransaction(tx) tx = rbf_node.signrawtransaction(tx)
txid = rbf_node.sendrawtransaction(tx["hex"]) txid = rbf_node.sendrawtransaction(tx["hex"])
assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
@ -166,65 +166,66 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
def test_small_output_fails(rbf_node, dest_address): def test_small_output_fails(rbf_node, dest_address):
# cannot bump fee with a too-small output # cannot bump fee with a too-small output
rbfid = spend_one_input(rbf_node, rbfid = spend_one_input(rbf_node,
Decimal("1.00000000"), Decimal("10.00000000"),
{dest_address: 0.80000000, {dest_address: 8.00000000,
get_change_address(rbf_node): Decimal("0.10000000")}) get_change_address(rbf_node): Decimal("1.00000000")})
rbf_node.bumpfee(rbfid, {"totalFee": 200000000}) rbf_node.bumpfee(rbfid, {"totalFee": 200000000})
rbfid = spend_one_input(rbf_node, rbfid = spend_one_input(rbf_node,
Decimal("1.00000000"), Decimal("10.00000000"),
{dest_address: 0.800000000, {dest_address: 8.00000000,
get_change_address(rbf_node): Decimal("1.00000000")}) get_change_address(rbf_node): Decimal("1.00000000")})
assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 200000001}) assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 200000001})
def test_dust_to_fee(rbf_node, dest_address): def test_dust_to_fee(rbf_node, dest_address):
# check that if output is reduced to dust, it will be converted to fee # check that if output is reduced to dust, it will be converted to fee
# the bumped tx sets fee=9900, but it converts to 10,000 # the bumped tx sets fee=99,000,000, but it converts to 100,000,000
rbfid = spend_one_input(rbf_node, rbfid = spend_one_input(rbf_node,
Decimal("1.00000000"), Decimal("10.00000000"),
{dest_address: 0.80000000, {dest_address: 8.00000000,
get_change_address(rbf_node): Decimal("0.10000000")}) get_change_address(rbf_node): Decimal("1.00000000")})
fulltx = rbf_node.getrawtransaction(rbfid, 1) fulltx = rbf_node.getrawtransaction(rbfid, 1)
bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 19900000}) bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 199000000})
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1) full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
assert_equal(bumped_tx["fee"], Decimal("0.00020000")) assert_equal(bumped_tx["fee"], Decimal("2.00000000"))
assert_equal(len(fulltx["vout"]), 2) assert_equal(len(fulltx["vout"]), 2)
assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated
def test_settxfee(rbf_node, dest_address): def test_settxfee(rbf_node, dest_address):
# Dogecoin: Increment is fixed, so this test tests for settxfee not making a difference
# check that bumpfee reacts correctly to the use of settxfee (paytxfee) # check that bumpfee reacts correctly to the use of settxfee (paytxfee)
# increase feerate by 2.5x, test that fee increased at least 2x # increase feerate by 2.5x, test that fee increased at least 2x
rbf_node.settxfee(Decimal("0.01000000")) rbf_node.settxfee(Decimal("2.00000000"))
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {dest_address: 3.00000000})
rbftx = rbf_node.gettransaction(rbfid) rbftx = rbf_node.gettransaction(rbfid)
rbf_node.settxfee(Decimal("0.00002500")) rbf_node.settxfee(Decimal("5.00000000"))
bumped_tx = rbf_node.bumpfee(rbfid) bumped_tx = rbf_node.bumpfee(rbfid)
assert bumped_tx["fee"] > 2 * abs(rbftx["fee"]) assert_equal(bumped_tx["fee"], abs(rbftx["fee"]) + Decimal("0.50000000"))
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
def test_rebumping(rbf_node, dest_address): def test_rebumping(rbf_node, dest_address):
# check that re-bumping the original tx fails, but bumping the bumper succeeds # check that re-bumping the original tx fails, but bumping the bumper succeeds
rbf_node.settxfee(Decimal("0.01000000")) rbf_node.settxfee(Decimal("10.00000000"))
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {dest_address: 7.00000000})
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 1000000}) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 150000000})
assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000}) assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000})
rbf_node.bumpfee(bumped["txid"], {"totalFee": 2000000}) rbf_node.bumpfee(bumped["txid"], {"totalFee": 200000000})
def test_rebumping_not_replaceable(rbf_node, dest_address): def test_rebumping_not_replaceable(rbf_node, dest_address):
# check that re-bumping a non-replaceable bump tx fails # check that re-bumping a non-replaceable bump tx fails
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {dest_address: 7.00000000})
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000000, "replaceable": False}) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 150000000, "replaceable": False})
assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
{"totalFee": 20000000}) {"totalFee": 200000000})
def test_unconfirmed_not_spendable(rbf_node, rbf_node_address): def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
# check that unconfirmed outputs from bumped transactions are not spendable # check that unconfirmed outputs from bumped transactions are not spendable
rbfid = create_fund_sign_send(rbf_node, {rbf_node_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {rbf_node_address: 7.00000000})
rbftx = rbf_node.gettransaction(rbfid)["hex"] rbftx = rbf_node.gettransaction(rbfid)["hex"]
assert rbfid in rbf_node.getrawmempool() assert rbfid in rbf_node.getrawmempool()
bumpid = rbf_node.bumpfee(rbfid)["txid"] bumpid = rbf_node.bumpfee(rbfid)["txid"]
@ -259,7 +260,9 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
def test_bumpfee_metadata(rbf_node, dest_address): def test_bumpfee_metadata(rbf_node, dest_address):
rbfid = rbf_node.sendtoaddress(dest_address, 0.90000000, "comment value", "to value") # Use a weirder value because otherwise the existing change outputs are consumed without leaving a change
# output on the TX, and then bumpfee() fails because there's no change.
rbfid = rbf_node.sendtoaddress(dest_address, 5.10000000, "comment value", "to value")
bumped_tx = rbf_node.bumpfee(rbfid) bumped_tx = rbf_node.bumpfee(rbfid)
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"]) bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(bumped_wtx["comment"], "comment value") assert_equal(bumped_wtx["comment"], "comment value")
@ -267,7 +270,7 @@ def test_bumpfee_metadata(rbf_node, dest_address):
def test_locked_wallet_fails(rbf_node, dest_address): def test_locked_wallet_fails(rbf_node, dest_address):
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.90000000}) rbfid = create_fund_sign_send(rbf_node, {dest_address: 8.00000000})
rbf_node.walletlock() rbf_node.walletlock()
assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.", assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid) rbf_node.bumpfee, rbfid)
@ -296,7 +299,7 @@ def get_change_address(node):
dummy transaction, calls fundrawtransaction to give add an input and change dummy transaction, calls fundrawtransaction to give add an input and change
output, then returns the change address.""" output, then returns the change address."""
dest_address = node.getnewaddress() dest_address = node.getnewaddress()
dest_amount = Decimal("0.00012345") dest_amount = Decimal("1.23450000")
rawtx = node.createrawtransaction([], {dest_address: dest_amount}) rawtx = node.createrawtransaction([], {dest_address: dest_amount})
fundtx = node.fundrawtransaction(rawtx) fundtx = node.fundrawtransaction(rawtx)
info = node.decoderawtransaction(fundtx["hex"]) info = node.decoderawtransaction(fundtx["hex"])

View file

@ -2573,7 +2573,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
} }
} while (pindexNewTip != pindexMostWork); } while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus(pindexNewTip->nHeight)); if (pindexNewTip != NULL) {
CheckBlockIndex(chainparams.GetConsensus(pindexNewTip->nHeight));
} else {
CheckBlockIndex(chainparams.GetConsensus(0));
}
// Write changes periodically to disk, after relay. // Write changes periodically to disk, after relay.
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {

View file

@ -2830,6 +2830,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
// Calculate the expected size of the new transaction. // Calculate the expected size of the new transaction.
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
// Doge: Round txSize up to nearest 1kB
if (txSize % 1024 != 0) {
txSize = txSize + 1024 - (txSize % 1024);
}
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx); const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx);
// optional parameters // optional parameters
@ -2896,9 +2900,9 @@ UniValue bumpfee(const JSONRPCRequest& request)
if (specifiedConfirmTarget) { if (specifiedConfirmTarget) {
nNewFee = CWallet::GetMinimumFee(*wtx.tx, maxNewTxSize, newConfirmTarget, mempool, CAmount(0)); nNewFee = CWallet::GetMinimumFee(*wtx.tx, maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
} }
// otherwise use the regular wallet logic to select payTxFee or default confirm target // otherwise bump the fee by 1 DOGE.
else { else {
nNewFee = CWallet::GetMinimumFee(*wtx.tx, maxNewTxSize, newConfirmTarget, mempool); nNewFee = nOldFee + walletIncrementalRelayFee.GetFeePerK();
} }
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);

View file

@ -56,7 +56,7 @@ static const CAmount DEFAULT_TRANSACTION_MINFEE = COIN;
//mlumin 5/2021: adding a minimum Wallet fee vs relay, currently still 1 COIN, to be reduced. //mlumin 5/2021: adding a minimum Wallet fee vs relay, currently still 1 COIN, to be reduced.
static const unsigned int DEFAULT_MIN_WALLET_TX_FEE = COIN; static const unsigned int DEFAULT_MIN_WALLET_TX_FEE = COIN;
//! minimum recommended increment for BIP 125 replacement txs //! minimum recommended increment for BIP 125 replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = COIN * 5; static const CAmount WALLET_INCREMENTAL_RELAY_FEE = COIN/10 * 5;
//! target minimum change amount //! target minimum change amount
static const CAmount MIN_CHANGE = COIN; static const CAmount MIN_CHANGE = COIN;
//! final minimum change amount after paying for fees //! final minimum change amount after paying for fees