Skip to content
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

RPC: Add child transactions to getrawmempool verbose output #12479

Merged
merged 2 commits into from Mar 6, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/rpc/blockchain.cpp
Expand Up @@ -372,6 +372,9 @@ std::string EntryDescriptionString()
" \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n"
" \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n"
" \"transactionid\", (string) child transaction id\n"
" ... ]\n";
}

Expand Down Expand Up @@ -406,6 +409,15 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
}

info.pushKV("depends", depends);

UniValue spent(UniValue::VARR);
const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash());
const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it);
for (const CTxMemPool::txiter &childiter : setChildren) {
spent.push_back(childiter->GetTx().GetHash().ToString());
}

info.pushKV("spentby", spent);
}

UniValue mempoolToJSON(bool fVerbose)
Expand Down
4 changes: 3 additions & 1 deletion test/functional/interface_rest.py
Expand Up @@ -295,8 +295,10 @@ def run_test(self):
# check that there are our submitted transactions in the TX memory pool
json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
for tx in txs:
for i, tx in enumerate(txs):
assert_equal(tx in json_obj, True)
assert_equal(json_obj[tx]['spentby'], txs[i+1:i+2])
assert_equal(json_obj[tx]['depends'], txs[i-1:i])

# now mine the transactions
newblockhash = self.nodes[1].generate(1)
Expand Down
46 changes: 43 additions & 3 deletions test/functional/mempool_packages.py
Expand Up @@ -47,14 +47,18 @@ def run_test(self):
value = sent_value
chain.append(txid)

# Check mempool has MAX_ANCESTORS transactions in it, and descendant
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
# count and fees should look correct
mempool = self.nodes[0].getrawmempool(True)
assert_equal(len(mempool), MAX_ANCESTORS)
descendant_count = 1
descendant_fees = 0
descendant_size = 0

ancestor_size = sum([mempool[tx]['size'] for tx in mempool])
ancestor_count = MAX_ANCESTORS
ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])

descendants = []
ancestors = list(chain)
for x in reversed(chain):
Expand All @@ -71,14 +75,43 @@ def run_test(self):
assert_equal(mempool[x]['descendantsize'], descendant_size)
descendant_count += 1

# Check that ancestor calculations are correct
assert_equal(mempool[x]['ancestorcount'], ancestor_count)
assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN)
assert_equal(mempool[x]['ancestorsize'], ancestor_size)
ancestor_size -= mempool[x]['size']
ancestor_fees -= mempool[x]['fee']
ancestor_count -= 1

# Check that parent/child list is correct
assert_equal(mempool[x]['spentby'], descendants[-1:])
assert_equal(mempool[x]['depends'], ancestors[-2:-1])

# Check that getmempooldescendants is correct
assert_equal(sorted(descendants), sorted(self.nodes[0].getmempooldescendants(x)))

# Check getmempooldescendants verbose output is correct
for descendant, dinfo in self.nodes[0].getmempooldescendants(x, True).items():
assert_equal(dinfo['depends'], [chain[chain.index(descendant)-1]])
if dinfo['descendantcount'] > 1:
assert_equal(dinfo['spentby'], [chain[chain.index(descendant)+1]])
else:
assert_equal(dinfo['spentby'], [])
descendants.append(x)

# Check that getmempoolancestors is correct
ancestors.remove(x)
assert_equal(sorted(ancestors), sorted(self.nodes[0].getmempoolancestors(x)))

# Check that getmempoolancestors verbose output is correct
for ancestor, ainfo in self.nodes[0].getmempoolancestors(x, True).items():
assert_equal(ainfo['spentby'], [chain[chain.index(ancestor)+1]])
if ainfo['ancestorcount'] > 1:
assert_equal(ainfo['depends'], [chain[chain.index(ancestor)-1]])
else:
assert_equal(ainfo['depends'], [])


# Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true
v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True)
assert_equal(len(v_ancestors), len(chain)-1)
Expand All @@ -100,7 +133,7 @@ def run_test(self):
for x in chain:
ancestor_fees += mempool[x]['fee']
assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)

# Undo the prioritisetransaction for later tests
self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)

Expand Down Expand Up @@ -149,6 +182,7 @@ def run_test(self):
vout = utxo[1]['vout']

transaction_package = []
tx_children = []
# First create one parent tx with 10 children
(txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
parent_transaction = txid
Expand All @@ -159,11 +193,17 @@ def run_test(self):
for i in range(MAX_DESCENDANTS - 1):
utxo = transaction_package.pop(0)
(txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
if utxo['txid'] is parent_transaction:
tx_children.append(txid)
for j in range(10):
transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})

mempool = self.nodes[0].getrawmempool(True)
assert_equal(mempool[parent_transaction]['descendantcount'], MAX_DESCENDANTS)
assert_equal(sorted(mempool[parent_transaction]['spentby']), sorted(tx_children))

for child in tx_children:
assert_equal(mempool[child]['depends'], [parent_transaction])

# Sending one more chained transaction will fail
utxo = transaction_package.pop(0)
Expand Down Expand Up @@ -232,7 +272,7 @@ def run_test(self):
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
sync_mempools(self.nodes)

# Now try to disconnect the tip on each node...
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
Expand Down