Jay Graber
6 years ago
2 changed files with 188 additions and 0 deletions
@ -0,0 +1,187 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2018 The Zcash developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Test proper expiry for transactions >= version 3 |
|||
# |
|||
|
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.authproxy import JSONRPCException |
|||
from test_framework.util import assert_equal, connect_nodes, \ |
|||
connect_nodes_bi, sync_blocks, start_nodes, sync_blocks, sync_mempools, \ |
|||
wait_and_assert_operationid_status |
|||
|
|||
from decimal import Decimal |
|||
import time |
|||
|
|||
class MempoolTxExpiryTest(BitcoinTestFramework): |
|||
|
|||
def setup_nodes(self): |
|||
return start_nodes(4, self.options.tmpdir, [["-nuparams=5ba81b19:205", "-txexpirydelta=4"]] * 4) |
|||
|
|||
# Test before, at, and after expiry block |
|||
# TODO: Test case of dependent txs in reorgs |
|||
# chain is at block height 199 when run_test executes |
|||
def run_test(self): |
|||
alice = self.nodes[0].getnewaddress() |
|||
z_alice = self.nodes[0].z_getnewaddress() |
|||
bob = self.nodes[2].getnewaddress() |
|||
z_bob = self.nodes[2].z_getnewaddress() |
|||
|
|||
# When Overwinter not yet activated, no expiryheight in tx |
|||
sapling_tx = self.nodes[0].sendtoaddress(bob, 0.01) |
|||
rawtx = self.nodes[0].getrawtransaction(sapling_tx, 1) |
|||
assert_equal(rawtx["overwintered"], False) |
|||
assert("expiryheight" not in rawtx) |
|||
|
|||
self.nodes[0].generate(6) |
|||
self.sync_all() |
|||
|
|||
## Shield one of Alice's coinbase funds to her zaddr |
|||
res = self.nodes[0].z_shieldcoinbase("*", z_alice, 0.0001, 1) |
|||
wait_and_assert_operationid_status(self.nodes[0], res['opid']) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
# Get balance on node 0 |
|||
bal = self.nodes[0].z_gettotalbalance() |
|||
print "Balance before zsend, after shielding 10: ", bal |
|||
assert_equal(Decimal(bal["private"]), Decimal("9.9999")) |
|||
|
|||
print "Splitting network..." |
|||
self.split_network() |
|||
|
|||
# Create transactions |
|||
zsendamount = Decimal('1.0') - Decimal('0.0001') |
|||
recipients = [] |
|||
recipients.append({"address": z_bob, "amount": zsendamount}) |
|||
myopid = self.nodes[0].z_sendmany(z_alice, recipients) |
|||
persist_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) |
|||
persist_transparent = self.nodes[0].sendtoaddress(bob, 0.01) |
|||
# Verify transparent transaction is version 3 intended for Overwinter branch |
|||
rawtx = self.nodes[0].getrawtransaction(persist_transparent, 1) |
|||
assert_equal(rawtx["version"], 3) |
|||
assert_equal(rawtx["overwintered"], True) |
|||
assert_equal(rawtx["expiryheight"], 212) |
|||
print "Blockheight at persist_transparent & persist_shielded creation:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Expiryheight of persist_transparent:", rawtx['expiryheight'] |
|||
# Verify shielded transaction is version 3 intended for Overwinter branch |
|||
rawtx = self.nodes[0].getrawtransaction(persist_shielded, 1) |
|||
print "Expiryheight of persist_shielded", rawtx['expiryheight'] |
|||
assert_equal(rawtx["version"], 3) |
|||
assert_equal(rawtx["overwintered"], True) |
|||
assert_equal(rawtx["expiryheight"], 212) |
|||
|
|||
print "\n Blockheight advances to less than expiry block height. After reorg, txs should persist in mempool" |
|||
assert(persist_transparent in self.nodes[0].getrawmempool()) |
|||
assert(persist_shielded in self.nodes[0].getrawmempool()) |
|||
assert_equal(set(self.nodes[2].getrawmempool()), set()) |
|||
print "mempool node 0:", self.nodes[0].getrawmempool() |
|||
print "mempool node 2:", self.nodes[2].getrawmempool() |
|||
bal = self.nodes[0].z_gettotalbalance() |
|||
print "Printing balance before persist_shielded & persist_transparent are initially mined from mempool", bal |
|||
# Txs are mined on node 0; will later be rolled back |
|||
self.nodes[0].generate(1) |
|||
print "Node 0 generated 1 block" |
|||
print "Node 0 height:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Node 2 height:", self.nodes[2].getblockchaininfo()['blocks'] |
|||
bal = self.nodes[0].z_gettotalbalance() |
|||
print "Printing balance after persist_shielded & persist_transparent are mined:", bal |
|||
assert_equal(set(self.nodes[0].getrawmempool()), set()) |
|||
|
|||
print "Mine 2 competing blocks on Node 2..." |
|||
blocks = self.nodes[2].generate(2) |
|||
for block in blocks: |
|||
blk = self.nodes[2].getblock(block) |
|||
print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) |
|||
print "Connect nodes to force a reorg" |
|||
connect_nodes_bi(self.nodes,0,2) |
|||
self.is_network_split = False |
|||
|
|||
print "Syncing blocks" |
|||
sync_blocks(self.nodes) |
|||
|
|||
print "Ensure that txs are back in mempool of node 0" |
|||
print "Blockheight node 0:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Blockheight node 2:", self.nodes[2].getblockchaininfo()['blocks'] |
|||
print "mempool node 0: ", self.nodes[0].getrawmempool() |
|||
print "mempool node 2: ", self.nodes[2].getrawmempool() |
|||
assert(persist_transparent in self.nodes[0].getrawmempool()) |
|||
assert(persist_shielded in self.nodes[0].getrawmempool()) |
|||
bal = self.nodes[0].z_gettotalbalance() |
|||
# Mine txs to get them out of the way of mempool sync in split_network() |
|||
print "Generating another block on node 0 to clear txs from mempool" |
|||
self.nodes[0].generate(1) |
|||
assert_equal(set(self.nodes[0].getrawmempool()), set()) |
|||
sync_blocks(self.nodes) |
|||
|
|||
print "Splitting network..." |
|||
self.split_network() |
|||
|
|||
print "\n Blockheight advances to equal expiry block height. After reorg, txs should persist in mempool" |
|||
myopid = self.nodes[0].z_sendmany(z_alice, recipients) |
|||
persist_shielded_2 = wait_and_assert_operationid_status(self.nodes[0], myopid) |
|||
persist_transparent_2 = self.nodes[0].sendtoaddress(bob, 0.01) |
|||
rawtx_trans = self.nodes[0].getrawtransaction(persist_transparent_2, 1) |
|||
rawtx_shield = self.nodes[0].getrawtransaction(persist_shielded_2, 1) |
|||
print "Blockheight node 0 at persist_transparent_2 creation:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Blockheight node 2 at persist_transparent_2 creation:", self.nodes[2].getblockchaininfo()['blocks'] |
|||
print "Expiryheight of persist_transparent_2:", rawtx_trans['expiryheight'] |
|||
print "Expiryheight of persist_shielded_2:", rawtx_shield['expiryheight'] |
|||
blocks = self.nodes[2].generate(4) |
|||
for block in blocks: |
|||
blk = self.nodes[2].getblock(block) |
|||
print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) |
|||
print "Connect nodes to force a reorg" |
|||
connect_nodes_bi(self.nodes, 0, 2) |
|||
self.is_network_split = False |
|||
sync_blocks(self.nodes) |
|||
print "Ensure that persist_transparent_2 & persist_shielded_2 are in mempool at expiry block height" |
|||
print "Blockheight node 0:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Blockheight node 2:", self.nodes[2].getblockchaininfo()['blocks'] |
|||
print "mempool node 0: ", self.nodes[0].getrawmempool() |
|||
print "mempool node 2: ", self.nodes[2].getrawmempool() |
|||
assert(persist_transparent_2 in self.nodes[0].getrawmempool()) |
|||
assert(persist_shielded_2 in self.nodes[0].getrawmempool()) |
|||
# Mine persist txs to get them out of the way of mempool sync in split_network() |
|||
self.nodes[0].generate(1) |
|||
assert_equal(set(self.nodes[0].getrawmempool()), set()) |
|||
sync_blocks(self.nodes) |
|||
print "Balance after persist_shielded_2 is mined to remove from mempool: ", self.nodes[0].z_gettotalbalance() |
|||
|
|||
print "Splitting network..." |
|||
self.split_network() |
|||
|
|||
print "\n Blockheight advances to greater than expiry block height. After reorg, txs should expire from mempool" |
|||
print "Balance before expire_shielded is sent: ", self.nodes[0].z_gettotalbalance() |
|||
myopid = self.nodes[0].z_sendmany(z_alice, recipients) |
|||
expire_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) |
|||
expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01) |
|||
print "Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks'] |
|||
print "Blockheight node 2 at expire_shielded creation:", self.nodes[2].getblockchaininfo()['blocks'] |
|||
print "Expiryheight of expire_transparent:", self.nodes[0].getrawtransaction(expire_transparent, 1)['expiryheight'] |
|||
print "Expiryheight of expire_shielded:", self.nodes[0].getrawtransaction(expire_shielded, 1)['expiryheight'] |
|||
assert(expire_transparent in self.nodes[0].getrawmempool()) |
|||
assert(expire_shielded in self.nodes[0].getrawmempool()) |
|||
blocks = self.nodes[2].generate(6) |
|||
for block in blocks: |
|||
blk = self.nodes[2].getblock(block) |
|||
print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) |
|||
print "Connect nodes to force a reorg" |
|||
connect_nodes_bi(self.nodes, 0, 2) |
|||
self.is_network_split = False |
|||
sync_blocks(self.nodes) |
|||
print "Ensure that expire_transparent & expire_shielded are in mempool at expiry block height" |
|||
print "mempool node 0: ", self.nodes[0].getrawmempool() |
|||
print "mempool node 2: ", self.nodes[2].getrawmempool() |
|||
assert_equal(set(self.nodes[0].getrawmempool()), set()) |
|||
print "Ensure balance of node 0 is correct" |
|||
bal = self.nodes[0].z_gettotalbalance() |
|||
print "Balance after expire_shielded has expired: ", bal |
|||
assert_equal(Decimal(bal["private"]), Decimal("7.9999")) |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
MempoolTxExpiryTest().main() |
Loading…
Reference in new issue