8000 rpc: Return block hash & height in getbalances, gettransaction and getwalletinfo by aureleoules · Pull Request #26094 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

rpc: Return block hash & height in getbalances, gettransaction and getwalletinfo #26094

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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 t 8000 o load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes-26094.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- The `getbalances` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
hash and height at the time the balances were calculated. This result shouldn't be cached because importing new keys could invalidate it.
- The `gettransaction` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
hash and height at the time the transaction information was generated.
- The `getwalletinfo` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
hash and height at the time the wallet information was generated.
3 changes: 3 additions & 0 deletions src/wallet/rpc/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ RPCHelpMan getbalances()
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
}},
RESULT_LAST_PROCESSED_BLOCK,
}
},
RPCExamples{
Expand Down Expand Up @@ -488,6 +489,8 @@ RPCHelpMan getbalances()
balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
balances.pushKV("watchonly", balances_watchonly);
}

AppendLastProcessedBlock(balances, wallet);
return balances;
},
};
Expand Down
2 changes: 2 additions & 0 deletions src/wallet/rpc/transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ RPCHelpMan gettransaction()
{
{RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
}},
RESULT_LAST_PROCESSED_BLOCK,
})
},
RPCExamples{
Expand Down Expand Up @@ -791,6 +792,7 @@ RPCHelpMan gettransaction()
entry.pushKV("decoded", decoded);
}

AppendLastProcessedBlock(entry, *pwallet);
return entry;
},
};
Expand Down
10 changes: 10 additions & 0 deletions src/wallet/rpc/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,14 @@ void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& st
throw JSONRPCError(code, error.original);
}
}

void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
AssertLockHeld(wallet.cs_wallet);
UniValue lastprocessedblock{UniValue::VOBJ};
lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
entry.pushKV("lastprocessedblock", lastprocessedblock);
}

} // namespace wallet
10 changes: 8 additions & 2 deletions src/wallet/rpc/util.h
< 8000 th scope="col">Original file line number
Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#ifndef BITCOIN_WALLET_RPC_UTIL_H
#define BITCOIN_WALLET_RPC_UTIL_H

#include <rpc/util.h>
#include <script/script.h>
#include <wallet/wallet.h>

#include <any>
#include <memory>
Expand All @@ -17,13 +19,17 @@ class UniValue;
struct bilingual_str;

namespace wallet {
class CWallet;
class LegacyScriptPubKeyMan;
enum class DatabaseStatus;
struct WalletContext;

extern const std::string HELP_REQUIRING_PASSPHRASE;

static const RPCResult RESULT_LAST_PROCESSED_BLOCK { RPCResult::Type::OBJ, "lastprocessedblock", "hash and height of the block this information was generated on",{
{RPCResult::Type::STR_HEX, "hash", "hash of the block this information was generated on"},
{RPCResult::Type::NUM, "height", "height of the block this information was generated on"}}
};

/**
* Figures out what wallet, if any, to use for a JSONRPCRequest.
*
Expand All @@ -45,8 +51,8 @@ std::string LabelFromValue(const UniValue& value);
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry);

void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error);

int64_t ParseISO8601DateTime(const std::string& str);
void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
} // namespace wallet

#endif // BITCOIN_WALLET_RPC_UTIL_H
3 changes: 3 additions & 0 deletions src/wallet/rpc/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static RPCHelpMan getwalletinfo()
}, /*skip_type_check=*/true},
{RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for scriptPubKey management"},
{RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
RESULT_LAST_PROCESSED_BLOCK,
}},
},
RPCExamples{
Expand Down Expand Up @@ -129,6 +130,8 @@ static RPCHelpMan getwalletinfo()
}
obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));

AppendLastProcessedBlock(obj, *pwallet);
return obj;
},
};
Expand Down
35 changes: 33 additions & 2 deletions test/functional/wallet_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_is_hash_string,
assert_raises_rpc_error,
)

