예제 #1
0
  def handleSync() -> None:
    #Handle sync requests.
    reqHash: bytes = bytes()
    bH: int = 0
    bB: int = 1
    while True:
      if bB == 3:
        break

      msg: bytes = rpc.meros.sync.recv()
      if MessageType(msg[0]) == MessageType.BlockListRequest:
        reqHash = msg[3 : 35]
        for b in range(len(blockchain.blocks)):
          if 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(blockchain.blocks[b - bl].header.hash)

              elif msg[1] == 1:
                blockList.append(blockchain.blocks[b + bl].header.hash)

              else:
                raise TestError("Meros asked for an invalid direction in a BlockListRequest.")

            if blockList == []:
              rpc.meros.dataMissing()
              break

            rpc.meros.blockList(blockList)
            break

          if b == len(blockchain.blocks):
            rpc.meros.dataMissing()

      elif MessageType(msg[0]) == MessageType.BlockHeaderRequest:
        reqHash = msg[1 : 33]
        if reqHash != blockchain.blocks[2 - bH].header.hash:
          raise TestError("Meros asked for a Block Header that didn't belong to the next Block.")

        #Send the BlockHeader.
        rpc.meros.syncBlockHeader(blockchain.blocks[2 - bH].header)
        bH += 1

      elif MessageType(msg[0]) == MessageType.BlockBodyRequest:
        reqHash = msg[1 : 33]
        if reqHash != blockchain.blocks[bB].header.hash:
          raise TestError("Meros asked for a Block Body that didn't belong to the next Block.")

        #Send the Block.
        rpc.meros.blockBody(blockchain.blocks[bB])
        bB += 1

      else:
        raise TestError("Unexpected message sent: " + msg.hex().upper())

    #Verify the Blockchain.
    verifyBlockchain(rpc, blockchain)
예제 #2
0
    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()
예제 #3
0
def TwoHundredThirtySevenTest(rpc: RPC) -> None:
    chains: Dict[str, List[Dict[str, Any]]]
    with open("e2e/Vectors/Merit/Reorganizations/TwoHundredThirtySeven.json",
              "r") as file:
        chains = json.loads(file.read())
    main: Blockchain = Blockchain.fromJSON(chains["main"])
    alt: Blockchain = Blockchain.fromJSON(chains["alt"])

    def sendBlock(toSend: Block) -> None:
        rpc.meros.liveBlockHeader(toSend.header)
        rpc.meros.handleBlockBody(toSend)

    #Send 0 through 3 of the main chain.
    rpc.meros.liveConnect(main.blocks[0].header.hash)
    rpc.meros.syncConnect(main.blocks[0].header.hash)
    sendBlock(main.blocks[1])
    sendBlock(main.blocks[2])
    sendBlock(main.blocks[3])

    #Send the alt chain, which won't have enough work to trigger a reorganization.
    rpc.meros.liveBlockHeader(alt.blocks[2].header)
    if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest:
        raise TestError(
            "Meros didn't request for the Block List for this alternate chain."
        )
    rpc.meros.blockList([alt.blocks[0].header.hash])
    if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockHeaderRequest:
        raise TestError(
            "Meros didn't request a BlockHeader in this alternate chain.")
    rpc.meros.syncBlockHeader(alt.blocks[1].header)

    #Verify Meros can sync the final Block.
    sendBlock(main.blocks[4])
        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())
예제 #5
0
def VUnknownSignedTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
    chain: Blockchain = Blockchain.fromJSON(json.loads(file.read()))
    file.close()

    #Send a single block so we have a miner.
    rpc.meros.liveConnect(chain.blocks[0].header.hash)
    rpc.meros.syncConnect(chain.blocks[0].header.hash)
    header: bytes = rpc.meros.liveBlockHeader(chain.blocks[1].header)
    if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest:
        raise TestError("Meros didn't ask for the body.")
    rpc.meros.blockBody(chain.blocks[1])
    if rpc.meros.live.recv() != header:
        raise TestError("Meros didn't broadcast the header.")

    #Create a valid Data.
    #Uneccessary at this time, but good preparation for the future.
    privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
    data: Data = Data(bytes(32), privKey.get_verifying_key().to_bytes())
    data.sign(privKey)
    data.beat(SpamFilter(5))

    #Sign the Data.
    verif: SignedVerification = SignedVerification(data.hash)
    verif.sign(0, PrivateKey(0))

    #Run twice. The first shouldn't send the Transaction. The second should.
    for i in range(2):
        rpc.meros.signedElement(verif)
        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.TransactionRequest:
            raise TestError("Meros didn't request the transaction.")

        if i == 0:
            #When we send DataMissing, we should be disconnected within a few seconds.
            rpc.meros.dataMissing()
            start: int = int(time())
            try:
                rpc.meros.sync.recv()
            except Exception:
                #More than a few seconds is allowed as Meros's own SyncRequest must timeout.
                if int(time()) - start > 10:
                    raise TestError(
                        "Meros didn't disconnect us for sending a Verification of a non-existent Transaction."
                    )
            #Clear our invalid connections.
            rpc.meros.live.connection.close()
            rpc.meros.sync.connection.close()
            sleep(65)
            #Init new ones.
            rpc.meros.liveConnect(chain.blocks[0].header.hash)
            rpc.meros.syncConnect(chain.blocks[0].header.hash)

        else:
            rpc.meros.syncTransaction(data)
            sleep(2)
            if not rpc.call("consensus", "getStatus",
                            [data.hash.hex()])["verifiers"]:
                raise TestError("Meros didn't add the Verification.")
예제 #6
0
  def sendBlock() -> None:
    #Send the Block with the MeritRemoval archived again.
    block: Block = Block.fromJSON(vectors[-1])
    rpc.meros.liveBlockHeader(block.header)

    #Flag of if the Block's Body synced.
    blockBodySynced: bool = False

    #Handle sync requests.
    reqHash: bytes = bytes()
    while True:
      if blockBodySynced:
        #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("Meros didn't add the same MeritRemoval twice.")
        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.
        blockBodySynced = True
        rpc.meros.blockBody(block)

      else:
        raise TestError("Unexpected message sent: " + msg.hex().upper())
