Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add best-blockchain to status payload
Browse files Browse the repository at this point in the history
alexruzenhack committed Jul 19, 2023
1 parent 53e6b6d commit cbe6045
Showing 2 changed files with 109 additions and 0 deletions.
5 changes: 5 additions & 0 deletions hathor/p2p/resources/status.py
Original file line number Diff line number Diff line change
@@ -17,8 +17,11 @@
import hathor
from hathor.api_util import Resource, set_cors
from hathor.cli.openapi_files.register import register_resource
from hathor.conf import HathorSettings
from hathor.util import json_dumpb

settings = HathorSettings()


@register_resource
class StatusResource(Resource):
@@ -70,6 +73,7 @@ def render_GET(self, request):
'plugins': status,
'warning_flags': [flag.value for flag in conn.warning_flags],
'protocol_version': str(conn.sync_version),
'best_blockchain': conn.state.best_blockchain,
})

known_peers = []
@@ -114,6 +118,7 @@ def render_GET(self, request):
'hash': best_block.hash_hex,
'height': best_block.get_metadata().height,
},
'best_blockchain': self.manager.get_best_blockchain(settings.DEFAULT_BEST_BLOCKCHAIN_BLOCKS),
}
}
return json_dumpb(data)
104 changes: 104 additions & 0 deletions tests/p2p/test_get_best_blockchain.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from unittest.mock import MagicMock

from twisted.internet.defer import inlineCallbacks

from hathor.conf import HathorSettings
from hathor.manager import DEFAULT_CAPABILITIES
from hathor.p2p.messages import ProtocolMessages
from hathor.p2p.resources import StatusResource
from hathor.p2p.states import ReadyState
from hathor.p2p.states.ready import BlockInfo
from hathor.simulator import FakeConnection
from hathor.simulator.trigger import StopAfterNMinedBlocks
from hathor.util import json_dumps
from tests import unittest
from tests.resources.base_resource import StubSite
from tests.simulation.base import SimulatorTestCase

settings = HathorSettings()
@@ -333,6 +337,106 @@ def test_stop_looping_on_exit(self):
self.assertIsNotNone(state2.lc_get_best_blockchain)
self.assertFalse(state2.lc_get_best_blockchain.running)

@inlineCallbacks
def test_best_blockchain_within_connected_peers(self):
manager1 = self.create_peer()
manager2 = self.create_peer()
conn12 = FakeConnection(manager1, manager2, latency=0.05)
self.simulator.add_connection(conn12)
self.simulator.run(60)

# check /status before generate blocks
self.web = StubSite(StatusResource(manager1))
response = yield self.web.get("status")
data = response.json_value()
connections = data.get('connections')
self.assertEqual(len(connections['connected_peers']), 1)
dag = data.get('dag')

# connected_peers
# assert there is the genesis block
best_blockchain = connections['connected_peers'][0]['best_blockchain']
self.assertEqual(len(best_blockchain), 1)
# assert the block_info height is from genesis
raw_block_info_height = best_blockchain[0][1]
self.assertEqual(raw_block_info_height, 0)

# dag
# assert there is the genesis block
best_blockchain = dag['best_blockchain']
self.assertEqual(len(best_blockchain), 1)
# assert the block_info height is from genesis
raw_block_info_height = best_blockchain[0][1]
self.assertEqual(raw_block_info_height, 0)

# mine 20 blocks
miner = self.simulator.create_miner(manager1, hashpower=1e6)
miner.start()
trigger = StopAfterNMinedBlocks(miner, quantity=20)
self.assertTrue(self.simulator.run(1440, trigger=trigger))
miner.stop()
# let the blocks to propagate
self.simulator.run(60)

# check /status after mine blocks
response = yield self.web.get("status")
data = response.json_value()
connections = data.get('connections')
self.assertEqual(len(connections['connected_peers']), 1)
dag = data.get('dag')

# connected_peers
# assert default best_blockchain length
best_blockchain = connections['connected_peers'][0]['best_blockchain']
self.assertEqual(len(best_blockchain), settings.DEFAULT_BEST_BLOCKCHAIN_BLOCKS)

# assert a raw_block_info can be converted to BlockInfo
try:
raw_block_info = best_blockchain[0]
block_info = BlockInfo.from_raw(raw_block_info)
# assert the first element height is from the lastest block mined
self.assertEqual(block_info.height, 20)
except ValueError:
self.fail('Block info not valid')

# assert decreasing order for a sequence of heights
height_sequence = [b[1] for b in best_blockchain]
try:
self.assertTrue(check_decreasing_monotonicity(height_sequence))
except ValueError as e:
self.fail(str(e))

# dag
# assert default best_blockchain length
best_blockchain = dag['best_blockchain']
self.assertEqual(len(best_blockchain), settings.DEFAULT_BEST_BLOCKCHAIN_BLOCKS)

# assert a raw_block_info can be converted to BlockInfo
try:
raw_block_info = best_blockchain[0]
block_info = BlockInfo.from_raw(raw_block_info)
# assert the first element height is from the lastest block mined
self.assertEqual(block_info.height, 20)
except ValueError:
self.fail('Block info not valid')

# assert decreasing order for a sequence of heights
height_sequence = [b[1] for b in best_blockchain]
try:
self.assertTrue(check_decreasing_monotonicity(height_sequence))
except ValueError as e:
self.fail(str(e))


def check_decreasing_monotonicity(sequence: list[int]) -> bool:
"""Check if a sequence is monotonic and is decreasing. Raise an exception otherwise.
"""
n = len(sequence)
for i in range(1, n):
if sequence[i] >= sequence[i-1]:
raise ValueError(f'Sequence not monotonic. Value {sequence[i]} >= {sequence[i-1]}. Index: {i}.')
return True


class SyncV1GetBestBlockchainTestCase(unittest.SyncV1Params, BaseGetBestBlockchainTestCase):
__test__ = True

0 comments on commit cbe6045

Please sign in to comment.