2018-08-14 17:50:20 +02:00
|
|
|
import asyncio
|
|
|
|
|
import tempfile
|
|
|
|
|
import unittest
|
|
|
|
|
|
2018-09-09 05:00:09 +02:00
|
|
|
from electrum import constants
|
2018-08-14 17:50:20 +02:00
|
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
|
from electrum import blockchain
|
2020-04-15 17:39:39 +02:00
|
|
|
from electrum.interface import Interface, ServerAddr
|
2018-11-20 18:57:16 +01:00
|
|
|
from electrum.crypto import sha256
|
2023-09-06 15:52:33 +00:00
|
|
|
from electrum.util import OldTaskGroup
|
asyncio: stop using get_event_loop(). introduce ~singleton loop.
asyncio.get_event_loop() became deprecated in python3.10. (see https://github.com/python/cpython/issues/83710)
```
.../electrum/electrum/daemon.py:470: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
.../electrum/electrum/network.py:276: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
```
Also, according to that thread, "set_event_loop() [... is] not deprecated by oversight".
So, we stop using get_event_loop() and set_event_loop() in our own code.
Note that libraries we use (such as the stdlib for python <3.10), might call get_event_loop,
which then relies on us having called set_event_loop e.g. for the GUI thread. To work around
this, a custom event loop policy providing a get_event_loop implementation is used.
Previously, we have been using a single asyncio event loop, created with
util.create_and_start_event_loop, and code in many places got a reference to this loop
using asyncio.get_event_loop().
Now, we still use a single asyncio event loop, but it is now stored as a global in
util._asyncio_event_loop (access with util.get_asyncio_loop()).
I believe these changes also fix https://github.com/spesmilo/electrum/issues/5376
2022-04-29 18:24:49 +02:00
|
|
|
from electrum import util
|
2018-11-20 18:57:16 +01:00
|
|
|
|
2019-09-22 20:46:01 +02:00
|
|
|
from . import ElectrumTestCase
|
|
|
|
|
|
2018-08-14 17:50:20 +02:00
|
|
|
|
2018-09-25 17:00:43 +02:00
|
|
|
class MockNetwork:
|
asyncio: stop using get_event_loop(). introduce ~singleton loop.
asyncio.get_event_loop() became deprecated in python3.10. (see https://github.com/python/cpython/issues/83710)
```
.../electrum/electrum/daemon.py:470: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
.../electrum/electrum/network.py:276: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
```
Also, according to that thread, "set_event_loop() [... is] not deprecated by oversight".
So, we stop using get_event_loop() and set_event_loop() in our own code.
Note that libraries we use (such as the stdlib for python <3.10), might call get_event_loop,
which then relies on us having called set_event_loop e.g. for the GUI thread. To work around
this, a custom event loop policy providing a get_event_loop implementation is used.
Previously, we have been using a single asyncio event loop, created with
util.create_and_start_event_loop, and code in many places got a reference to this loop
using asyncio.get_event_loop().
Now, we still use a single asyncio event loop, but it is now stored as a global in
util._asyncio_event_loop (access with util.get_asyncio_loop()).
I believe these changes also fix https://github.com/spesmilo/electrum/issues/5376
2022-04-29 18:24:49 +02:00
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.asyncio_loop = util.get_asyncio_loop()
|
2023-09-06 15:52:33 +00:00
|
|
|
self.taskgroup = OldTaskGroup()
|
2024-10-14 13:32:22 +02:00
|
|
|
self.proxy = None
|
2018-09-25 17:00:43 +02:00
|
|
|
|
2018-09-04 16:57:07 +02:00
|
|
|
class MockInterface(Interface):
|
|
|
|
|
def __init__(self, config):
|
|
|
|
|
self.config = config
|
2018-11-20 18:57:16 +01:00
|
|
|
network = MockNetwork()
|
|
|
|
|
network.config = config
|
2024-10-14 13:32:22 +02:00
|
|
|
super().__init__(network=network, server=ServerAddr.from_str('mock-server:50000:t'))
|
2018-08-14 17:50:20 +02:00
|
|
|
self.q = asyncio.Queue()
|
2018-11-20 18:57:16 +01:00
|
|
|
self.blockchain = blockchain.Blockchain(config=self.config, forkpoint=0,
|
|
|
|
|
parent=None, forkpoint_hash=constants.net.GENESIS, prev_hash=None)
|
2018-09-04 16:57:07 +02:00
|
|
|
self.tip = 12
|
2018-11-20 18:57:16 +01:00
|
|
|
self.blockchain._size = self.tip + 1
|
2023-09-06 15:52:33 +00:00
|
|
|
|
2018-08-14 17:50:20 +02:00
|
|
|
async def get_block_header(self, height, assert_mode):
|
|
|
|
|
assert self.q.qsize() > 0, (height, assert_mode)
|
|
|
|
|
item = await self.q.get()
|
|
|
|
|
print("step with height", height, item)
|
|
|
|
|
assert item['block_height'] == height, (item['block_height'], height)
|
|
|
|
|
assert assert_mode in item['mock'], (assert_mode, item)
|
|
|
|
|
return item
|
|
|
|
|
|
2023-09-06 15:52:33 +00:00
|
|
|
async def run(self):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
2019-09-22 20:46:01 +02:00
|
|
|
class TestNetwork(ElectrumTestCase):
|
2018-09-09 05:00:09 +02:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
super().setUpClass()
|
2024-09-16 15:27:45 +00:00
|
|
|
constants.BitcoinRegtest.set_as_network()
|
2018-09-09 05:00:09 +02:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def tearDownClass(cls):
|
|
|
|
|
super().tearDownClass()
|
2024-09-16 15:27:45 +00:00
|
|
|
constants.BitcoinMainnet.set_as_network()
|
2018-09-09 05:00:09 +02:00
|
|
|
|
2023-02-18 10:01:21 +00:00
|
|
|
async def asyncSetUp(self):
|
|
|
|
|
await super().asyncSetUp()
|
2019-09-22 20:46:01 +02:00
|
|
|
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
2018-09-04 16:57:07 +02:00
|
|
|
self.interface = MockInterface(self.config)
|
2018-08-14 17:50:20 +02:00
|
|
|
|
2023-02-18 10:01:21 +00:00
|
|
|
async def test_fork_noconflict(self):
|
2018-08-14 17:50:20 +02:00
|
|
|
blockchain.blockchains = {}
|
2018-09-04 16:57:07 +02:00
|
|
|
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
|
|
|
|
|
def mock_connect(height):
|
|
|
|
|
return height == 6
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 7, 'mock': {'backward':1,'check': lambda x: False, 'connect': mock_connect, 'fork': self.mock_fork}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 2, 'mock': {'backward':1,'check':lambda x: True, 'connect': lambda x: False}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 4, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
ifa = self.interface
|
2023-02-18 10:01:21 +00:00
|
|
|
res = await ifa.sync_until(8, next_height=7)
|
|
|
|
|
self.assertEqual(('fork', 8), res)
|
2018-09-04 16:57:07 +02:00
|
|
|
self.assertEqual(self.interface.q.qsize(), 0)
|
2018-08-14 17:50:20 +02:00
|
|
|
|
2023-02-18 10:01:21 +00:00
|
|
|
async def test_fork_conflict(self):
|
2018-09-17 22:21:55 +02:00
|
|
|
blockchain.blockchains = {7: {'check': lambda bad_header: False}}
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
|
|
|
|
|
def mock_connect(height):
|
|
|
|
|
return height == 6
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 7, 'mock': {'backward':1,'check': lambda x: False, 'connect': mock_connect, 'fork': self.mock_fork}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 2, 'mock': {'backward':1,'check':lambda x: True, 'connect': lambda x: False}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 4, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
ifa = self.interface
|
2023-02-18 10:01:21 +00:00
|
|
|
res = await ifa.sync_until(8, next_height=7)
|
|
|
|
|
self.assertEqual(('fork', 8), res)
|
2018-09-17 22:21:55 +02:00
|
|
|
self.assertEqual(self.interface.q.qsize(), 0)
|
|
|
|
|
|
2023-02-18 10:01:21 +00:00
|
|
|
async def test_can_connect_during_backward(self):
|
2018-08-14 17:50:20 +02:00
|
|
|
blockchain.blockchains = {}
|
2018-09-04 16:57:07 +02:00
|
|
|
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
|
|
|
|
|
def mock_connect(height):
|
|
|
|
|
return height == 2
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 7, 'mock': {'backward':1, 'check': lambda x: False, 'connect': mock_connect, 'fork': self.mock_fork}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 2, 'mock': {'backward':1, 'check': lambda x: False, 'connect': mock_connect, 'fork': self.mock_fork}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 3, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 4, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
|
|
|
|
|
ifa = self.interface
|
2023-02-18 10:01:21 +00:00
|
|
|
res = await ifa.sync_until(8, next_height=4)
|
|
|
|
|
self.assertEqual(('catchup', 5), res)
|
2018-09-04 16:57:07 +02:00
|
|
|
self.assertEqual(self.interface.q.qsize(), 0)
|
2018-08-14 17:50:20 +02:00
|
|
|
|
|
|
|
|
def mock_fork(self, bad_header):
|
2018-11-20 18:57:16 +01:00
|
|
|
forkpoint = bad_header['block_height']
|
|
|
|
|
b = blockchain.Blockchain(config=self.config, forkpoint=forkpoint, parent=None,
|
2023-02-17 11:35:03 +00:00
|
|
|
forkpoint_hash=sha256(str(forkpoint)).hex(), prev_hash=sha256(str(forkpoint-1)).hex())
|
2018-11-20 18:57:16 +01:00
|
|
|
return b
|
2018-08-14 17:50:20 +02:00
|
|
|
|
2023-02-18 10:01:21 +00:00
|
|
|
async def test_chain_false_during_binary(self):
|
2018-08-14 17:50:20 +02:00
|
|
|
blockchain.blockchains = {}
|
2018-09-04 16:57:07 +02:00
|
|
|
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
|
|
|
|
|
mock_connect = lambda height: height == 3
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 7, 'mock': {'backward':1, 'check': lambda x: False, 'connect': mock_connect}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 2, 'mock': {'backward':1, 'check': lambda x: True, 'connect': mock_connect}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 4, 'mock': {'binary':1, 'check': lambda x: False, 'fork': self.mock_fork, 'connect': mock_connect}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 3, 'mock': {'binary':1, 'check': lambda x: True, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 5, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
|
|
|
|
|
self.interface.q.put_nowait({'block_height': 6, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
|
|
|
|
|
ifa = self.interface
|
2023-02-18 10:01:21 +00:00
|
|
|
res = await ifa.sync_until(8, next_height=6)
|
|
|
|
|
self.assertEqual(('catchup', 7), res)
|
2018-09-04 16:57:07 +02:00
|
|
|
self.assertEqual(self.interface.q.qsize(), 0)
|
2018-08-14 17:50:20 +02:00
|
|
|
|
|
|
|
|
|
2024-09-16 15:27:45 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
constants.BitcoinRegtest.set_as_network()
|
2018-08-14 17:50:20 +02:00
|
|
|
unittest.main()
|