예제 #7
0
def MissingOneSketchTest(meros: Meros) -> None:
    vectors: Dict[str, Any]
    with open("e2e/Vectors/Merit/Sketches/MissingOne.json", "r") as file:
        vectors = json.loads(file.read())

    blockchain: Blockchain = Blockchain.fromJSON(vectors["blockchain"])

    meros.liveConnect(blockchain.blocks[0].header.hash)
    meros.syncConnect(blockchain.blocks[0].header.hash)

    header: bytes = meros.liveBlockHeader(blockchain.blocks[1].header)
    meros.handleBlockBody(blockchain.blocks[1])
    if meros.live.recv() != header:
        raise TestError(
            "Meros didn't broadcast a BlockHeader for a Block it just added.")

    for data in vectors["datas"]:
        if meros.liveTransaction(Data.fromJSON(data)) != meros.live.recv():
            raise TestError("Meros didn't broadcast back a Data Transaction.")

    for verif in vectors["verifications"]:
        if meros.signedElement(
                SignedVerification.fromSignedJSON(verif)) != meros.live.recv():
            raise TestError("Meros didn't broadcast back a Verification.")

    header = meros.liveBlockHeader(blockchain.blocks[2].header)
    meros.handleBlockBody(blockchain.blocks[2], 1)

    if MessageType(meros.sync.recv()[0]) != MessageType.SketchHashRequests:
        raise TestError("Meros didn't request the packet it's missing.")
    meros.packet(VerificationPacket.fromJSON(vectors["packet"]))

    if meros.live.recv() != header:
        raise TestError(
            "Meros didn't broadcast a BlockHeader for a Block it just added.")
