def createSketchCheck(salt: bytes = bytes(4), packets: List[VerificationPacket] = []) -> bytes: #Create sketch hashes for every packet. sketchHashes: List[int] = [] for packet in packets: sketchHashes.append(Sketch.hash(salt, packet)) #Sort the Sketch Hashes. sketchHashes.sort(reverse=True) #Hash each sketch hash to leaf length. leaves: List[bytes] = [] for sketchHash in sketchHashes: leaves.append( blake2b(sketchHash.to_bytes(8, byteorder="big"), digest_size=32).digest()) #Return the Merkle hash. return merkle(leaves)
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 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 checkFail() -> None: #This Block should cause the node to disconnect us AFTER it syncs our Transaction. syncedTX: bool = False #Grab the Block. block: Block = merit.blockchain.blocks[-1] #Send the Block. rpc.meros.blockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: try: msg: bytes = rpc.meros.recv() except TestError: if syncedTX: raise SuccessError("Node disconnected us after we sent an invalid Transaction.") raise TestError("Node errored before syncing our Transaction.") if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif 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(merit.state.nicks, 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: reqHash = msg[1 : 33] if reqHash not in transactions.txs: raise TestError("Meros asked for a non-existent Transaction.") rpc.meros.transaction(transactions.txs[reqHash]) syncedTX = True elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: #Raise a TestError if the Block was added. raise TestError("Meros synced a Transaction which competed with a finalized Transaction.") else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def live(self) -> None: #Handshake with the node. self.rpc.meros.connect(254, 254, self.merit.blockchain.blocks[0].header.hash) #Send each Block. for b in range(1, len(self.merit.blockchain.blocks)): #Grab the Block. block: Block = self.merit.blockchain.blocks[b] #Send the Block. self.rpc.meros.blockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: msg: bytes = self.rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: self.rpc.meros.syncingAcknowledged() elif 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. self.rpc.meros.blockBody(self.merit.state.nicks, 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. self.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.") self.rpc.meros.packet(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 non-existent Transaction.") self.rpc.meros.transaction(self.transactions.txs[reqHash]) elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #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 HundredSixBlockElementsTest(rpc: RPC) -> None: #Load the vectors. file: IO[Any] = open( "PythonTests/Vectors/Consensus/HundredSix/BlockElements.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Transactions. transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) #Parse the Blocks from the vectors. blocks: List[Block] = [] for block in vectors["blocks"]: blocks.append(Block.fromJSON({}, block)) for block in blocks: #Handshake with the node. rpc.meros.connect(254, 254, blockchain.blocks[0].header.hash) #Send the Block. rpc.meros.blockHeader(block.header) #Flag of if the Block's Body synced. blockBodySynced: bool = False #Handle sync requests. reqHash: bytes = bytes() while True: try: msg: bytes = rpc.meros.recv() except TestError: if not blockBodySynced: raise TestError( "Node disconnected us before syncing the body.") #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 if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif 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. blockBodySynced = True 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: reqHash = msg[1:33] if reqHash not in transactions.txs: raise TestError( "Meros asked for a non-existent Transaction.") rpc.meros.transaction(transactions.txs[reqHash]) elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: #Raise a TestError if the Block was added. raise TestError("Meros synced a Block with an invalid holder.") else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node. rpc.reset()
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 live(self) -> 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)): #Grab the Block. 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: pendingPackets.append(packet.hash) if packet.hash not in self.rpc.meros.sentTXs: pendingTXs.append(packet.hash) for elem in block.body.elements: if isinstance(elem, MeritRemoval): #pylint: disable=consider-merging-isinstance if ((isinstance(elem.e1, Verification) or isinstance(elem.e1, 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) or isinstance(elem.e2, VerificationPacket)) and (elem.e2.hash not in self.rpc.meros.sentTXs) and (elem.e2.hash not in pendingTXs)): pendingTXs.append(elem.e2.hash) #Send the Block. self.rpc.meros.liveBlockHeader(block.header) #Handle sync requests. 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: #If we already sent the body, raise. if not pendingBody: raise TestError( "Meros asked for the same Block Body multiple times." ) #Verify Meros asked for the right Block Body. 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. self.rpc.meros.blockBody(block) #Mark the body as sent. pendingBody = False 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. self.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.") 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()