/quarkchain/cluster/p2p_commands.py

https://github.com/QuarkChain/pyquarkchain · Python · 361 lines · 265 code · 79 blank · 17 comment · 14 complexity · 899157021cb26248ac8ad27b56095c5d MD5 · raw file

  1. from enum import IntEnum
  2. from quarkchain.core import (
  3. Branch,
  4. uint8,
  5. uint16,
  6. uint32,
  7. uint128,
  8. hash256,
  9. TypedTransaction,
  10. )
  11. from quarkchain.core import RootBlockHeader, MinorBlockHeader, RootBlock, MinorBlock
  12. from quarkchain.core import Serializable, PrependedSizeListSerializer
  13. from quarkchain.utils import check
  14. class HelloCommand(Serializable):
  15. FIELDS = [
  16. ("version", uint32),
  17. ("network_id", uint32),
  18. ("peer_id", hash256),
  19. ("peer_ip", uint128),
  20. ("peer_port", uint16),
  21. (
  22. "chain_mask_list",
  23. PrependedSizeListSerializer(4, uint32),
  24. ), # TODO create shard mask object
  25. ("root_block_header", RootBlockHeader),
  26. ("genesis_root_block_hash", hash256),
  27. ]
  28. def __init__(
  29. self,
  30. version,
  31. network_id,
  32. peer_id,
  33. peer_ip,
  34. peer_port,
  35. chain_mask_list,
  36. root_block_header,
  37. genesis_root_block_hash,
  38. ):
  39. fields = {k: v for k, v in locals().items() if k != "self"}
  40. super(type(self), self).__init__(**fields)
  41. class GetPeerListRequest(Serializable):
  42. FIELDS = [("max_peers", uint32)]
  43. def __init__(self, max_peers):
  44. self.max_peers = max_peers
  45. class PeerInfo(Serializable):
  46. FIELDS = [("ip", uint128), ("port", uint16)]
  47. def __init__(self, ip, port):
  48. self.ip = ip
  49. self.port = port
  50. class GetPeerListResponse(Serializable):
  51. FIELDS = [("peer_info_list", PrependedSizeListSerializer(4, PeerInfo))]
  52. def __init__(self, peer_info_list=None):
  53. self.peer_info_list = peer_info_list if peer_info_list is not None else []
  54. class NewMinorBlockHeaderListCommand(Serializable):
  55. """RPC to inform peers about new root or minor blocks with the following constraints
  56. - If the RPC is sent to root, then the minor block header list must be empty.
  57. - If the RPC is sent to a shard, then all minor block headers must be in the shard.
  58. """
  59. FIELDS = [
  60. ("root_block_header", RootBlockHeader),
  61. ("minor_block_header_list", PrependedSizeListSerializer(4, MinorBlockHeader)),
  62. ]
  63. def __init__(self, root_block_header, minor_block_header_list):
  64. self.root_block_header = root_block_header
  65. self.minor_block_header_list = minor_block_header_list
  66. class NewTransactionListCommand(Serializable):
  67. """ Broadcast transactions """
  68. FIELDS = [("transaction_list", PrependedSizeListSerializer(4, TypedTransaction))]
  69. def __init__(self, transaction_list=None):
  70. self.transaction_list = transaction_list if transaction_list is not None else []
  71. class Direction(IntEnum):
  72. GENESIS = 0
  73. TIP = 1
  74. class GetRootBlockHeaderListRequest(Serializable):
  75. """ Obtain block hashs in the active chain.
  76. """
  77. FIELDS = [
  78. ("block_hash", hash256),
  79. ("limit", uint32),
  80. ("direction", uint8), # 0 to genesis, 1 to tip
  81. ]
  82. def __init__(self, block_hash, limit, direction):
  83. self.block_hash = block_hash
  84. self.limit = limit
  85. self.direction = direction
  86. class GetRootBlockHeaderListResponse(Serializable):
  87. FIELDS = [
  88. ("root_tip", RootBlockHeader),
  89. ("block_header_list", PrependedSizeListSerializer(4, RootBlockHeader)),
  90. ]
  91. def __init__(self, root_tip, block_header_list):
  92. self.root_tip = root_tip
  93. self.block_header_list = block_header_list
  94. class GetRootBlockHeaderListWithSkipRequest(Serializable):
  95. FIELDS = [
  96. ("type", uint8), # 0 block hash, 1 block height
  97. ("data", hash256),
  98. ("limit", uint32),
  99. ("skip", uint32),
  100. ("direction", uint8), # 0 to genesis, 1 to tip
  101. ]
  102. def __init__(self, type, data, limit, skip, direction):
  103. self.type = type
  104. self.data = data
  105. self.limit = limit
  106. self.skip = skip
  107. self.direction = direction
  108. def get_height(self):
  109. check(self.type == 1)
  110. return int.from_bytes(self.data, byteorder="big")
  111. def get_hash(self):
  112. check(self.type == 0)
  113. return self.data
  114. @staticmethod
  115. def create_for_height(height, limit, skip, direction):
  116. return GetRootBlockHeaderListWithSkipRequest(
  117. 1,
  118. height.to_bytes(32, byteorder="big"),
  119. limit,
  120. skip,
  121. direction
  122. )
  123. @staticmethod
  124. def create_for_hash(hash, limit, skip, direction):
  125. return GetRootBlockHeaderListWithSkipRequest(
  126. 0,
  127. hash,
  128. limit,
  129. skip,
  130. direction
  131. )
  132. class GetMinorBlockHeaderListWithSkipRequest(Serializable):
  133. FIELDS = [
  134. ("type", uint8), # 0 block hash, 1 block height
  135. ("data", hash256),
  136. ("branch", Branch),
  137. ("limit", uint32),
  138. ("skip", uint32),
  139. ("direction", uint8), # 0 to genesis, 1 to tip
  140. ]
  141. def __init__(self, type, data, branch, limit, skip, direction):
  142. self.type = type
  143. self.data = data
  144. self.branch = branch
  145. self.limit = limit
  146. self.skip = skip
  147. self.direction = direction
  148. def get_height(self):
  149. check(self.type == 1)
  150. return int.from_bytes(self.data, byteorder="big")
  151. def get_hash(self):
  152. check(self.type == 0)
  153. return self.data
  154. @staticmethod
  155. def create_for_height(height, branch, limit, skip, direction):
  156. return GetMinorBlockHeaderListWithSkipRequest(
  157. 1,
  158. height.to_bytes(32, byteorder="big"),
  159. branch,
  160. limit,
  161. skip,
  162. direction
  163. )
  164. @staticmethod
  165. def create_for_hash(hash, branch, limit, skip, direction):
  166. return GetMinorBlockHeaderListWithSkipRequest(
  167. 0,
  168. hash,
  169. branch,
  170. limit,
  171. skip,
  172. direction
  173. )
  174. class GetRootBlockListRequest(Serializable):
  175. """ RPC to get a root block list. The RPC should be only fired by root chain
  176. """
  177. FIELDS = [("root_block_hash_list", PrependedSizeListSerializer(4, hash256))]
  178. def __init__(self, root_block_hash_list=None):
  179. self.root_block_hash_list = (
  180. root_block_hash_list if root_block_hash_list is not None else []
  181. )
  182. class GetRootBlockListResponse(Serializable):
  183. FIELDS = [("root_block_list", PrependedSizeListSerializer(4, RootBlock))]
  184. def __init__(self, root_block_list=None):
  185. self.root_block_list = root_block_list if root_block_list is not None else []
  186. class GetMinorBlockListRequest(Serializable):
  187. """ RPCP to get a minor block list. The RPC should be only fired by a shard, and
  188. all minor blocks should be from the same shard.
  189. """
  190. FIELDS = [("minor_block_hash_list", PrependedSizeListSerializer(4, hash256))]
  191. def __init__(self, minor_block_hash_list=None):
  192. self.minor_block_hash_list = (
  193. minor_block_hash_list if minor_block_hash_list is not None else []
  194. )
  195. class GetMinorBlockListResponse(Serializable):
  196. FIELDS = [("minor_block_list", PrependedSizeListSerializer(4, MinorBlock))]
  197. def __init__(self, minor_block_list=None):
  198. self.minor_block_list = minor_block_list if minor_block_list is not None else []
  199. class GetMinorBlockHeaderListRequest(Serializable):
  200. """ Obtain block hashs in the active chain.
  201. """
  202. FIELDS = [
  203. ("block_hash", hash256),
  204. ("branch", Branch),
  205. ("limit", uint32),
  206. ("direction", uint8), # 0 to genesis, 1 to tip
  207. ]
  208. def __init__(self, block_hash, branch, limit, direction):
  209. self.block_hash = block_hash
  210. self.branch = branch
  211. self.limit = limit
  212. self.direction = direction
  213. class GetMinorBlockHeaderListResponse(Serializable):
  214. FIELDS = [
  215. ("root_tip", RootBlockHeader),
  216. ("shard_tip", MinorBlockHeader),
  217. ("block_header_list", PrependedSizeListSerializer(4, MinorBlockHeader)),
  218. ]
  219. def __init__(self, root_tip, shard_tip, block_header_list):
  220. self.root_tip = root_tip
  221. self.shard_tip = shard_tip
  222. self.block_header_list = block_header_list
  223. class NewBlockMinorCommand(Serializable):
  224. FIELDS = [("block", MinorBlock)]
  225. def __init__(self, block):
  226. self.block = block
  227. class NewRootBlockCommand(Serializable):
  228. FIELDS = [("block", RootBlock)]
  229. def __init__(self, block):
  230. self.block = block
  231. class PingPongCommand(Serializable):
  232. """
  233. with 32B message which is undefined at the moment
  234. """
  235. FIELDS = [("message", hash256)]
  236. def __init__(self, message):
  237. self.message = message
  238. class CommandOp:
  239. HELLO = 0
  240. NEW_MINOR_BLOCK_HEADER_LIST = 1
  241. NEW_TRANSACTION_LIST = 2
  242. GET_PEER_LIST_REQUEST = 3 # only handled by simple_network peers
  243. GET_PEER_LIST_RESPONSE = 4
  244. GET_ROOT_BLOCK_HEADER_LIST_REQUEST = 5
  245. GET_ROOT_BLOCK_HEADER_LIST_RESPONSE = 6
  246. GET_ROOT_BLOCK_LIST_REQUEST = 7
  247. GET_ROOT_BLOCK_LIST_RESPONSE = 8
  248. GET_MINOR_BLOCK_LIST_REQUEST = 9
  249. GET_MINOR_BLOCK_LIST_RESPONSE = 10
  250. GET_MINOR_BLOCK_HEADER_LIST_REQUEST = 11
  251. GET_MINOR_BLOCK_HEADER_LIST_RESPONSE = 12
  252. NEW_BLOCK_MINOR = 13
  253. PING = 14
  254. PONG = 15
  255. GET_ROOT_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST = 16
  256. GET_ROOT_BLOCK_HEADER_LIST_WITH_SKIP_RESPONSE = 17
  257. NEW_ROOT_BLOCK = 18
  258. GET_MINOR_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST = 19
  259. GET_MINOR_BLOCK_HEADER_LIST_WITH_SKIP_RESPONSE = 20
  260. OP_SERIALIZER_MAP = {
  261. CommandOp.HELLO: HelloCommand,
  262. CommandOp.NEW_MINOR_BLOCK_HEADER_LIST: NewMinorBlockHeaderListCommand,
  263. CommandOp.NEW_TRANSACTION_LIST: NewTransactionListCommand,
  264. CommandOp.GET_PEER_LIST_REQUEST: GetPeerListRequest,
  265. CommandOp.GET_PEER_LIST_RESPONSE: GetPeerListResponse,
  266. CommandOp.GET_ROOT_BLOCK_HEADER_LIST_REQUEST: GetRootBlockHeaderListRequest,
  267. CommandOp.GET_ROOT_BLOCK_HEADER_LIST_RESPONSE: GetRootBlockHeaderListResponse,
  268. CommandOp.GET_ROOT_BLOCK_LIST_REQUEST: GetRootBlockListRequest,
  269. CommandOp.GET_ROOT_BLOCK_LIST_RESPONSE: GetRootBlockListResponse,
  270. CommandOp.GET_MINOR_BLOCK_LIST_REQUEST: GetMinorBlockListRequest,
  271. CommandOp.GET_MINOR_BLOCK_LIST_RESPONSE: GetMinorBlockListResponse,
  272. CommandOp.GET_MINOR_BLOCK_HEADER_LIST_REQUEST: GetMinorBlockHeaderListRequest,
  273. CommandOp.GET_MINOR_BLOCK_HEADER_LIST_RESPONSE: GetMinorBlockHeaderListResponse,
  274. CommandOp.NEW_BLOCK_MINOR: NewBlockMinorCommand,
  275. CommandOp.PING: PingPongCommand,
  276. CommandOp.PONG: PingPongCommand,
  277. CommandOp.GET_ROOT_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST: GetRootBlockHeaderListWithSkipRequest,
  278. CommandOp.GET_ROOT_BLOCK_HEADER_LIST_WITH_SKIP_RESPONSE: GetRootBlockHeaderListResponse,
  279. CommandOp.NEW_ROOT_BLOCK: NewRootBlockCommand,
  280. CommandOp.GET_MINOR_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST: GetMinorBlockHeaderListWithSkipRequest,
  281. CommandOp.GET_MINOR_BLOCK_HEADER_LIST_WITH_SKIP_RESPONSE: GetMinorBlockHeaderListResponse,
  282. }