예제 #8
0
  def test() -> None:
    #Data.
    dataHash: str = rpc.call("personal", "data", {"data": "abc"})
    #Read the initial Data.
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.Data:
      raise TestError("Meros didn't broadcast the initial Data.")
    #Read the actual Data.
    serializedData: bytes = rpc.meros.live.recv()
    if serializedData[1:] != Data.fromJSON(rpc.call("transactions", "getTransaction", {"hash": dataHash})).serialize():
      raise TestError("Meros didn't broadcast the created Data.")
    res: Any = rpc.call("network", "broadcast", {"transaction": dataHash})
    if not (isinstance(res, bool) and res):
      raise TestError("Broadcast didn't return true.")
    if rpc.meros.live.recv() != serializedData:
      raise TestError("Meros didn't broadcast a Transaction when told to.")

    #Block.
    header: BlockHeader = Block.fromJSON(vectors["blockchain"][0]).header
    rpc.call("network", "broadcast", {"block": header.hash.hex()})
    if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()):
      raise TestError("Meros didn't broadcast the Blockheader.")

    #Data and Block.
    rpc.call("network", "broadcast", {"block": header.hash.hex(), "transaction": dataHash})
    if rpc.meros.live.recv() != serializedData:
      raise TestError("Meros didn't broadcast a Transaction when told to.")
    if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()):
      raise TestError("Meros didn't broadcast the Blockheader.")

    #Non-existent Transaction.
    try:
      rpc.call("network", "broadcast", {"transaction": bytes(32).hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-2 Transaction not found.":
        raise TestError("Meros didn't error when told to broadcast a non-existent Transaction.")

    #Non-existent Block.
    try:
      rpc.call("network", "broadcast", {"block": bytes(32).hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-2 Block not found.":
        raise TestError("Meros didn't error when told to broadcast a non-existent Block.")

    #Mint.
    try:
      rpc.call("network", "broadcast", {"transaction": mint.hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-3 Transaction is a Mint.":
        raise TestError("Meros didn't error when told to broadcast a Mint.")

    #Genesis Block.
    try:
      rpc.call("network", "broadcast", {"block": Blockchain().blocks[0].header.hash.hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-3 Block is the genesis Block.":
        raise TestError("Meros didn't error when told to broadcast the genesis Block.")
예제 #9
0
        def sendMeritRemoval() -> None:
            #Send the Datas.
            for data in datas:
                if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
                    raise TestError("Meros didn't send us the Data.")

            #Send the Block containing the modified Merit Removal.
            block: Block = Block.fromJSON(vectors["blockchains"][i][-1])
            rpc.meros.liveBlockHeader(block.header)

            #Flag of if the Block's Body synced.
            blockBodySynced: bool = False

            #Handle sync requests.
            reqHash: bytes = bytes()
            while True:
                if blockBodySynced:
                    #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 height is 2.
                        #The genesis Block and the Block granting Merit.
                        try:
                            if rpc.call("merit", "getHeight") != 2:
                                raise Exception()
                        except Exception:
                            raise TestError(
                                "Node added a Block containg a repeat MeritRemoval."
                            )

                        #Since the node didn't add the Block, raise SuccessError.
                        raise SuccessError(
                            "Node didn't add a Block containing a repeat MeritRemoval."
                        )
                    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.
                    blockBodySynced = True
                    rpc.meros.blockBody(block)

                else:
                    raise TestError("Unexpected message sent: " +
                                    msg.hex().upper())
예제 #10
0
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."
        )
예제 #11
0
파일: TGUReorgTest.py 프로젝트: Vyryn/Meros
def reorg(
  rpc: RPC,
  alt: Blockchain
) -> None:
  #Sync up the new Blockchain.
  header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header)
  #Use the genesis as the default value for this outer defined variable.
  lastBlock: Block = alt.blocks[0]
  while True:
    req: bytes = rpc.meros.sync.recv()
    if MessageType(req[0]) == MessageType.BlockListRequest:
      blockList: List[bytes] = []
      for block in alt.blocks:
        if block.header.hash == req[2:]:
          break
        blockList.append(block.header.hash)
      blockList = blockList[-req[1]:]
      blockList.reverse()
      rpc.meros.blockList(blockList)

    elif MessageType(req[0]) == MessageType.BlockHeaderRequest:
      reqHash: bytes = req[1:]
      for block in alt.blocks:
        if reqHash == block.header.hash:
          rpc.meros.syncBlockHeader(block.header)
          break

    elif MessageType(req[0]) == MessageType.BlockBodyRequest:
      reqHash: bytes = req[1:-4]
      for block in alt.blocks:
        if reqHash == block.header.hash:
          lastBlock = block
          rpc.meros.rawBlockBody(block, 5)
          break

    elif MessageType(req[0]) == MessageType.SketchHashRequests:
      rpc.meros.packet(lastBlock.body.packets[0])

      #If we've sent the last BlockBody, and its packets, we've synced the chain.
      if lastBlock.header.hash == alt.blocks[-1].header.hash:
        break

  if header != rpc.meros.live.recv():
    raise TestError("Meros didn't broadcast back the alt chain's header.")
예제 #12
0
def verify(rpc: RPC, txHash: bytes, nick: int = 0, mr: bool = False) -> None:
    sv: SignedVerification = SignedVerification(txHash)
    sv.sign(nick, PrivateKey(nick))
    temp: bytes = rpc.meros.signedElement(sv)
    if mr:
        if MessageType(
                rpc.meros.live.recv()[0]) != MessageType.SignedMeritRemoval:
            raise TestError("Meros didn't create a MeritRemoval.")
    elif temp != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Verification.")
예제 #13
0
    def sendAlternateTip() -> None:
        rpc.meros.liveBlockHeader(alt.blocks[-1].header)

        req: bytes = rpc.meros.sync.recv()
        if MessageType(req[0]) != MessageType.BlockListRequest:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders.")
        if req[-32:] != alt.blocks[-2].header.hash:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders for THIS header."
            )

        blockList: List[bytes] = []
        b: int = len(alt.blocks) - 3
        while b != -1:
            blockList.append(alt.blocks[b].header.hash)
            b -= 1
        rpc.meros.blockList(blockList)

        diff = -14
        while diff != -1:
            if rpc.meros.sync.recv() != (
                    MessageType.BlockHeaderRequest.toByte() +
                    alt.blocks[diff].header.hash):
                raise TestError("Meros didn't request a previous BlockHeader.")
            rpc.meros.syncBlockHeader(alt.blocks[diff].header)
            diff += 1

        #Meros will now attempt the re-org, having verified the work.
        #Break the chain early via a data missing.
        diff = -14
        while diff != 0:
            if diff == -2:
                if rpc.meros.sync.recv()[:-4] != (
                        MessageType.BlockBodyRequest.toByte() +
                        alt.blocks[diff].header.hash):
                    raise TestError(
                        "Meros didn't request a previous BlockBody.")
                rpc.meros.dataMissing()
                sleep(35)
                for socket in [rpc.meros.live, rpc.meros.sync]:
                    socket.connection.close()
                #We could just edit the condition above, yet this keeps parity with the other reorg tests.
                break
            else:
                rpc.meros.handleBlockBody(alt.blocks[diff])
            diff += 1

        #Verify Meros at least went back to the fork point.
        #Ideally, it'd go back to the original chain.
        #Or if we synced enough blocks where we still have a chain with more work, we should remain on it.
        verifyBlockchain(rpc, forkPoint)
        raise SuccessError("Meros reverted back to the fork point.")
예제 #14
0
def VarIntTest(
  meros: Meros
) -> None:
  #Solely used to get the genesis Block hash.
  blockchain: Blockchain = Blockchain()

  #Handshake with the node using a pointless VarInt for the protocol/network/services.
  live: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  live.connect(("127.0.0.1", meros.tcp))
  live.send(
    MessageType.Handshake.toByte() +
    int("1000000000000000", 2).to_bytes(2, "big") +
    int("11111111100000001000000000000000", 2).to_bytes(4, "big") +
    int("100000101001101000111010", 2).to_bytes(3, "big") +
    b'\0\0' +
    blockchain.blocks[0].header.hash
  )

  sync: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  sync.connect(("127.0.0.1", meros.tcp))
  sync.send(
    MessageType.Syncing.toByte() +
    int("1000000000000000", 2).to_bytes(2, "big") +
    int("11111111100000001000000000000000", 2).to_bytes(4, "big") +
    int("100000101001101000111010", 2).to_bytes(3, "big") +
    b'\0\0' +
    blockchain.blocks[0].header.hash
  )

  #Verify Meros considers us valid by receiving its initial handshake, and then waiting for the next one.
  if (
    (MessageType(live.recv(38)[0]) != MessageType.Handshake) or
    (MessageType(sync.recv(38)[0]) != MessageType.Syncing)
  ):
    raise TestError("Meros didn't handshake with us.")
  if MessageType(live.recv(38)[0]) != MessageType.Handshake:
    raise TestError("Meros didn't handshake with us to stop us from timing out.")
예제 #15
0
    def sendAlternateTip() -> None:
        header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header)

        req: bytes = rpc.meros.sync.recv()
        if MessageType(req[0]) != MessageType.BlockListRequest:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders.")
        if req[3:35] != alt.blocks[-1].header.hash:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders for THIS header."
            )

        blockList: List[bytes] = []
        b: int = len(alt.blocks) - 2
        while b != -1:
            blockList.append(alt.blocks[b].header.hash)
            b -= 1
        rpc.meros.blockList(blockList)

        diff = -2
        while diff != -1:
            req = rpc.meros.sync.recv()
            if req != (MessageType.BlockHeaderRequest.toByte() +
                       alt.blocks[diff].header.hash):
                raise TestError("Meros didn't request a previous BlockHeader.")
            rpc.meros.syncBlockHeader(alt.blocks[diff].header)
            diff += 1

        diff = -2
        while diff != 0:
            req = rpc.meros.sync.recv()
            if req != (MessageType.BlockBodyRequest.toByte() +
                       alt.blocks[diff].header.hash):
                raise TestError("Meros didn't request a previous BlockBody.")
            rpc.meros.blockBody(alt.blocks[diff])
            diff += 1

        if rpc.meros.live.recv() != header:
            raise TestError("Meros didn't send back the BlockHeader.")

        #Verify the alternate Blockchain.
        verifyBlockchain(rpc, alt)

        #Raise SuccessError so the Liver doesn't fail when verifying the original chain.
        raise SuccessError("Meros re-organized to the alternate chain.")
def TwoHundredSeventyThreeTest(meros: Meros) -> None:
    blockchain: Blockchain = Blockchain()

    vectors: Dict[str, Any]
    with open("e2e/Vectors/Merit/TwoHundredSeventyThree.json", "r") as file:
        vectors = json.loads(file.read())
    header: BlockHeader = BlockHeader.fromJSON(vectors)

    meros.liveConnect(blockchain.last())
    meros.syncConnect(blockchain.last())

    #Sanity check on the behavior of select.
    readable, _, _ = select([meros.live.connection, meros.sync.connection], [],
                            [], 65)
    if len(readable) != 1:
        raise Exception(
            "Misuse of select; multiple sockets reported readable.")
    if MessageType(meros.live.recv()[0]) != MessageType.Handshake:
        raise Exception(
            "Misuse of select; it didn't return the live socket trying to Handshake. Keep-alives could also be broken."
        )
    meros.live.send(MessageType.BlockchainTail.toByte() + blockchain.last())

    #Send the header.
    meros.liveBlockHeader(header)

    #Meros should disconnect us immediately. If it doesn't, it'll either send a keep-alive or a BlockBodyRequest.
    #One is inefficient as it doesn't properly protect against spam attacks.
    #One is invalid completely.
    readable, _, _ = select([meros.live.connection, meros.sync.connection], [],
                            [], 65)
    #On Linux, both sockets immediately appear as readable.
    #That is why we iterate, instead of just checking length == 0.
    for s in readable:
        try:
            temp: str = s.recv(1)
            if len(temp) != 0:
                raise TestError(
                    "Meros tried to send us something instead of immediately disconnecting us."
                )
        except TestError as e:
            raise e
        except Exception:
            pass
예제 #17
0
  def send16AndInvalidAlt() -> None:
    #Send the Block after the fork from the main chain.
    header: bytes = rpc.meros.liveBlockHeader(main.blocks[16].header)
    req: bytes = rpc.meros.sync.recv()
    if req != (MessageType.BlockBodyRequest.toByte() + main.blocks[16].header.hash):
      raise TestError("Meros didn't request the BlockBody for this Block from the main chain.")
    rpc.meros.blockBody(main.blocks[16])
    if rpc.meros.live.recv() != header:
      raise TestError("Meros didn't send back the BlockHeader.")

    #Send the headers of the alt chain to trigger a re-org.
    header = rpc.meros.liveBlockHeader(alt.blocks[-1].header)
    req = rpc.meros.sync.recv()
    if MessageType(req[0]) != MessageType.BlockListRequest:
      raise TestError("Meros didn't request the list of previous BlockHeaders.")
    if req[3 : 35] != alt.blocks[-2].header.hash:
      raise TestError("Meros didn't request the list of previous BlockHeaders for THIS header.")

    blockList: List[bytes] = []
    b: int = len(alt.blocks) - 3
    while b != -1:
      blockList.append(alt.blocks[b].header.hash)
      b -= 1
    rpc.meros.blockList(blockList)

    diff = -4
    while diff != -1:
      req = rpc.meros.sync.recv()
      if req != (MessageType.BlockHeaderRequest.toByte() + alt.blocks[diff].header.hash):
        raise TestError("Meros didn't request a previous BlockHeader.")
      rpc.meros.syncBlockHeader(alt.blocks[diff].header)
      diff += 1

    #Meros should now revert and attempt the re-org.
    #Disconnect to make sure it fails.
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()

    #Sleep long enough to get counted as disconnected and removed from the list of connections.
    sleep(35)

    #Reconnect.
    rpc.meros.live = MerosSocket(rpc.meros.tcp, 254, 254, True, main.blocks[15].header.hash)
    rpc.meros.sync = MerosSocket(rpc.meros.tcp, 254, 254, False, main.blocks[15].header.hash)
예제 #18
0
  def sendMeritRemoval() -> None:
    #Send and verify the MeritRemoval.
    removalBytes: bytes = rpc.meros.signedElement(removal)

    sent: int = 0
    while True:
      if sent == 2:
        break

      msg: bytes = rpc.meros.sync.recv()
      if MessageType(msg[0]) == MessageType.TransactionRequest:
        rpc.meros.syncTransaction(transactions.txs[msg[1 : 33]])
        sent += 1
      else:
        raise TestError("Unexpected message sent: " + msg.hex().upper())

    if removalBytes != rpc.meros.live.recv():
      raise TestError("Meros didn't send us the Merit Removal.")
    verifyMeritRemoval(rpc, 1, 1, removal.holder, True)
예제 #19
0
def TElementTest(rpc: RPC) -> None:
    merit: Merit = Merit()

    blsPrivKey: PrivateKey = PrivateKey(0)
    blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex()

    #Handshake with the node.
    rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

    #Send the first Block.
    block: Block
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        block = Block.fromJSON(json.loads(file.read())[0])
    merit.blockchain.add(block)
    rpc.meros.liveBlockHeader(block.header)
    rpc.meros.handleBlockBody(block)
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
        raise TestError(
            "Meros didn't broadcast the Block Header it just added.")

    #Create and transmit a DataDifficulty.
    dataDiff: SignedDataDifficulty = SignedDataDifficulty(0, 0, 0)
    dataDiff.sign(0, blsPrivKey)
    rpc.meros.signedElement(dataDiff)
    sleep(1.5)

    #Verify the block template has the DataDifficulty.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": blsPubKey})
    template["header"] = bytes.fromhex(template["header"])
    if template["header"][36:68] != BlockHeader.createContents([], [dataDiff]):
        raise TestError("Block template doesn't have the Data Difficulty.")

    #Mine the Block.
    block = Block(
        BlockHeader(
            0,
            block.header.hash,
            BlockHeader.createContents([], [dataDiff]),
            0,
            template["header"][-43:-39],
            BlockHeader.createSketchCheck(template["header"][-43:-39], []),
            0,
            int.from_bytes(template["header"][-4:], byteorder="little"),
        ), BlockBody([], [dataDiff], dataDiff.signature))
    if block.header.serializeHash()[:-4] != template["header"]:
        raise TestError("Failed to recreate the header.")

    block.mine(blsPrivKey, merit.blockchain.difficulty())
    merit.blockchain.add(block)

    #Publish it.
    rpc.call(
        "merit", "publishBlock", {
            "id":
            template["id"],
            "header": (template["header"] +
                       block.header.proof.to_bytes(4, byteorder="little") +
                       block.header.signature).hex()
        })

    #Create and transmit a new DataDifficulty.
    dataDiff = SignedDataDifficulty(3, 0, 0)
    dataDiff.sign(0, blsPrivKey)
    rpc.meros.signedElement(dataDiff)
    sleep(1.5)

    #Verify the block template has a MeritRemoval.
    #Thanks to implicit Merit Removals, this just means it has the new difficulty.
    template = rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey})
    template["header"] = bytes.fromhex(template["header"])
    if template["header"][36:68] != BlockHeader.createContents([], [dataDiff]):
        raise TestError("Block template doesn't have the Merit Removal.")

    #Mine the Block.
    block = Block(
        BlockHeader(
            0, block.header.hash, BlockHeader.createContents([], [dataDiff]),
            0, template["header"][-43:-39],
            BlockHeader.createSketchCheck(template["header"][-43:-39], []), 0,
            int.from_bytes(template["header"][-4:], byteorder="little")),
        BlockBody([], [dataDiff], dataDiff.signature))
    if block.header.serializeHash()[:-4] != template["header"]:
        raise TestError("Failed to recreate the header.")

    block.mine(blsPrivKey, merit.blockchain.difficulty())
    merit.blockchain.add(block)

    rpc.call(
        "merit", "publishBlock", {
            "id":
            template["id"],
            "header": (template["header"] +
                       block.header.proof.to_bytes(4, byteorder="little") +
                       block.header.signature).hex()
        })

    verifyBlockchain(rpc, merit.blockchain)
예제 #20
0
    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()
예제 #21
0
def TwoHundredThirtyFiveTest(rpc: RPC) -> None:
    blockchain: Blockchain = Blockchain()
    dataFilter: SpamFilter = SpamFilter(5)

    edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
    edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

    #Mine one Block to the node.
    blsPrivKey: PrivateKey = PrivateKey(
        bytes.fromhex(rpc.call("personal", "getMiner")))
    blsPubKey: bytes = blsPrivKey.toPublicKey().serialize()

    #Call getBlockTemplate just to get an ID.
    #Skips the need to write a sync loop for the BlockBody.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        [blsPubKey.hex()])

    #Mine a Block.
    block = Block(
        BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1,
                    bytes(4), bytes(32), blsPubKey,
                    blockchain.blocks[0].header.time + 1200, 0), BlockBody())
    block.mine(blsPrivKey, blockchain.difficulty())
    blockchain.add(block)

    rpc.call("merit", "publishBlock",
             [template["id"], block.serialize().hex()])

    #Send Meros a Data and receive its Verification to make sure it's verifying Transactions in the first place.
    data: Data = Data(bytes(32), edPubKey.to_bytes())
    data.sign(edPrivKey)
    data.beat(dataFilter)

    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back the Data.")
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't send us its SignedVerification.")

    #Close our connection and mine 8 Blocks so its Merit is locked.
    rpc.meros.live.connection.close()
    for _ in range(8):
        block = Block(
            BlockHeader(0, blockchain.blocks[-1].header.hash, bytes(32), 1,
                        bytes(4), bytes(32), 0,
                        blockchain.blocks[-1].header.time + 1200, 0),
            BlockBody())
        #Reusing its key is fine as mining doesn't count as participation.
        block.mine(blsPrivKey, blockchain.difficulty())
        blockchain.add(block)

    #Sleep 30 seconds to make sure Meros noted we disconnected, and then reconnect.
    sleep(30)
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)

    #Sync the Blocks.
    for b in range(8):
        header: bytes = rpc.meros.liveBlockHeader(blockchain.blocks[b +
                                                                    2].header)
        if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() +
                                     blockchain.blocks[b + 2].header.hash):
            raise TestError("Meros didn't request the BlockBody.")
        rpc.meros.blockBody(blockchain.blocks[b + 2])
        if rpc.meros.live.recv() != header:
            raise TestError("Meros didn't send back the header.")
        if MessageType(
                rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
            raise TestError("Meros didn't verify this Block's data.")

    #Verify its Merit is locked.
    #Theoretically, all code after this check is unecessary.
    #Meros verifies a Block's Data after updating its State.
    #Therefore, if the above last Block had its Data verified, this issue should be closed.
    #That said, the timing is a bit too tight for comfort.
    #Better safe than sorry. Hence why the code after this check exists.
    if rpc.call("merit", "getMerit", [0])["status"] != "Locked":
        raise TestError("Merit wasn't locked when it was supposed to be.")

    #Send it a Transaction and make sure Meros verifies it, despite having its Merit locked.
    data = Data(data.hash, edPubKey.to_bytes())
    data.sign(edPrivKey)
    data.beat(dataFilter)

    if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back the Data.")
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't send us its SignedVerification.")
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()
예제 #23
0
def EightyEightTest(
  rpc: RPC
) -> None:
  edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
  edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

  blsPrivKey: PrivateKey = PrivateKey(0)
  blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex()

  file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
  blocks: List[Dict[str, Any]] = json.loads(file.read())
  file.close()

  merit: Merit = Merit()
  dataFilter: SpamFilter = SpamFilter(5)

  #Handshake with the node.
  rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
  rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

  #Send the first Block.
  block: Block = Block.fromJSON(blocks[0])
  merit.blockchain.add(block)
  rpc.meros.liveBlockHeader(block.header)

  #Handle sync requests.
  reqHash: bytes = bytes()
  while True:
    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)
      break

    else:
      raise TestError("Unexpected message sent: " + msg.hex().upper())

  if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
    raise TestError("Meros didn't broadcast the Block Header it just added.")

  #Create two Datas.
  datas: List[Data] = [Data(bytes(32), edPubKey.to_bytes())]
  datas.append(Data(datas[0].hash, b"Hello there! General Kenobi."))

  for data in datas:
    #Sign them and have them beat the spam filter.
    data.sign(edPrivKey)
    data.beat(dataFilter)

    #Transmit them.
    rpc.meros.liveTransaction(data)

  #Verify both.
  verifs: List[SignedVerification] = [
    SignedVerification(datas[0].hash),
    SignedVerification(datas[1].hash)
  ]
  for verif in verifs:
    verif.sign(0, blsPrivKey)

  #Only transmit the second.
  rpc.meros.signedElement(verifs[1])
  sleep(0.5)

  #Verify the block template has no verifications.
  if bytes.fromhex(
    rpc.call("merit", "getBlockTemplate", [blsPubKey])["header"]
  )[36 : 68] != bytes(32):
    raise TestError("Block template has Verification Packets.")

  #Transmit the first signed verification.
  rpc.meros.signedElement(verifs[0])
  sleep(0.5)

  #Verify the block template has both verifications.
  template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey])
  template["header"] = bytes.fromhex(template["header"])
  packets: List[VerificationPacket] = [VerificationPacket(datas[0].hash, [0]), VerificationPacket(datas[1].hash, [0])]
  if template["header"][36 : 68] != BlockHeader.createContents(packets):
    raise TestError("Block template doesn't have both Verification Packets.")

  #Mine the Block.
  block = Block(
    BlockHeader(
      0,
      block.header.hash,
      BlockHeader.createContents(packets),
      1,
      template["header"][-43 : -39],
      BlockHeader.createSketchCheck(template["header"][-43 : -39], packets),
      0,
      int.from_bytes(template["header"][-4:], byteorder="little")
    ),
    BlockBody(
      packets,
      [],
      Signature.aggregate([verifs[0].signature, verifs[1].signature])
    )
  )
  if block.header.serializeHash()[:-4] != template["header"]:
    raise TestError("Failed to recreate the header.")
  if block.body.serialize(
    block.header.sketchSalt,
    len(packets)
  ) != bytes.fromhex(template["body"]):
    raise TestError("Failed to recreate the body.")

  block.mine(blsPrivKey, merit.blockchain.difficulty())
  merit.blockchain.add(block)

  rpc.call(
    "merit",
    "publishBlock",
    [
      template["id"],
      (
        template["header"] +
        block.header.proof.to_bytes(4, byteorder="little") +
        block.header.signature +
        block.body.serialize(block.header.sketchSalt, len(packets))
      ).hex()
    ]
  )

  verifyBlockchain(rpc, merit.blockchain)
