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

Verified

This commit was signed with the committer’s verified signature.
987Nabil Nabil Abdel-Hafeez
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.