def serialize( self, sketchSalt: bytes, capacityArg: int = -1 ) -> bytes: capacity: int = capacityArg if capacity == -1: capacity = len(self.packets) // 5 + 1 if len(self.packets) != 0 else 0 sketch: Sketch = Sketch(capacity) for packet in self.packets: sketch.add(sketchSalt, packet) result: bytes = ( self.packetsContents + capacity.to_bytes(4, "big") + sketch.serialize() + len(self.elements).to_bytes(4, "big") ) for elem in self.elements: result += elem.prefix + elem.serialize() result += self.aggregate.serialize() return result
def sendDataAndBlock() -> None: #Send the Data. if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) != MessageType.SketchHashRequests: raise TestError("Meros didn't request the packets for this Block.") packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError("Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) try: if MessageType( rpc.meros.live.recv()[0]) == MessageType.BlockHeader: raise TestError("Meros added the Block.") except Exception as e: if str(e) != "Meros added the Block.": raise SuccessError()
def checkFail() -> None: #Grab the Block. #pylint: disable=cell-var-from-loop block: Block = merit.blockchain.blocks[12] #Send the Block. rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) #Handle sync requests. while True: msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.SketchHashRequests: if msg[1:33] != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range( int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash: bytes = msg[1:33] #pylint: disable=cell-var-from-loop if reqHash not in transactions.txs: raise TestError( "Meros asked for a non-existent Transaction.") #pylint: disable=cell-var-from-loop rpc.meros.syncTransaction(transactions.txs[reqHash]) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: raise SuccessError( "Node disconnected us after we sent an invalid Transaction." ) except Exception: raise TestError("Meros sent a keep-alive.") else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def TwoHundredFourTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/TwoHundredFour.json", "r") as file: vectors = json.loads(file.read()) #Instantiate a Blockchain to set the RandomX key. chain: Blockchain = Blockchain() blank: Block = Block.fromJSON(vectors["blank"]) if len(vectors["blocks"]) != 1: raise Exception( "Misread this test's vectors, creating an invalid test due to that." ) block: Block = Block.fromJSON(vectors["blocks"][0]) rpc.meros.liveConnect(chain.last()) rpc.meros.syncConnect(chain.last()) #Send a blank block so Meros acknowledges a holder. sentHeader: bytes = rpc.meros.liveBlockHeader(blank.header) rpc.meros.handleBlockBody(blank) if rpc.meros.live.recv() != sentHeader: raise TestError( "Meros didn't rebroadcast the header for a blank Block.") rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) != MessageType.SketchHashRequests: raise TestError("Unexpected message sent: " + msg.hex().upper()) if msg[1:33] != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) if int.from_bytes(msg[33:37], byteorder="little") != 1: raise TestError("Meros didn't ask for one VerificationPacket.") if int.from_bytes(msg[37:45], byteorder="little") != Sketch.hash( block.header.sketchSalt, block.body.packets[0]): raise TestError("Meros didn't ask for the VerificationPacket.") rpc.meros.packet(block.body.packets[0]) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. sleep(1) if rpc.meros.process.poll() is not None: raise TestError( "Node crashed trying to handle a VerificationPacket with no holders." ) except Exception: raise TestError( "Meros didn't disconnect us after sending a VerificationPacket with no holders; it also didn't crash." )
def serialize(self, sketchSalt: bytes, capacity: int) -> bytes: sketch: Sketch = Sketch(capacity) for packet in self.packets: sketch.add(sketchSalt, packet) result: bytes = (self.packetsContents + capacity.to_bytes(4, "little") + sketch.serialize() + len(self.elements).to_bytes(4, "little")) for elem in self.elements: result += elem.prefix + elem.serialize() result += self.aggregate.serialize() return result
def sendBlock( toSend: Block ) -> None: rpc.meros.liveBlockHeader(toSend.header) rpc.meros.handleBlockBody(toSend) if toSend.body.packets: if rpc.meros.sync.recv() != ( MessageType.SketchHashRequests.toByte() + toSend.header.hash + (1).to_bytes(4, byteorder="little") + Sketch.hash(toSend.header.sketchSalt, toSend.body.packets[0]).to_bytes(8, byteorder="little") ): raise TestError("Meros didn't ask for this BlockBody's VerificationPacket.") rpc.meros.packet(toSend.body.packets[0])
def createSketchCheck(salt: bytes = bytes(4), packets: List[VerificationPacket] = []) -> bytes: sketchHashes: List[int] = [] for packet in packets: sketchHashes.append(Sketch.hash(salt, packet)) sketchHashes.sort() #Hash each sketch hash to leaf length. leaves: List[bytes] = [] for sketchHash in sketchHashes: leaves.append( blake2b(sketchHash.to_bytes(8, byteorder="little"), digest_size=32).digest()) return merkle(leaves)
def PinsketchTest() -> None: for _ in range(100): capacity: int = getrandbits(7) + 1 sketches: List[Sketch] = [Sketch(capacity) for _ in range(2)] differences: List[int] = [] for _ in range(capacity + getrandbits(7)): sketches[0].hashes.append(getrandbits(64)) MinisketchLib.minisketch_add_uint64( sketches[0].sketch, c_uint64(sketches[0].hashes[-1])) if (getrandbits(2) == 0) and (len(differences) < capacity): differences.append(sketches[0].hashes[-1]) else: sketches[1].hashes.append(sketches[0].hashes[-1]) MinisketchLib.minisketch_add_uint64( sketches[1].sketch, c_uint64(sketches[1].hashes[-1])) differences = sorted(differences) miniSerialized: List[bytes] = [ sketch.serialize() for sketch in sketches ] pythonSerialized: List[bytes] = [ encodeSketch(sketch.hashes, capacity) for sketch in sketches ] for i in range(len(miniSerialized)): if miniSerialized[i] != pythonSerialized[i]: raise TestError( "Pure Python Pinsketch encoded the sketch to a different serialization than Minisketch." ) sketches[1].merge(miniSerialized[0]) mergedArr: bytearray = bytearray() for i in range(len(pythonSerialized[0])): mergedArr.append(pythonSerialized[0][i] ^ pythonSerialized[1][i]) merged: bytes = bytes(mergedArr) if merged != sketches[1].serialize(): raise TestError( "The merged Python sketch serialization is different than the Minisketch serialization of its merged Sketch." ) if differences != sketches[1].decode(): raise TestError("Minisketch didn't decode the differences.") if differences != decodeSketch(merged): raise TestError( "Pure Python Pinsketch didn't decode the differences.")
def checkFail() -> None: #This Block should cause the node to disconnect us AFTER it attempts to sync our Transaction. syncedTX: bool = False #Grab the Block. block: Block = merit.blockchain.blocks[2] #Send the Block. rpc.meros.liveBlockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: if syncedTX: #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: raise SuccessError( "Node disconnected us after we sent a parsable, yet invalid, Verification." ) except Exception: raise TestError("Meros sent a keep-alive.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the Block we just sent it." ) #Send the BlockBody. rpc.meros.blockBody(block) elif MessageType(msg[0]) == MessageType.SketchHashesRequest: if not block.body.packets: raise TestError( "Meros asked for Sketch Hashes from a Block without any." ) reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for Sketch Hashes that didn't belong to the Block we just sent it." ) #Create the haashes. hashes: List[int] = [] for packet in block.body.packets: hashes.append(Sketch.hash(block.header.sketchSalt, packet)) #Send the Sketch Hashes. rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="big")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="big") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) elif MessageType(msg[0]) == MessageType.TransactionRequest: rpc.meros.dataMissing() syncedTX = True else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def TwoHundredFourTest( rpc: RPC ) -> None: file: IO[Any] = open("e2e/Vectors/Consensus/TwoHundredFour.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Instantiate a Blockchain to set the RandomX key. chain: Blockchain = Blockchain() blank: Block = Block.fromJSON(vectors["blank"]) for blockJSON in vectors["blocks"]: block: Block = Block.fromJSON(blockJSON) rpc.meros.liveConnect(chain.last()) rpc.meros.syncConnect(chain.last()) #Send a blank block so Meros acknowledges a holder. sentHeader: bytes = rpc.meros.liveBlockHeader(blank.header) if rpc.meros.sync.recv() != MessageType.BlockBodyRequest.toByte() + blank.header.hash: raise TestError("Meros didn't request the body for a blank Block.") rpc.meros.blockBody(blank) if rpc.meros.live.recv() != sentHeader: raise TestError("Meros didn't rebroadcast the header for a blank Block.") with raises(SuccessError): rpc.meros.liveBlockHeader(block.header) sentProblem: bool = False while True: if sentProblem: #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. sleep(1) if rpc.meros.process.poll() is not None: raise TestError("Node crashed trying to handle a VerificationPacket with no holders.") raise SuccessError("Node disconnected us after we sent a VerificationPacket with no holders.") except Exception: raise TestError("Meros didn't disconnect us after sending a VerificationPacket with no holders; it also didn't crash.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: if msg[1 : 33] != block.header.hash: raise TestError("Meros asked for a Block Body that didn't belong to the Block we just sent it.") rpc.meros.blockBody(block) #If this Block has no packets, we've already sent the problem. sentProblem = not block.body.packets elif MessageType(msg[0]) == MessageType.SketchHashRequests: if msg[1 : 33] != block.header.hash: raise TestError("Meros asked for Verification Packets that didn't belong to the Block we just sent it.") if int.from_bytes(msg[33 : 37], byteorder="little") != 1: raise TestError("Meros didn't ask for one VerificationPacket.") if int.from_bytes(msg[37 : 45], byteorder="little") != Sketch.hash(block.header.sketchSalt, block.body.packets[0]): raise TestError("Meros didn't ask for the VerificationPacket.") rpc.meros.packet(block.body.packets[0]) sentProblem = True else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node for the next test case. rpc.reset()
def verifyBeaten() -> None: #Verify beaten was set. The fourth Transaction is also beaten, yet should be pruned. #That's why we don't check its status. for send in sends[1:3]: if not rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["beaten"]: raise TestError( "Meros didn't mark a child and its descendant as beaten.") #Check the pending Verification for the beaten descendant was deleted. if ((rpc.call("consensus", "getStatus", {"hash": sends[2].hash.hex()})["verifiers"] != [0]) or (bytes.fromhex( rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey})["header"])[36:68] != bytes(32))): raise TestError("Block template still has the Verification.") #Verify the fourth Transaction was pruned. with raises(TestError): rpc.call("transactions", "getTransaction", {"hash": sends[3].hash.hex()}) #Verify neither the second or third Transaction tree can be appended to. #Publishes a never seen-before Send for the descendant. #Re-broadcasts the pruned Transaction for the parent. for send in sends[3:]: #Most of these tests use a socket connection for this. #This has identical effects, returns an actual error instead of a disconnect, #and doesn't force us to wait a minute for our old socket to be cleared. with raises(TestError): rpc.call("transactions", "publishTransaction", { "type": "Send", "transaction": send.serialize().hex() }) #Not loaded above as it can only be loqaded after the chain starts, which is done by the Liver. #RandomX cache keys and all that. blockWBeatenVerif: Block = Block.fromJSON( vectors["blockWithBeatenVerification"]) #The following code used to test behavior which was removed, in order to be more forgiving for nodes a tad behind. #Verify we can't add that SignedVerification now. #rpc.meros.signedElement(verif) #try: # rpc.meros.live.recv() # #Hijacks a random Exception type for our purposes. # raise MessageException("Meros didn't disconnect us after we sent a Verification for a beaten Transaction.") #except TestError: # pass #except MessageException as e: # raise TestError(e.message) #sleep(65) #rpc.meros.liveConnect(blockWBeatenVerif.header.last) #Verify we can't add a Block containing that Verification. rpc.meros.liveBlockHeader(blockWBeatenVerif.header) #BlockBody sync request. rpc.meros.handleBlockBody(blockWBeatenVerif) #Sketch hash sync request. hashReqs: bytes = rpc.meros.sync.recv()[37:] for h in range(0, len(hashReqs), 8): for packet in blockWBeatenVerif.body.packets: if int.from_bytes(hashReqs[h:h + 8], byteorder="little") == Sketch.hash( blockWBeatenVerif.header.sketchSalt, packet): rpc.meros.packet(packet) break try: rpc.meros.live.recv() raise MessageException( "Meros didn't disconnect us after we sent a Block containing a Verification of a beaten Transaction." ) except TestError: pass except MessageException as e: raise TestError(e.message) sleep(65) rpc.meros.liveConnect(blockWBeatenVerif.header.last) rpc.meros.syncConnect(blockWBeatenVerif.header.last)
def sync( self ) -> None: #Handshake with the node. self.rpc.meros.syncConnect(self.merit.blockchain.blocks[self.settings["height"]].header.hash) #Handle sync requests. reqHash: bytes = bytes() while True: #Break out of the for loop if the sync finished. #This means we sent every Block, every Element, every Transaction... if ( (self.blockHashes == set()) and (self.packets == {}) and (self.txs == set()) ): break msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[3 : 35] for b in range(len(self.merit.blockchain.blocks)): if self.merit.blockchain.blocks[b].header.hash == reqHash: blockList: List[bytes] = [] for bl in range(1, msg[2] + 2): if msg[1] == 0: if b - bl < 0: break blockList.append(self.merit.blockchain.blocks[b - bl].header.hash) if b - bl != 0: self.blocks.append(self.merit.blockchain.blocks[b - bl]) elif msg[1] == 1: if b + bl > self.settings["height"]: break blockList.append(self.merit.blockchain.blocks[b + bl].header.hash) self.blocks.append(self.merit.blockchain.blocks[b + bl]) else: raise TestError("Meros asked for an invalid direction in a BlockListRequest.") if blockList == []: self.rpc.meros.dataMissing() break self.rpc.meros.blockList(blockList) break if b == self.settings["height"]: self.rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: if (self.txs != set()) or (self.packets != {}): raise TestError("Meros asked for a new Block before syncing the last Block's Transactions and Packets.") reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for a BlockHeader other than the next Block's on the last BlockList.") self.rpc.meros.syncBlockHeader(self.blocks[-1].header) elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for a BlockBody other than the next Block's on the last BlockList.") self.rpc.meros.blockBody(self.blocks[-1]) self.blockHashes.remove(self.blocks[-1].header.hash) #Set packets/transactions. self.packets = {} for packet in self.blocks[-1].body.packets: if packet.hash not in self.synced: self.txs.add(packet.hash) self.packets[Sketch.hash(self.blocks[-1].header.sketchSalt, packet)] = packet #Update the list of mentioned Transactions. noVCMRs: bool = True for elem in self.blocks[-1].body.elements: if isinstance(elem, MeritRemoval): if isinstance(elem.e1, (Verification, VerificationPacket)): self.txs.add(elem.e1.hash) noVCMRs = False if isinstance(elem.e2, (Verification, VerificationPacket)): self.txs.add(elem.e2.hash) noVCMRs = False if (self.packets == {}) and noVCMRs: del self.blocks[-1] elif MessageType(msg[0]) == MessageType.SketchHashesRequest: reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for Sketch Hashes that didn't belong to the header we just sent it.") #Get the haashes. hashes: List[int] = list(self.packets) #Send the Sketch Hashes. self.rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: if not self.packets: raise TestError("Meros asked for Verification Packets from a Block without any.") reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for Verification Packets that didn't belong to the Block we just sent it.") #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33 : 37], byteorder="big")): sketchHash: int = int.from_bytes(msg[37 + (h * 8) : 45 + (h * 8)], byteorder="big") if sketchHash not in self.packets: raise TestError("Meros asked for a non-existent Sketch Hash.") self.rpc.meros.packet(self.packets[sketchHash]) del self.packets[sketchHash] elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1 : 33] if self.transactions is None: raise TestError("Meros asked for a Transaction when we have none.") if reqHash not in self.transactions.txs: raise TestError("Meros asked for a Transaction we don't have.") if reqHash not in self.txs: raise TestError("Meros asked for a Transaction we haven't mentioned.") self.rpc.meros.syncTransaction(self.transactions.txs[reqHash]) self.synced.add(reqHash) self.txs.remove(reqHash) if self.txs == set(): del self.blocks[-1] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) if self.settings["playback"]: #Playback their messages. self.rpc.meros.sync.playback()
def sync(self) -> None: self.rpc.meros.syncConnect( self.merit.blockchain.blocks[self.settings["height"]].header.hash) reqHash: bytes = bytes() while True: #Break out of the for loop if the sync finished. #This means we sent every Block, every Element, every Transaction... if ((self.blockHashes == set()) and (self.packets == {}) and (self.txs == set())): break msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[-32:] for b in range(len(self.merit.blockchain.blocks)): if self.merit.blockchain.blocks[b].header.hash == reqHash: if self.blocks[-1].header.hash != reqHash: self.blocks.append(self.merit.blockchain.blocks[b]) blockList: List[bytes] = [] for bl in range(1, msg[1] + 2): if b - bl < 0: break blockList.append( self.merit.blockchain.blocks[b - bl].header.hash) if b - bl != 0: self.blocks.append( self.merit.blockchain.blocks[b - bl]) if blockList == []: self.rpc.meros.dataMissing() break self.rpc.meros.blockList(blockList) break if b == self.settings["height"]: self.rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:33] if (self.txs != set()) or (self.packets != {}): raise TestError( "Meros asked for a new Block before syncing the last Block's Transactions and Packets." ) if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for a BlockHeader other than the next Block's on the last BlockList." ) self.rpc.meros.syncBlockHeader(self.blocks[-1].header) elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for a BlockBody other than the next Block's on the last BlockList." ) if int.from_bytes(msg[33:37], "little") != len( self.blocks[-1].body.packets): raise Exception( "Meros didn't request 100% of packets in the BlockBody when syncing." ) self.rpc.meros.rawBlockBody(self.blocks[-1], len(self.blocks[-1].body.packets)) self.blockHashes.remove(self.blocks[-1].header.hash) #Set the packets/transactions which should be synced. self.packets = {} for packet in self.blocks[-1].body.packets: if not ((packet.hash in self.rpc.meros.sentTXs) or (packet.hash == (Data( self.merit.blockchain.genesis, self.blocks[-1].header.last).hash))): self.txs.add(packet.hash) self.packets[Sketch.hash(self.blocks[-1].header.sketchSalt, packet)] = packet #Update the list of mentioned Transactions. noVCMRs: bool = True if (self.packets == {}) and noVCMRs: del self.blocks[-1] elif MessageType(msg[0]) == MessageType.SketchHashRequests: reqHash = msg[1:33] if not self.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in self.packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") self.rpc.meros.packet(self.packets[sketchHash]) del self.packets[sketchHash] if (self.packets == {}) and (self.txs == set()): del self.blocks[-1] elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if self.transactions is None: raise TestError( "Meros asked for a Transaction when we have none.") if reqHash not in self.transactions.txs: raise TestError( "Meros asked for a Transaction we don't have.") if reqHash not in self.txs: raise TestError( "Meros asked for a Transaction we haven't mentioned.") self.rpc.meros.syncTransaction(self.transactions.txs[reqHash]) self.txs.remove(reqHash) if (self.packets == {}) and (self.txs == set()): del self.blocks[-1] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) #Playback their messages. #Verifies Meros can respond as it can receive. if self.settings["playback"]: self.rpc.meros.sync.playback() #Reset the node. self.rpc.reset()
def HundredSixBlockElementsTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/HundredSix/BlockElements.json", "r") as file: vectors = json.loads(file.read()) #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) blocks: List[Block] = [] for block in vectors["blocks"]: blocks.append(Block.fromJSON(block)) for block in blocks: #Handshake with the node. rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Send the Block. rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) #Flag of if the Block's Body synced. doneSyncing: bool = len(block.body.packets) == 0 #Handle sync requests. reqHash: bytes = bytes() while True: if doneSyncing: #Sleep for a second so Meros handles the Block. sleep(1) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. try: if rpc.call("merit", "getHeight") != 1: raise Exception() except Exception: raise TestError( "Node crashed after being sent a malformed Element." ) #Since the node didn't crash, break out of this loop to trigger the next test case. break except Exception: raise TestError("Meros sent a keep-alive.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.SketchHashRequests: if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) doneSyncing = True elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if reqHash not in transactions.txs: raise TestError( "Meros asked for a non-existent Transaction.") rpc.meros.syncTransaction(transactions.txs[reqHash]) else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node so we can test the next invalid Block. rpc.reset()
from e2e.Classes.Transactions.Data import Data from e2e.Classes.Transactions.Transactions import Transactions from e2e.Classes.Consensus.VerificationPacket import VerificationPacket from e2e.Vectors.Generation.PrototypeChain import PrototypeChain edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key() proto: PrototypeChain = PrototypeChain(1, keepUnlocked=False) datas: List[Data] = [Data(bytes(32), edPubKey)] counter: int = 0 datas.append(Data(datas[0].hash, counter.to_bytes(4, byteorder="little"))) while (Sketch.hash(bytes(4), VerificationPacket(datas[0].hash, [0])) <= Sketch.hash(bytes(4), VerificationPacket(datas[1].hash, [0]))): counter += 1 datas[1] = Data(datas[0].hash, counter.to_bytes(4, byteorder="little")) proto.add(packets=[ VerificationPacket(datas[1].hash, [0]), VerificationPacket(datas[0].hash, [0]) ]) transactions: Transactions = Transactions() for data in datas: data.sign(edPrivKey) transactions.add(data) with open("e2e/Vectors/Merit/OutOfOrder/Packets.json", "w") as vectors:
def live(self, ignorePackets: List[bytes] = []) -> None: #Handshake with the node. self.rpc.meros.liveConnect(self.merit.blockchain.blocks[0].header.hash) self.rpc.meros.syncConnect(self.merit.blockchain.blocks[0].header.hash) #Send each Block. for b in range(1, len(self.merit.blockchain.blocks)): block: Block = self.merit.blockchain.blocks[b] #Set loop variables with pending data. pendingBody: bool = True pendingPackets: List[bytes] = [] pendingTXs: List[bytes] = [] for packet in block.body.packets: if packet.hash in ignorePackets: continue pendingPackets.append(packet.hash) #Don't include sent Transactions or independently created Block Data. if not ((packet.hash in self.rpc.meros.sentTXs) or (packet.hash == (Data(self.merit.blockchain.genesis, block.header.last).hash))): pendingTXs.append(packet.hash) for elem in block.body.elements: if isinstance(elem, MeritRemoval): if (isinstance(elem.e1, (Verification, VerificationPacket)) and (elem.e1.hash not in self.rpc.meros.sentTXs) and (elem.e1.hash not in pendingTXs)): pendingTXs.append(elem.e1.hash) if (isinstance(elem.e2, (Verification, VerificationPacket)) and (elem.e2.hash not in self.rpc.meros.sentTXs) and (elem.e2.hash not in pendingTXs)): pendingTXs.append(elem.e2.hash) self.rpc.meros.liveBlockHeader(block.header) reqHash: bytes = bytes() while True: #If we sent every bit of data, break. if ((not pendingBody) and (not pendingPackets) and (not pendingTXs)): break #Receive the next message. msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if not pendingBody: raise TestError( "Meros asked for the same Block Body multiple times." ) if reqHash != block.header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the Block we just sent it." ) self.rpc.meros.blockBody(block) pendingBody = False elif MessageType(msg[0]) == MessageType.SketchHashesRequest: reqHash = msg[1:33] if not block.body.packets: raise TestError( "Meros asked for Sketch Hashes from a Block without any." ) if reqHash != block.header.hash: raise TestError( "Meros asked for Sketch Hashes that didn't belong to the Block we just sent it." ) #Create the haashes. hashes: List[int] = [] for packet in block.body.packets: hashes.append( Sketch.hash(block.header.sketchSalt, packet)) self.rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: reqHash = msg[1:33] if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) if reqHash != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range( int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") self.rpc.meros.packet(packets[sketchHash]) #Delete the VerificationPacket from pending. del pendingPackets[pendingPackets.index( packets[sketchHash].hash)] #Make sure Meros asked for every packet. if pendingPackets: raise TestError( "Meros didn't ask for every Verification Packet.") elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if self.transactions is None: raise TestError( "Meros asked for a Transaction when we have none.") if reqHash not in pendingTXs: raise TestError( "Meros asked for a non-existent Transaction, a Transaction part of a different Block, or an already sent Transaction." ) self.rpc.meros.syncTransaction( self.transactions.txs[reqHash]) #Delete the Transaction from pending. del pendingTXs[pendingTXs.index(reqHash)] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Receive the BlockHeader from Meros. if MessageType( self.rpc.meros.live.recv()[0]) != MessageType.BlockHeader: raise TestError("Meros didn't broadcast the new BlockHeader.") #Add any new nicks to the lookup table. if self.merit.blockchain.blocks[b].header.newMiner: self.merit.state.nicks.append( self.merit.blockchain.blocks[b].header.minerKey) #If there's a callback at this height, call it. if b in self.callbacks: self.callbacks[b]() #Execute the every-Block callback, if it exists. if self.everyBlock is not None: self.everyBlock(b) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) #Reset the node. self.rpc.reset()
def MatchesHeaderQuantityTest(meros: Meros) -> None: #Create an instance of Merit to make sure the RandomX VM key was set. Merit() blocks: List[Block] txs: List[Data] = [] verifs: List[SignedVerification] = [] with open( "e2e/Vectors/Merit/TwoHundredSeventyFour/MatchesHeaderQuantity.json", "r") as file: vectors: Dict[str, Any] = json.loads(file.read()) blocks = [Block.fromJSON(block) for block in vectors["blocks"]] txs = [Data.fromJSON(tx) for tx in vectors["transactions"]] verifs = [ SignedVerification.fromSignedJSON(verif) for verif in vectors["verifications"] ] #Connect. meros.liveConnect(blocks[0].header.last) meros.syncConnect(blocks[0].header.last) #Send a single Block to earn Merit. meros.liveBlockHeader(blocks[0].header) meros.handleBlockBody(blocks[0]) #Send the header. meros.liveBlockHeader(blocks[1].header) #Fail Sketch Resolution, and send a different amount of sketch hashes. meros.handleBlockBody(blocks[1], 0) if MessageType(meros.sync.recv()[0]) != MessageType.SketchHashesRequest: raise TestError( "Meros didn't request the hashes after failing sketch resolution.") #Send a quantity of sketch hashes that doesn't match the header. meros.sketchHashes([ Sketch.hash(blocks[1].header.sketchSalt, VerificationPacket(tx.hash, [0])) for tx in txs ]) try: if len(meros.sync.recv()) == 0: raise TestError() raise Exception() except TestError: pass except Exception: raise TestError("Meros tried to further sync an invalid Block Body.") #Sleep so we can reconnect. sleep(65) #Repeat setup. meros.liveConnect(blocks[0].header.last) meros.syncConnect(blocks[0].header.last) #Send two Transactions. for i in range(2): meros.liveTransaction(txs[i]) meros.signedElement(verifs[i]) #Send the header and a large enough sketch to cause resolution. meros.liveBlockHeader(blocks[1].header) meros.handleBlockBody(blocks[1], 3) #Should now have been disconnected thanks to having 5 hashes. try: if len(meros.sync.recv()) == 0: raise TestError() raise Exception() except TestError: pass except Exception: raise TestError("Meros tried to further sync an invalid Block Body.")