예제 #24
0
def HundredFiftyFiveTest(rpc: RPC) -> None:
    edPrivKeys: List[ed25519.SigningKey] = [
        ed25519.SigningKey(b'\0' * 32),
        ed25519.SigningKey(b'\1' * 32)
    ]
    edPubKeys: List[ed25519.VerifyingKey] = [
        edPrivKeys[0].get_verifying_key(), edPrivKeys[1].get_verifying_key()
    ]

    blsPrivKey: PrivateKey = PrivateKey(
        bytes.fromhex(rpc.call("personal", "getMiner")))
    blsPubKey: bytes = blsPrivKey.toPublicKey().serialize()

    blockchain: Blockchain = Blockchain()
    dataFilter: SpamFilter = SpamFilter(5)

    #Handshake with the node.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)

    #Call getBlockTemplate just to get an ID.
    #Skips the need to write a sync loop for the BlockBody.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        [blsPubKey.hex()])

    #Mine a Block.
    block = Block(
        BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1,
                    bytes(4), bytes(32), blsPubKey,
                    blockchain.blocks[0].header.time + 1200, 0), BlockBody())
    block.mine(blsPrivKey, blockchain.difficulty())
    blockchain.add(block)

    rpc.call("merit", "publishBlock",
             [template["id"], block.serialize().hex()])

    if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
        raise TestError("Meros didn't broadcast the Block we just published.")
    #Ignore the Verification for the Block's Data.
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError(
            "Meros didn't send the SignedVerification for the Block's Data.")

    datas: List[Data] = [
        Data(bytes(32), edPubKeys[0].to_bytes()),
        Data(bytes(32), edPubKeys[1].to_bytes())
    ]

    for d in range(len(datas)):
        datas[d].sign(edPrivKeys[d])
        datas[d].beat(dataFilter)

        #Send the Data and verify Meros sends it back.
        if rpc.meros.liveTransaction(datas[d]) != rpc.meros.live.recv():
            raise TestError("Meros didn't send back the Data.")

        #Verify Meros sends back a Verification.
        res: bytes = rpc.meros.live.recv()
        if MessageType(res[0]) != MessageType.SignedVerification:
            raise TestError("Meros didn't send a SignedVerification.")

        verif: SignedVerification = SignedVerification(datas[d].hash)
        verif.sign(0, blsPrivKey)
        if res[1:] != verif.signedSerialize():
            raise TestError(
                "Meros didn't send the correct SignedVerification.")
