wallet, rpc: add anti-fee-sniping to send and sendall
This commit is contained in:
1 parent
fed41b75fb
commit
20802c7b65
4 files changed
+26
-10
No files matched your search
@@ -92,8 +92,21 @@ std::set<int> InterpretSubtractFeeFromOutputInstructions(const UniValue& sffo_in
|
|||||||
return sffo_set;
|
return sffo_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, const CMutableTransaction& rawTx)
|
static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, CMutableTransaction& rawTx)
|
||||||
{
|
{
|
||||||
|
bool can_anti_fee_snipe = !options.exists("locktime");
|
||||||
|
|
||||||
|
for (const CTxIn& tx_in : rawTx.vin) {
|
||||||
|
// Checks sequence values consistent with DiscourageFeeSniping
|
||||||
|
can_anti_fee_snipe = can_anti_fee_snipe && (tx_in.nSequence == CTxIn::MAX_SEQUENCE_NONFINAL || tx_in.nSequence == MAX_BIP125_RBF_SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_anti_fee_snipe) {
|
||||||
|
LOCK(pwallet->cs_wallet);
|
||||||
|
FastRandomContext rng_fast;
|
||||||
|
DiscourageFeeSniping(rawTx, rng_fast, pwallet->chain(), pwallet->GetLastBlockHash(), pwallet->GetLastBlockHeight());
|
||||||
|
}
|
||||||
|
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
PartiallySignedTransaction psbtx(rawTx);
|
PartiallySignedTransaction psbtx(rawTx);
|
||||||
|
|
||||||
@@ -1257,7 +1270,7 @@ RPCHelpMan send()
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
{"locktime", RPCArg::Type::NUM, RPCArg::DefaultHint{"locktime close to block height to prevent fee sniping"}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
||||||
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||||
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
||||||
{"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
|
{"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
|
||||||
@@ -1326,7 +1339,8 @@ RPCHelpMan send()
|
|||||||
rawTx.vout.clear();
|
rawTx.vout.clear();
|
||||||
auto txr = FundTransaction(*pwallet, rawTx, recipients, options, coin_control, /*override_min_fee=*/false);
|
auto txr = FundTransaction(*pwallet, rawTx, recipients, options, coin_control, /*override_min_fee=*/false);
|
||||||
|
|
||||||
return FinishTransaction(pwallet, options, CMutableTransaction(*txr.tx));
|
CMutableTransaction tx = CMutableTransaction(*txr.tx);
|
||||||
|
return FinishTransaction(pwallet, options, tx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1374,7 +1388,7 @@ RPCHelpMan sendall()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
{"locktime", RPCArg::Type::NUM, RPCArg::DefaultHint{"locktime close to block height to prevent fee sniping"}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
||||||
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||||
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
||||||
{"send_max", RPCArg::Type::BOOL, RPCArg::Default{false}, "When true, only use UTXOs that can pay for their own fees to maximize the output amount. When 'false' (default), no UTXO is left behind. send_max is incompatible with providing specific inputs."},
|
{"send_max", RPCArg::Type::BOOL, RPCArg::Default{false}, "When true, only use UTXOs that can pay for their own fees to maximize the output amount. When 'false' (default), no UTXO is left behind. send_max is incompatible with providing specific inputs."},
|
||||||
|
|||||||
@@ -936,11 +936,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256&
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng_fast,
|
||||||
* Set a height-based locktime for new transactions (uses the height of the
|
|
||||||
* current chain tip unless we are not synced with the current chain
|
|
||||||
*/
|
|
||||||
static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng_fast,
|
|
||||||
interfaces::Chain& chain, const uint256& block_hash, int block_height)
|
interfaces::Chain& chain, const uint256& block_hash, int block_height)
|
||||||
{
|
{
|
||||||
// All inputs must be added by now
|
// All inputs must be added by now
|
||||||
|
|||||||
@@ -218,6 +218,12 @@ struct CreatedTransactionResult
|
|||||||
: tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {}
|
: tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a height-based locktime for new transactions (uses the height of the
|
||||||
|
* current chain tip unless we are not synced with the current chain
|
||||||
|
*/
|
||||||
|
void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng_fast, interfaces::Chain& chain, const uint256& block_hash, int block_height);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new transaction paying the recipients with a set of coins
|
* Create a new transaction paying the recipients with a set of coins
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class WalletRescanUnconfirmed(BitcoinTestFramework):
|
|||||||
# The only UTXO available to spend is tx_parent_to_reorg.
|
# The only UTXO available to spend is tx_parent_to_reorg.
|
||||||
assert_equal(len(w0_utxos), 1)
|
assert_equal(len(w0_utxos), 1)
|
||||||
assert_equal(w0_utxos[0]["txid"], tx_parent_to_reorg["txid"])
|
assert_equal(w0_utxos[0]["txid"], tx_parent_to_reorg["txid"])
|
||||||
tx_child_unconfirmed_sweep = w0.sendall([ADDRESS_BCRT1_UNSPENDABLE])
|
tx_child_unconfirmed_sweep = w0.sendall(recipients=[ADDRESS_BCRT1_UNSPENDABLE], options={"locktime":0})
|
||||||
assert tx_child_unconfirmed_sweep["txid"] in node.getrawmempool()
|
assert tx_child_unconfirmed_sweep["txid"] in node.getrawmempool()
|
||||||
node.syncwithvalidationinterfacequeue()
|
node.syncwithvalidationinterfacequeue()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user