Merge #8456: [RPC] Simplified bumpfee command.

cc0243a [RPC] bumpfee (mrbandrews)
52dde66 [wallet] Add include_unsafe argument to listunspent RPC (Russell Yanofsky)
766e8a4 [wallet] Add IsAllFromMe: true if all inputs are from wallet (Suhas Daftuar)
This commit is contained in:
Wladimir J. van der Laan
2017-01-19 19:59:19 +01:00
8 changed files with 715 additions and 14 deletions

View File

@@ -411,6 +411,13 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
return result;
}
bool CWallet::HasWalletSpend(const uint256& txid) const
{
AssertLockHeld(cs_wallet);
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
return (iter != mapTxSpends.end() && iter->first.hash == txid);
}
void CWallet::Flush(bool shutdown)
{
bitdb.Flush(shutdown);
@@ -826,6 +833,35 @@ void CWallet::MarkDirty()
}
}
bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
{
LOCK(cs_wallet);
auto mi = mapWallet.find(originalHash);
// There is a bug if MarkReplaced is not called on an existing wallet transaction.
assert(mi != mapWallet.end());
CWalletTx& wtx = (*mi).second;
// Ensure for now that we're not overwriting data
assert(wtx.mapValue.count("replaced_by_txid") == 0);
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
CWalletDB walletdb(strWalletFile, "r+");
bool success = true;
if (!walletdb.WriteTx(wtx)) {
LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString());
success = false;
}
NotifyTransactionChanged(this, originalHash, CT_UPDATED);
return success;
}
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
@@ -1154,6 +1190,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
return ISMINE_NO;
}
// Note that this function doesn't distinguish between a 0-valued input,
// and a not-"is mine" (according to the filter) input.
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
@@ -1236,6 +1274,27 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
return nDebit;
}
bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
{
LOCK(cs_wallet);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
auto mi = mapWallet.find(txin.prevout.hash);
if (mi == mapWallet.end())
return false; // any unknown inputs can't be from us
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n >= prev.tx->vout.size())
return false; // invalid input!
if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
return false;
}
return true;
}
CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
{
CAmount nCredit = 0;
@@ -1958,6 +2017,37 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
// We should not consider coins from transactions that are replacing
// other transactions.
//
// Example: There is a transaction A which is replaced by bumpfee
// transaction B. In this case, we want to prevent creation of
// a transaction B' which spends an output of B.
//
// Reason: If transaction A were initially confirmed, transactions B
// and B' would no longer be valid, so the user would have to create
// a new transaction C to replace B'. However, in the case of a
// one-block reorg, transactions B' and C might BOTH be accepted,
// when the user only wanted one of them. Specifically, there could
// be a 1-block reorg away from the chain where transactions A and C
// were accepted to another chain where B, B', and C were all
// accepted.
if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) {
continue;
}
// Similarly, we should not consider coins from transactions that
// have been replaced. In the example above, we would want to prevent
// creation of a transaction A' spending an output of A, because if
// transaction B were initially confirmed, conflicting with A and
// A', we wouldn't want to the user to create a transaction D
// intending to replace A', but potentially resulting in a scenario
// where A, A', and D could all be accepted (instead of just B and
// D, or just A and A' like the user would want).
if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) {
continue;
}
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
isminetype mine = IsMine(pcoin->tx->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&