예제 #25
0
    def sendAlternateTip() -> None:
        rpc.meros.liveBlockHeader(alt.blocks[-1].header)

        req: bytes = rpc.meros.sync.recv()
        if MessageType(req[0]) != MessageType.BlockListRequest:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders.")
        if req[3:35] != alt.blocks[-2].header.hash:
            raise TestError(
                "Meros didn't request the list of previous BlockHeaders for THIS header."
            )

        blockList: List[bytes] = []
        b: int = len(alt.blocks) - 3
        while b != (len(alt.blocks) - 35):
            blockList.append(alt.blocks[b].header.hash)
            b -= 1
        rpc.meros.blockList(blockList)

        diff = -20
        while diff != -1:
            req = rpc.meros.sync.recv()
            if req != (MessageType.BlockHeaderRequest.toByte() +
                       alt.blocks[diff].header.hash):
                raise TestError("Meros didn't request a previous BlockHeader.")
            rpc.meros.syncBlockHeader(alt.blocks[diff].header)
            diff += 1

        #Advance the chain far enough to switch to the new key.
        diff = -20
        while diff != -11:
            req = rpc.meros.sync.recv()
            if req != (MessageType.BlockBodyRequest.toByte() +
                       alt.blocks[diff].header.hash):
                raise TestError("Meros didn't request a previous BlockBody.")
            rpc.meros.blockBody(alt.blocks[diff])
            diff += 1

        #Cause the reorganization to fail.
        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest:
            raise TestError("Meros didn't request a BlockBody.")
        rpc.meros.dataMissing()

        sleep(65)
        rpc.meros.liveConnect(main.blocks[0].header.hash)
        rpc.meros.syncConnect(main.blocks[0].header.hash)

        #Sync back the regular chain.
        rpc.meros.liveBlockHeader(main.blocks[400].header)
        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest:
            raise TestError("Meros didn't request the Block list.")
        blockList = []
        b = 398
        while b != 380:
            blockList.append(main.blocks[b].header.hash)
            b -= 1
        rpc.meros.blockList(blockList)

        for b in range(391, 400):
            if rpc.meros.sync.recv() != (
                    MessageType.BlockHeaderRequest.toByte() +
                    main.blocks[b].header.hash):
                raise TestError("Meros didn't request the BlockHeader.")
            rpc.meros.syncBlockHeader(main.blocks[b].header)

        for b in range(391, 401):
            if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte()
                                         + main.blocks[b].header.hash):
                raise TestError("Meros didn't request the BlockBody.")
            rpc.meros.blockBody(main.blocks[b])
