Beispiel #1
0
    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)
Beispiel #2
0
    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
Beispiel #3
0
    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())
Beispiel #5
0
    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()
Beispiel #7
0
    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()
Beispiel #8
0
    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()