Expand Down Expand Up @@ -183,8 +184,13 @@ def test_balances(*, fee_node_1=0):
'untrusted_pending': Decimal('30.0') - fee_node_1}} # Doesn't include output of node 0's send since it was spent
if self.options.descriptors:
del expected_balances_0["watchonly"]
assert_equal(self.nodes[0].getbalances(), expected_balances_0)
assert_equal(self.nodes[1].getbalances(), expected_balances_1)
balances_0 = self.nodes[0].getbalances()
balances_1 = self.nodes[1].getbalances()
# remove lastprocessedblock keys (they will be tested later)
del balances_0['lastprocessedblock']
del balances_1['lastprocessedblock']
assert_equal(balances_0, expected_balances_0)
assert_equal(balances_1, expected_balances_1)
# getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions
assert_equal(self.nodes[0].getbalance(), Decimal('9.99')) # change from node 0's send
assert_equal(self.nodes[1].getbalance(), Decimal('0')) # node 1's send had an unsafe input
Expand Down Expand Up @@ -309,5 +315,30 @@ def test_balances(*, fee_node_1=0):
assert_equal(self.nodes[0].getbalances()['mine']['untrusted_pending'], Decimal('0.1'))


# Tests the lastprocessedblock JSON object in getbalances, getwalletinfo
# and gettransaction by checking for valid hex strings and by comparing
# the hashes & heights between generated blocks.
self.log.info("Test getbalances returns expected lastprocessedblock json object")
prev_hash = self.nodes[0].getbestblockhash()
prev_height = self.nodes[0].getblock(prev_hash)['height']
self.generatetoaddress(self.nodes[0], 5, self.nodes[0].get_deterministic_priv_key().address)
lastblock = self.nodes[0].getbalances()['lastprocessedblock']
assert_is_hash_string(lastblock['hash'])
assert_equal((prev_hash == lastblock['hash']), False)
assert_equal(lastblock['height'], prev_height + 5)

prev_hash = self.nodes[0].getbestblockhash()
prev_height = self.nodes[0].getblock(prev_hash)['height']
self.log.info("Test getwalletinfo returns expected lastprocessedblock json object")
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['lastprocessedblock']['height'], prev_height)
assert_equal(walletinfo['lastprocessedblock']['hash'], prev_hash)

self.log.info("Test gettransaction returns expected lastprocessedblock json object")
txid = self.nodes[1].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
tx_info = self.nodes[1].gettransaction(txid)
assert_equal(tx_info['lastprocessedblock']['height'], prev_height)
assert_equal(tx_info['lastprocessedblock']['hash'], prev_hash)

if __name__ == '__main__':
WalletTest().main()
2 changes: 1 addition & 1 deletion test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ def run_test(self):
"category": baz["category"],
"vout": baz["vout"]}
expected_fields = frozenset({'amount', 'bip125-replaceable', 'confirmations', 'details', 'fee',
'hex', 'time', 'timereceived', 'trusted', 'txid', 'wtxid', 'walletconflicts'})
'hex', 'lastprocessedblock', 'time', 'timereceived', 'trusted', 'txid', 'wtxid', 'walletconflicts'})
verbose_field = "decoded"
expected_verbose_fields = expected_fields | {verbose_field}

Expand Down
5 changes: 4 additions & 1 deletion test/functional/wallet_orphanedreward.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ def run_test(self):
assert_equal(self.nodes[0].getbestblockhash(), orig_chain_tip)
self.generate(self.nodes[0], 3)

assert_equal(self.nodes[1].getbalances(), pre_reorg_conf_bals)
balances = self.nodes[1].getbalances()
del balances["lastprocessedblock"]
del pre_reorg_conf_bals["lastprocessedblock"]
assert_equal(balances, pre_reorg_conf_bals)
assert_equal(self.nodes[1].gettransaction(txid)["details"][0]["abandoned"], True)


Expand Down
0