예제 #26
0
def BusyTest(meros: Meros) -> None:
    #Solely used to get the genesis Block hash.
    blockchain: Blockchain = Blockchain()

    #Handshake with the node.
    meros.syncConnect(blockchain.blocks[0].header.hash)

    #Create two new server sockets.
    def createServerSocket() -> socket.socket:
        result: socket.socket = socket.socket(socket.AF_INET,
                                              socket.SOCK_STREAM)
        result.bind(("127.0.0.1", 0))
        result.listen(2)
        return result

    busyServer: socket.socket = createServerSocket()
    server: socket.socket = createServerSocket()

    #Receive Syncing until Meros asks for peers.
    while True:
        res = meros.sync.recv()
        if MessageType(res[0]) == MessageType.Syncing:
            meros.sync.send(MessageType.BlockchainTail.toByte() +
                            blockchain.blocks[0].header.hash)
        elif MessageType(res[0]) == MessageType.PeersRequest:
            break

    #Craft a Peers message of our own server.
    meros.sync.send(MessageType.Peers.toByte() + bytes.fromhex("017F000001") +
                    busyServer.getsockname()[1].to_bytes(2, "little"))

    #Use select to obtain a non-blocking accept.
    busy: int = 0
    buf: bytes
    for _ in select.select([busyServer], [], [], 5000):
        #Accept a new connection.
        client, _ = busyServer.accept()

        #Verify Meros's Handshake.
        buf = client.recv(38)
        if MessageType(
                buf[0]) not in {MessageType.Handshake, MessageType.Syncing}:
            busyServer.close()
            raise TestError(
                "Meros didn't start its connection with a Handshake.")

        if buf[1:] != (
            (254).to_bytes(1, "little") + (254).to_bytes(1, "little") +
            (128).to_bytes(1, "little") + meros.tcp.to_bytes(2, "little") +
                blockchain.blocks[0].header.hash):
            busyServer.close()
            raise TestError("Meros had an invalid Handshake.")

        #Send back Busy.
        client.send(MessageType.Busy.toByte() + bytes.fromhex("017F000001") +
                    server.getsockname()[1].to_bytes(2, "little"))

        busy += 1
        if busy == 2:
            busyServer.close()
            break

    #Make sure Meros connects to the server we redirected to.
    with raises(SuccessError):
        for _ in select.select([server], [], [], 5000):
            #Accept a new connection.
            client, _ = server.accept()

            #Verify Meros's Handshake.
            buf = client.recv(38)
            if MessageType(buf[0]) not in {
                    MessageType.Handshake, MessageType.Syncing
            }:
                server.close()
                raise TestError(
                    "Meros didn't start its connection with a Handshake.")

            if buf[1:] != (
                (254).to_bytes(1, "little") + (254).to_bytes(1, "little") +
                (128).to_bytes(1, "little") + meros.tcp.to_bytes(2, "little") +
                    blockchain.blocks[0].header.hash):
                server.close()
                raise TestError("Meros had an invalid Handshake.")

            server.close()
            raise SuccessError(
                "Meros connected to the server we redirected it to with a Busy message."
            )

        #Raise a TestError.
        busyServer.close()
        server.close()
        raise TestError("Meros didn't connect to the redirected server.")
