import asyncio
from test_framework.electrumutil import (
ElectrumTestFramework,
script_to_scripthash,
)
from test_framework.script import CScript, OP_TRUE, OP_DROP, OP_NOP, OP_FALSE
from test_framework.util import assert_equal
from test_framework.utiltx import pad_tx
from test_framework.serialize import to_hex
from test_framework.electrumconnection import ElectrumConnection
from test_framework.utilcompat import lock, unlock
from test_framework.environment import on_bch, on_nex
if on_nex():
from test_framework.nex.script import anyonecanspend_unlocking_script
elif on_bch():
from test_framework.bch.script import anyonecanspend_unlocking_script
else:
raise NotImplementedError()
GET_HISTORY = "blockchain.scripthash.get_history"
GET_MEMPOOL = "blockchain.scripthash.get_mempool"
class ElectrumMempoolChain(ElectrumTestFramework):
async def run_test(self):
n = self.nodes[0]
await self.bootstrap_p2p()
cli = ElectrumConnection()
try:
await cli.connect()
coinbases = await self.mine_blocks(cli, n, 100)
await self.test_blockheight_unconfirmed(n, cli, coinbases.pop(0))
await self.test_chain_to_from_one_scripthash(n, cli, coinbases.pop(0))
finally:
cli.disconnect()
async def test_blockheight_unconfirmed(self, n, cli, coinbase):
scriptpubkey = CScript([OP_FALSE, OP_DROP, OP_NOP])
scripthash = script_to_scripthash(lock(scriptpubkey))
assert_equal(0, len(await cli.call(GET_HISTORY, scripthash)))
assert_equal(0, len(await cli.call(GET_MEMPOOL, scripthash)))
fee = 200
tx1 = self.create_transaction(
coinbase,
n=0,
value=coinbase.vout[0].n_value - fee,
sig=anyonecanspend_unlocking_script(),
out=lock(scriptpubkey),
)
pad_tx(tx1)
tx2 = self.create_transaction(
tx1,
n=0,
value=tx1.vout[0].n_value - fee,
sig=unlock(scriptpubkey),
out=lock(scriptpubkey),
)
pad_tx(tx2)
tx3 = self.create_transaction(
tx2,
n=0,
value=tx2.vout[0].n_value - fee,
sig=unlock(scriptpubkey),
out=lock(scriptpubkey),
)
pad_tx(tx3)
for tx in [tx1, tx2, tx3]:
await cli.call("blockchain.transaction.broadcast", to_hex(tx))
await self.wait_for_mempool_count(cli, n, count=3)
res = await cli.call(GET_HISTORY, scripthash)
print("RES: ", res)
assert_equal(3, len(res))
assert_equal(res, await cli.call(GET_MEMPOOL, scripthash))
def get_tx(txhash):
for tx in res:
if tx["tx_hash"] == txhash:
return tx
raise AssertionError("tx not in result")
assert_equal(0, get_tx(tx1.get_rpc_hex_id())["height"])
assert_equal(-1, get_tx(tx2.get_rpc_hex_id())["height"])
assert_equal(-1, get_tx(tx3.get_rpc_hex_id())["height"])
await self.mine_blocks(cli, n, 1, [tx1])
for call in [GET_HISTORY, GET_MEMPOOL]:
res = await cli.call(call, scripthash)
if call == GET_HISTORY:
assert_equal(n.getblockcount(), get_tx(tx1.get_rpc_hex_id())["height"])
else:
assert (tx["tx_hash"] != tx1.get_rpc_hex_id() for tx in res)
assert_equal(0, get_tx(tx2.get_rpc_hex_id())["height"])
assert_equal(-1, get_tx(tx3.get_rpc_hex_id())["height"])
await self.mine_blocks(cli, n, 1, [tx2, tx3])
assert len(n.getrawtxpool()) == 0
async def test_chain_to_from_one_scripthash(self, n, cli, coinbase):
scriptpubkey = CScript([OP_TRUE, OP_TRUE, OP_DROP, OP_DROP])
scripthash = script_to_scripthash(lock(scriptpubkey))
assert_equal(0, len(await cli.call(GET_HISTORY, scripthash)))
assert_equal(0, len(await cli.call(GET_MEMPOOL, scripthash)))
def has_tx(res, txhash):
for tx in res:
if tx["tx_hash"] == txhash:
return True
return False
chain_length = 25
tx_chain = self._create_tx_chain(coinbase, scriptpubkey, chain_length)
assert len(n.getrawtxpool()) == 0
await self.p2p.send_txs_and_test(tx_chain, n)
await self.wait_for_mempool_count(cli, n, count=len(tx_chain))
res = await cli.call(GET_HISTORY, scripthash)
assert_equal(len(tx_chain), len(res))
assert all(has_tx(res, tx.get_rpc_hex_id()) for tx in tx_chain)
res_mempool = await cli.call(GET_MEMPOOL, scripthash)
assert_equal(res, res_mempool)
await self.mine_blocks(cli, n, 1, tx_chain)
res = await cli.call(GET_HISTORY, scripthash)
assert_equal(len(tx_chain), len(res))
assert all(has_tx(res, tx.get_rpc_hex_id()) for tx in tx_chain)
assert_equal(0, len(await cli.call(GET_MEMPOOL, scripthash)))
def _create_tx_chain(self, coinbase, scriptpubkey, chain_len):
tx_chain = [coinbase]
fee = 300
for _ in range(chain_len):
prev_tx = tx_chain[-1]
amount = prev_tx.vout[0].n_value - fee
if len(tx_chain) == 1:
unlocking_script = anyonecanspend_unlocking_script()
else:
unlocking_script = unlock(scriptpubkey)
tx = self.create_transaction(
prev_tx,
n=0,
value=amount,
sig=unlocking_script,
out=lock(scriptpubkey),
)
pad_tx(tx)
tx_chain.append(tx)
tx_chain.pop(0) return tx_chain
if __name__ == "__main__":
asyncio.run(ElectrumMempoolChain().main())