比特币 RPC 命令「listsinceblock」
$ bitcoin-cli help listsinceblock listsinceblock ( "blockhash" target-confirmations includeWatchonly ) 获取从区块 [blockhash] 开始的区块中的全部交易,如果该参数省略则获取全部区块交易 参数: 1. "blockhash" (字符串,可选)列出从该区块哈希开始的交易 2. target-confirmations(数字型,可选)所需的确认数,必须为 1 或更多 3. includeWatchonly (布尔型,可选,默认为 false)包含 watchonly 地址的交易(见 'importaddress') 结果: { "transactions": [ "account":"accountname", (字符串)已过时。关联该交易的帐户名。默认账户为 ""。 "address":"bitcoinaddress",(字符串)该交易的比特币地址。不代表移动交易(类别为 = move)。 "category":"send|receive", (字符串)该交易类别。'send' 为负,'receive' 为正。 "amount": x.xxx, (数字)以 BTC 为单位的金额。对于 'send' 类型为负,且对于 'move' 类型为移出。 对于 'receive' 类型为正,且对于 'move' 类型为移入资金。 "vout" : n, (数字)输出序号 "fee": x.xxx, (数字)以 BTC 为单位的交易费。只对于 'send' 类型的交易是负。 "confirmations": n, (数字)该交易的确认数。适用于 'send' 和 'receive' 类型的交易。 "blockhash": "hashvalue", (字符串)包含该交易的区块哈希。适用于 'send' 和 'receive' 类型的交易。 "blockindex": n, (数字)包含该交易的区块索引。适用于 'send' 和 'receive' 类型的交易。 "blocktime": xxx, (数字)从格林尼治时间(1970-01-01 00:00:00)开始以秒为单位的区块接收时间。 "txid": "transactionid", (字符串)该交易索引。适用于 'send' 和 'receive' 类型的交易。 "time": xxx, (数字)从格林尼治时间(1970-01-01 00:00:00)开始以秒为单位的交易时间。 "timereceived": xxx, (数字)从格林尼治时间(1970-01-01 00:00:00)开始以秒为单位的交易接收时间。 "comment": "...", (字符串)该交易关联的一条备注。 "label" : "label" (字符串)该地址/交易的一条备注,如果存在 "to": "...", (字符串)交易目标关联的一条备注。 ], "lastblock": "lastblockhash" (字符串)最新的区块哈希 } 例子: > bitcoin-cli listsinceblock > bitcoin-cli listsinceblock "000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad" 6 > curl --user myusername:mypassword --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listsinceblock", "params": ["000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad", 6] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
源码剖析
listsinceblock
对应的函数在文件 rpcserver.h
中被引用。
extern UniValue listsinceblock(const UniValue& params, bool fHelp);
实现在文件 wallet/rpcwallet.cpp
中。
UniValue listsinceblock(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp)) // 1. 确保钱包可用
return NullUniValue;
if (fHelp)
throw runtime_error(
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
"2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
"3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
" \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n"
" \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n"
" outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n"
" \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
" \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
" \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
" ],\n"
" \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("listsinceblock", "")
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
); // 2. 帮助内容
LOCK2(cs_main, pwalletMain->cs_wallet);
CBlockIndex *pindex = NULL;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE; // watchonly
if (params.size() > 0)
{
uint256 blockId;
blockId.SetHex(params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
if (it != mapBlockIndex.end())
pindex = it->second;
}
if (params.size() > 1)
{
target_confirms = params[1].get_int();
if (target_confirms < 1)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
UniValue transactions(UniValue::VARR);
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
{
CWalletTx tx = (*it).second;
if (depth == -1 || tx.GetDepthInMainChain() < depth)
ListTransactions(tx, "*", 0, true, transactions, filter); // 3. 列出交易
}
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("transactions", transactions));
ret.push_back(Pair("lastblock", lastblock.GetHex()));
return ret;
}
1. 确保钱包可用
参考比特币 RPC 命令「fundrawtransaction」1. 确保钱包可用。
2. 帮助内容
参考比特币 RPC 命令「getbestblockhash」1. 帮助内容。
3. 列出交易
函数 ListTransactions(tx, "*", 0, true, transactions, filter)
定义在文件 wallet/rpcwallet.cpp
中。
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
{
CAmount nFee; // 交易费
string strSentAccount; // 发送账户
list<COutputEntry> listReceived; // 接收输出条目列表
list<COutputEntry> listSent; // 发送输出条目列表
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); // 获取相应金额
bool fAllAccounts = (strAccount == string("*")); // 全部账户标志
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); // watchonly 标志
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
{ // 发送列表非空 或 交易费非 0 且 全部账户 或 发送账户为指定账户(这里是 "*" 表示全部账户)
BOOST_FOREACH(const COutputEntry& s, listSent) // 遍历发送列表
{
UniValue entry(UniValue::VOBJ);
if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount)); // 发送账户
MaybePushAddress(entry, s.destination); // 发送地址
entry.push_back(Pair("category", "send")); // 交易类别为发送
entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); // 交易金额,符号表示发送
if (pwalletMain->mapAddressBook.count(s.destination))
entry.push_back(Pair("label", pwalletMain->mapAddressBook[s.destination].name)); // 标签为帐户名
entry.push_back(Pair("vout", s.vout)); // 输出索引
entry.push_back(Pair("fee", ValueFromAmount(-nFee))); // 交易费
if (fLong) // true
WalletTxToJSON(wtx, entry); // 钱包交易信息转化为 JSON 格式
entry.push_back(Pair("abandoned", wtx.isAbandoned())); // 是否被抛弃
ret.push_back(entry); // 加入交易信息集
}
}
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
{ // 接收列表非空 且 该交易深度大于等于最小深度(确认数)
BOOST_FOREACH(const COutputEntry& r, listReceived) // 遍历接收列表
{
string account;
if (pwalletMain->mapAddressBook.count(r.destination)) // 若该地址存在于地址簿
account = pwalletMain->mapAddressBook[r.destination].name; // 获取地址对应账户名
if (fAllAccounts || (account == strAccount)) // 全部账户 或 该账户为指定账户("*")
{
UniValue entry(UniValue::VOBJ);
if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account)); // 账户名
MaybePushAddress(entry, r.destination); // 接收地址
if (wtx.IsCoinBase()) // 该交易为创币交易
{
if (wtx.GetDepthInMainChain() < 1) // 该交易在主链上的深度为 0
entry.push_back(Pair("category", "orphan")); // 孤儿链
else if (wtx.GetBlocksToMaturity() > 0) // 成熟所需区块数大于 0
entry.push_back(Pair("category", "immature")); // 未成熟
else
entry.push_back(Pair("category", "generate")); // regtest 生成
}
else
{ // 普通交易(非创币交易)
entry.push_back(Pair("category", "receive")); // 交易类别为接收
}
entry.push_back(Pair("amount", ValueFromAmount(r.amount))); // 交易金额
if (pwalletMain->mapAddressBook.count(r.destination)) // 若该地址存在于地址簿
entry.push_back(Pair("label", account)); // 标签为该地址对应的帐户名
entry.push_back(Pair("vout", r.vout)); // 输出索引
if (fLong)
WalletTxToJSON(wtx, entry); // 钱包交易信息转化为 JSON
ret.push_back(entry); // 加入交易信息集
}
}
}
}
函数 WalletTxToJSON(wtx, entry)
定义在文件 wallet/rpcwallet.cpp
中。
void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
{
int confirms = wtx.GetDepthInMainChain();
entry.push_back(Pair("confirmations", confirms)); // 确认数
if (wtx.IsCoinBase())
entry.push_back(Pair("generated", true)); // 为创币交易
if (confirms > 0) // 已上链
{
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); // 区块哈希
entry.push_back(Pair("blockindex", wtx.nIndex)); // 交易索引
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); // 区块创建时间
} else { // 还在内存池中(未上链)
entry.push_back(Pair("trusted", wtx.IsTrusted())); // 该交易可信
}
uint256 hash = wtx.GetHash();
entry.push_back(Pair("txid", hash.GetHex())); // 交易索引
UniValue conflicts(UniValue::VARR);
BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
conflicts.push_back(conflict.GetHex());
entry.push_back(Pair("walletconflicts", conflicts)); // 钱包冲突
entry.push_back(Pair("time", wtx.GetTxTime())); // 交易发起时间
entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); // 交易接收时间
// Add opt-in RBF status // 添加选择性的 RBF 状态
std::string rbfStatus = "no";
if (confirms <= 0) {
LOCK(mempool.cs);
if (!mempool.exists(hash)) {
if (SignalsOptInRBF(wtx)) {
rbfStatus = "yes";
} else {
rbfStatus = "unknown";
}
} else if (IsRBFOptIn(*mempool.mapTx.find(hash), mempool)) {
rbfStatus = "yes";
}
}
entry.push_back(Pair("bip125-replaceable", rbfStatus));
BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
entry.push_back(Pair(item.first, item.second));
}