예제 #27
0
def TwoHundredThirtyTwoTest(
  rpc: RPC
) -> None:
  chains: Dict[str, List[Dict[str, Any]]]
  with open("e2e/Vectors/Merit/Reorganizations/TwoHundredThirtyTwo.json", "r") as file:
    chains = json.loads(file.read())
  main: Blockchain = Blockchain.fromJSON(chains["main"])
  alt: Blockchain = Blockchain.fromJSON(chains["alt"])

  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])

  #Make the initial connection and sync the main chain.
  rpc.meros.liveConnect(main.blocks[0].header.hash)
  rpc.meros.syncConnect(main.blocks[0].header.hash)
  sendBlock(main.blocks[1])
  sendBlock(main.blocks[2])

  #Trigger the reorganization to the alternate chain.
  #We only want the revert aspect of this.
  rpc.meros.liveBlockHeader(alt.blocks[3].header)
  if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest:
    raise TestError("Meros didn't ask for the Block List of the alternate chain.")
  rpc.meros.blockList([alt.blocks[1].header.hash])
  if rpc.meros.sync.recv() != (MessageType.BlockHeaderRequest.toByte() + alt.blocks[2].header.hash):
    raise TestError("Meros didn't ask for the other BlockHeader in this alternate chain.")
  rpc.meros.syncBlockHeader(alt.blocks[2].header)

  #Cause the re-organization to fail.
  rpc.meros.live.connection.close()
  rpc.meros.sync.connection.close()
  rpc.socket.close()
  sleep(35)

  #Reboot the node to reload the database.
  rpc.meros.quit()
  rpc.meros.calledQuit = False

  rpc.meros.process = Popen(["./build/Meros", "--data-dir", rpc.meros.dataDir, "--log-file", rpc.meros.log, "--db", rpc.meros.db, "--network", "devnet", "--tcp-port", str(rpc.meros.tcp), "--rpc-port", str(rpc.meros.rpc), "--no-gui"])
  while True:
    try:
      connection: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      connection.connect(("127.0.0.1", rpc.meros.rpc))
      connection.shutdown(socket.SHUT_RDWR)
      connection.close()
      break
    except ConnectionRefusedError:
      sleep(1)

  rpc.meros.liveConnect(main.blocks[0].header.hash)
  rpc.meros.syncConnect(main.blocks[0].header.hash)
  sendBlock(main.blocks[2])

  rpc.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  rpc.socket.connect(("127.0.0.1", rpc.meros.rpc))
  verifyBlockchain(rpc, main)
예제 #28
0
def TElementTest(
  rpc: RPC
) -> None:
  file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
  blocks: List[Dict[str, Any]] = json.loads(file.read())
  file.close()
  merit: Merit = Merit()

  blsPrivKey: PrivateKey = PrivateKey(0)
  blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex()

  #Handshake with the node.
  rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
  rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

  #Send the first Block.
  block: Block = Block.fromJSON(blocks[0])
  merit.blockchain.add(block)
  rpc.meros.liveBlockHeader(block.header)

  #Handle sync requests.
  reqHash: bytes = bytes()
  while True:
    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.")

      rpc.meros.blockBody(block)
      break

    else:
      raise TestError("Unexpected message sent: " + msg.hex().upper())

  if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
    raise TestError("Meros didn't broadcast the Block Header it just added.")

  #Create and transmit a DataDifficulty.
  dataDiff: SignedDataDifficulty = SignedDataDifficulty(0, 0, 0)
  dataDiff.sign(0, blsPrivKey)
  rpc.meros.signedElement(dataDiff)
  sleep(0.5)

  #Verify the block template has the DataDifficulty.
  template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey])
  template["header"] = bytes.fromhex(template["header"])
  if template["header"][36 : 68] != BlockHeader.createContents([], [dataDiff]):
    raise TestError("Block template doesn't have the Data Difficulty.")

  #Mine the Block.
  block = Block(
    BlockHeader(
      0,
      block.header.hash,
      BlockHeader.createContents([], [dataDiff]),
      1,
      template["header"][-43 : -39],
      BlockHeader.createSketchCheck(template["header"][-43 : -39], []),
      0,
      int.from_bytes(template["header"][-4:], byteorder="little"),
    ),
    BlockBody([], [dataDiff], dataDiff.signature)
  )
  if block.header.serializeHash()[:-4] != template["header"]:
    raise TestError("Failed to recreate the header.")
  if block.body.serialize(block.header.sketchSalt) != bytes.fromhex(template["body"]):
    raise TestError("Failed to recreate the body.")

  block.mine(blsPrivKey, merit.blockchain.difficulty())
  merit.blockchain.add(block)

  #Publish it.
  rpc.call(
    "merit",
    "publishBlock",
    [
      template["id"],
      (
        template["header"] +
        block.header.proof.to_bytes(4, byteorder="little") +
        block.header.signature +
        block.body.serialize(block.header.sketchSalt)
      ).hex()
    ]
  )

  #Create and transmit a new DataDifficulty.
  dataDiff = SignedDataDifficulty(3, 1, 0)
  dataDiff.sign(0, blsPrivKey)
  rpc.meros.signedElement(dataDiff)
  sleep(0.5)

  #Verify the block template has the DataDifficulty.
  template = rpc.call("merit", "getBlockTemplate", [blsPubKey])
  template["header"] = bytes.fromhex(template["header"])
  if template["header"][36 : 68] != BlockHeader.createContents([], [dataDiff]):
    raise TestError("Block template doesn't have the new Data Difficulty.")

  #Create and transmit a new DataDifficulty reusing an existing nonce.
  signatures: List[Signature] = [dataDiff.signature]
  dataDiff = SignedDataDifficulty(4, 1, 0)
  dataDiff.sign(0, blsPrivKey)
  signatures.append(dataDiff.signature)
  rpc.meros.signedElement(dataDiff)
  sleep(0.5)

  #Verify the block template has a MeritRemoval.
  mr: MeritRemoval = MeritRemoval(
    SignedDataDifficulty(3, 1, 0),
    SignedDataDifficulty(4, 1, 0),
    False
  )
  template = rpc.call("merit", "getBlockTemplate", [blsPubKey])
  template["header"] = bytes.fromhex(template["header"])
  if template["header"][36 : 68] != BlockHeader.createContents([], [mr]):
    raise TestError("Block template doesn't have the Merit Removal.")

  #Mine the Block.
  block = Block(
    BlockHeader(
      0,
      block.header.hash,
      BlockHeader.createContents([], [mr]),
      1,
      template["header"][-43 : -39],
      BlockHeader.createSketchCheck(template["header"][-43 : -39], []),
      0,
      int.from_bytes(template["header"][-4:], byteorder="little")
    ),
    BlockBody([], [mr], Signature.aggregate(signatures))
  )
  if block.header.serializeHash()[:-4] != template["header"]:
    raise TestError("Failed to recreate the header.")
  if block.body.serialize(block.header.sketchSalt) != bytes.fromhex(template["body"]):
    raise TestError("Failed to recreate the body.")

  block.mine(blsPrivKey, merit.blockchain.difficulty())
  merit.blockchain.add(block)

  rpc.call(
    "merit",
    "publishBlock",
    [
      template["id"],
      (
        template["header"] +
        block.header.proof.to_bytes(4, byteorder="little") +
        block.header.signature +
        block.body.serialize(block.header.sketchSalt)
      ).hex()
    ]
  )

  verifyBlockchain(rpc, merit.blockchain)
예제 #29
0
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()
예제 #30
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())