Exemple #1
0
  def test() -> None:
    recipient: Ristretto.SigningKey = Ristretto.SigningKey(b'\1' * 32)
    recipientPub: bytes = recipient.get_verifying_key()
    address: str = bech32_encode("mr", convertbits(bytes([0]) + recipientPub, 8, 5))

    #Create a Send.
    send: Send = Send.fromJSON(vectors["send"])
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
      raise TestError("Meros didn't broadcast back a Send.")
    verify(rpc, send.hash)
    if rpc.call("transactions", "getUTXOs", {"address": address}) != [{"hash": send.hash.hex().upper(), "nonce": 0}]:
      raise TestError("Meros didn't consider a confirmed Transaction's outputs as UTXOs.")
    #Spend it, with a newer Mint as an input as well so we can prune it without pruning the original.
    newerSend: Send = createSend(rpc, [Claim.fromJSON(vectors["newerMintClaim"])], recipientPub)
    _: Send = createSend(rpc, [send, newerSend], bytes(32), recipient)
    if rpc.call("transactions", "getUTXOs", {"address": address}) != []:
      raise TestError("Meros thinks the recipient has UTXOs.")

    #Remove the spending Send by pruning its ancestor (a Mint).
    reorg(rpc, Blockchain.fromJSON(vectors["blocksWithoutNewerMint"]))
    #Meros should add back its parent as an UTXO.
    if rpc.call("transactions", "getUTXOs", {"address": address}) != [{"hash": send.hash.hex().upper(), "nonce": 0}]:
      raise TestError("Meros didn't consider a Transaction without spenders as an UTXO.")
    #Remove the original Send and verify its outputs are no longer considered UTXOs.
    reorg(rpc, Blockchain.fromJSON(vectors["blocksWithoutOlderMint"]))
    if rpc.call("transactions", "getUTXOs", {"address": address}) != []:
      raise TestError("Meros didn't remove the outputs of a pruned Transaction as UTXOs.")

    raise SuccessError()
Exemple #2
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])
Exemple #3
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.")
Exemple #4
0
def DepthOneTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Merit/Reorganizations/DepthOne.json",
                         "r")
    chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read())
    file.close()

    #Load the alternate blockchain.
    alt: Blockchain = Blockchain.fromJSON(chains["alt"])

    #Send the alternate tip.
    def sendAlternateTip() -> None:
        header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header)
        req: bytes = rpc.meros.sync.recv()
        if req != (MessageType.BlockBodyRequest.toByte() +
                   alt.blocks[-1].header.hash):
            raise TestError("Meros didn't request the BlockBody.")
        rpc.meros.blockBody(alt.blocks[-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.")

    with raises(SuccessError):
        Liver(rpc, chains["main"], callbacks={3: sendAlternateTip}).live()
Exemple #5
0
def OneHundredPercentSketchTest(meros: Meros) -> None:
    vectors: Dict[str, Any]
    with open("e2e/Vectors/Merit/Sketches/OneHundredPercent.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])
    if meros.live.recv() != header:
        raise TestError(
            "Meros didn't broadcast a BlockHeader for a Block it just added.")
Exemple #6
0
def StateTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Merit/State.json", "r")
    blocks: List[Dict[str, Any]] = json.loads(file.read())
    file.close()

    blockchain: Blockchain = Blockchain.fromJSON(blocks)
    state: State = State()

    def checkState(block: int) -> None:
        state.add(blockchain, block)

        meritSum: int = 0
        for miner in range(len(state.balances)):
            meritSum += state.balances[miner]
            if rpc.call("merit", "getMerit", [miner]) != {
                    "status": "Unlocked",
                    "malicious": False,
                    "merit": state.balances[miner]
            }:
                raise TestError("Merit doesn't match.")

        if meritSum != min(block, state.lifetime):
            raise TestError("Merit sum is invalid.")

    Liver(rpc, blocks, everyBlock=checkState).live()
def ShorterChainMoreWorkTest(
  rpc: RPC
) -> None:
  file: IO[Any] = open("e2e/Vectors/Merit/Reorganizations/ShorterChainMoreWork.json", "r")
  chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read())
  file.close()

  #Load the alternate blockchain.
  alt: Blockchain = Blockchain.fromJSON(chains["alt"])

  #Send the alternate tip.
  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[-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

    diff = -4
    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.")

  with raises(SuccessError):
    Liver(
      rpc,
      chains["main"],
      callbacks={
        25: sendAlternateTip
      }
    ).live()
Exemple #8
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.")
Exemple #9
0
def BlockListTest(rpc: RPC) -> None:
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        vectors: List[Dict[str, Any]] = json.loads(file.read())
    chain: Blockchain = Blockchain.fromJSON(vectors)

    amount1: int = 25
    amount2: int = 6

    def constructResponse(amount: int, lastBlock: int = -1) -> bytes:
        if lastBlock == -1:
            lastBlock = len(chain.blocks)
        lastBlock = min(amount, lastBlock)
        quantity: bytes = (lastBlock - 1).to_bytes(1, byteorder="little")
        hashes: List[bytes] = [
            block.header.hash for block in chain.blocks[:lastBlock]
        ]
        return quantity + b"".join(reversed(hashes))

    def beforeGenesis() -> None:
        rpc.meros.blockListRequest(1, chain.blocks[0].header.hash)
        blockList: bytes = rpc.meros.sync.recv()
        if blockList != MessageType.DataMissing.toByte():
            raise TestError(
                "Meros did not return a DataMissing response to a BlockListRequest of the Block before genesis."
            )

    def lessThanRequested() -> None:
        rpc.meros.blockListRequest(amount2,
                                   chain.blocks[amount2 - 1].header.hash)
        blockList: bytes = rpc.meros.sync.recv()
        if blockList[1:] != constructResponse(amount1, amount2 - 1):
            raise TestError(
                "Meros didn't properly return fewer blocks when a BlockListRequest requests more blocks than exist."
            )
        if (len(blockList[2:]) / 32) != (amount2 - 1):
            raise Exception(
                "Testing methodology error; considered the right amount of Blocks valid (not less than requested)."
            )

    def recHash() -> None:
        rpc.meros.blockListRequest(amount1, chain.blocks[amount1].header.hash)
        blockList: bytes = rpc.meros.sync.recv()
        if blockList[1:] != constructResponse(amount1):
            raise TestError(
                "Meros returned a different BlockList than expected in response to a BlockListRequest."
            )

    Liver(rpc,
          vectors,
          callbacks={
              1: beforeGenesis,
              (amount2 - 1): lessThanRequested,
              amount1: recHash
          }).live()
Exemple #10
0
def DifficultyTest(rpc: RPC) -> None:
    #Declared here so we can access the difficulties from this callback.
    blockchain: Blockchain

    def checkDifficulty(block: int) -> None:
        if rpc.call("merit",
                    "getDifficulty") != blockchain.difficulties[block]:
            raise TestError("Difficulty doesn't match.")

    with open("e2e/Vectors/Merit/Difficulty.json", "r") as file:
        blocks: List[Dict[str, Any]] = json.loads(file.read())
        blockchain = Blockchain.fromJSON(blocks)
        Liver(rpc, blocks, everyBlock=checkDifficulty).live()
Exemple #11
0
    def fromJSON(json: List[Dict[str, Any]]) -> Any:
        result: Merit = Merit.__new__(Merit)
        result.blockchain = Blockchain.fromJSON(json)
        result.state = State()
        result.epochs = Epochs()
        result.mints = []

        for b in range(1, len(result.blockchain.blocks)):
            mint: Optional[Mint] = result.epochs.shift(result.state,
                                                       result.blockchain, b)
            if mint is not None:
                result.mints.append(mint)

            result.state.add(result.blockchain, b)
        return result
Exemple #12
0
def DifficultyTest(rpc: RPC) -> None:
    #Blocks.
    file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
    blocks: List[Dict[str, Any]] = json.loads(file.read())
    file.close()

    #Blockchain.
    blockchain: Blockchain = Blockchain.fromJSON(blocks)

    def checkDifficulty(block: int) -> None:
        if int(rpc.call("merit", "getDifficulty"),
               16) != blockchain.difficulties[block]:
            raise TestError("Difficulty doesn't match.")

    Liver(rpc, blocks, everyBlock=checkDifficulty).live()
Exemple #13
0
def StateTest(rpc: RPC) -> None:
    #Blocks.
    file: IO[Any] = open("e2e/Vectors/Merit/StateBlocks.json", "r")
    blocks: List[Dict[str, Any]] = json.loads(file.read())
    file.close()

    #Blockchain.
    blockchain: Blockchain = Blockchain.fromJSON(blocks)
    #State.
    state: State = State()

    def checkState(block: int) -> None:
        state.add(blockchain, block)

        for miner in state.unlocked:
            if rpc.call("merit", "getMerit", [miner]) != {
                    "unlocked": True,
                    "malicious": False,
                    "merit": state.unlocked[miner]
            }:
                raise TestError("Merit doesn't match.")

    Liver(rpc, blocks, everyBlock=checkState).live()
Exemple #14
0
def THFSLessWorkTest(
  rpc: RPC
) -> None:
  chains: Dict[str, List[Dict[str, Any]]]
  with open("e2e/Vectors/Merit/Reorganizations/LongerChainMoreWork.json", "r") as file:
    chains = json.loads(file.read())

  #Load the alternate blockchain.
  alt: Blockchain = Blockchain.fromJSON(chains["alt"])

  #Load a chain of the fork point.
  forkPoint: Blockchain = Blockchain.fromJSON(chains["main"][0 : 15])

  #Send the alternate tip.
  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 == -10:
        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.")

  with raises(SuccessError):
    Liver(
      rpc,
      chains["main"],
      callbacks={
        25: sendAlternateTip
      }
    ).live()
Exemple #15
0
#Ed25519 lib.
import ed25519

#Blake2b standard function.
from hashlib import blake2b

#JSON standard lib.
import json

cmFile: IO[Any] = open("e2e/Vectors/Transactions/ClaimedMint.json", "r")
cmVectors: Dict[str, Any] = json.loads(cmFile.read())
#Transactions.
transactions: Transactions = Transactions.fromJSON(cmVectors["transactions"])
#Blockchain.
blockchain: Blockchain = Blockchain.fromJSON(cmVectors["blockchain"])
cmFile.close()

#SpamFilter.
sendFilter: SpamFilter = SpamFilter(3)

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

#BLS keys.
blsPrivKeys: List[PrivateKey] = [
    PrivateKey(blake2b(b'\0', digest_size=32).digest()),
    PrivateKey(blake2b(b'\1', digest_size=32).digest())
]
blsPubKeys: List[PublicKey] = [
Exemple #16
0
def TwoHundredTwentyEightTest(
  rpc: RPC
) -> None:
  file: IO[Any] = open("e2e/Vectors/Merit/Reorganizations/ShorterChainMoreWork.json", "r")
  chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read())
  file.close()

  #Load the Blockchains.
  main: Blockchain = Blockchain.fromJSON(chains["main"])
  alt: Blockchain = Blockchain.fromJSON(chains["alt"])

  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)

    #The Liver will now send Block 16 again. The trick is whether or not it can add 16.
    #If it can't, because it didn't prune the Data, this issue is still valid.

  #This is based off the ShorterChainMoreWork test.
  #While that test syncs the full chain A, it then syncs the alt chain.
  #As this chain tests a failed re-org attempt, we can stay on the main chain and fully verify it.
  #So instead of syncing the full chain, we go to right before the fork, and manually sync the forked Block.
  #Then we trigger the re-org, cause it to fail, preserving the revert.
  #Then the Liver syncs the rest of the main chain, unaware of this.
  Liver(
    rpc,
    chains["main"],
    callbacks={
      15: send16AndInvalidAlt
    }
  ).live()
Exemple #17
0
 def verify() -> None:
     verifyMeritRemoval(rpc, 1, 1, removal.holder, False)
     verifyBlockchain(rpc, Blockchain.fromJSON(vectors["blockchain"]))
     raise SuccessError(
         "MeritRemoval and Blockchain were properly handled.")
Exemple #18
0
def ChainReorgDifferentKeyTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Merit/RandomX/ChainReorgSameKey.json",
                         "r")
    chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read())
    file.close()

    #Load the blockchains.
    main: Blockchain = Blockchain.fromJSON(chains["main"])
    alt: Blockchain = Blockchain.fromJSON(chains["alt"])

    #Send the alternate tip.
    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])

    Liver(rpc, chains["main"], callbacks={400: sendAlternateTip}).live()
Exemple #19
0
from e2e.Classes.Merit.Block import Block
from e2e.Classes.Merit.Blockchain import Blockchain

#Blake2b standard function.
from hashlib import blake2b

#JSON standard lib.
import json

#Miner Private Key.
privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest())

#Blockchains.
bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
blocks: List[Dict[str, Any]] = json.loads(bbFile.read())
main: Blockchain = Blockchain.fromJSON(blocks)
#Only add the first 15 to the alt chain.
alt: Blockchain = Blockchain()
for b in range(15):
    alt.add(Block.fromJSON(blocks[b]))

#Generate an alternative five Blocks.
for i in range(1, 5):
    #Create the next Block.
    block = Block(
        BlockHeader(
            0,
            alt.last(),
            bytes(32),
            1,
            bytes(4),
Exemple #20
0
def GetBlockTest(rpc: RPC) -> None:
    blockchain: Blockchain
    claim: Claim
    send: Send
    datas: List[Data]

    txKey: Callable[[Dict[str, Any]], str] = lambda tx: tx["hash"]

    def verify() -> None:
        for b in range(len(blockchain.blocks)):
            block: Dict[str, Any] = rpc.call(
                "merit", "getBlock",
                {"block": blockchain.blocks[b].header.hash.hex().upper()},
                False)
            if rpc.call("merit", "getBlock", {"block": b}, False) != block:
                raise TestError(
                    "Meros reported different Blocks depending on if nonce/hash indexing."
                )

            #Python doesn't keep track of the removals.
            #That said, they should all be empty except for the last one.
            if b != (len(blockchain.blocks) - 1):
                if block["removals"] != []:
                    raise TestError("Meros reported the Block had removals.")
            del block["removals"]

            if blockchain.blocks[b].toJSON() != block:
                raise TestError(
                    "Meros's JSON serialization of Blocks differs from Python's."
                )

        #Test the key serialization of the first Block.
        #The final Block uses a nick, hence the value in this.
        if rpc.call("merit", "getBlock", {"block": 1},
                    False)["header"]["miner"] != PrivateKey(
                        0).toPublicKey().serialize().hex().upper():
            raise TestError("Meros didn't serialize a miner's key properly.")

        #Manually test the final, and most complex, block.
        final: Dict[str, Any] = rpc.call("merit", "getBlock",
                                         {"block": len(blockchain.blocks) - 1},
                                         False)
        final["transactions"].sort(key=txKey)
        final["removals"].sort()
        if final != {
                "hash":
                blockchain.blocks[-1].header.hash.hex().upper(),
                "header": {
                    "version":
                    blockchain.blocks[-1].header.version,
                    "last":
                    blockchain.blocks[-1].header.last.hex().upper(),
                    "contents":
                    blockchain.blocks[-1].header.contents.hex().upper(),
                    "packets":
                    blockchain.blocks[-1].header.packetsQuantity,
                    "sketchSalt":
                    blockchain.blocks[-1].header.sketchSalt.hex().upper(),
                    "sketchCheck":
                    blockchain.blocks[-1].header.sketchCheck.hex().upper(),
                    "miner":
                    blockchain.blocks[-1].header.minerKey.hex().upper()
                    if blockchain.blocks[-1].header.newMiner else
                    blockchain.blocks[-1].header.minerNick,
                    "time":
                    blockchain.blocks[-1].header.time,
                    "proof":
                    blockchain.blocks[-1].header.proof,
                    "signature":
                    blockchain.blocks[-1].header.signature.hex().upper()
                },
                "transactions":
                sorted([{
                    "hash": claim.hash.hex().upper(),
                    "holders": [0]
                }, {
                    "hash": send.hash.hex().upper(),
                    "holders": [0, 1, 2]
                }, {
                    "hash": datas[0].hash.hex().upper(),
                    "holders": [0, 2]
                }, {
                    "hash": datas[1].hash.hex().upper(),
                    "holders": [0, 1, 3]
                }, {
                    "hash": datas[2].hash.hex().upper(),
                    "holders": [0, 1, 2, 3, 4]
                }, {
                    "hash": datas[3].hash.hex().upper(),
                    "holders": [0, 1, 2, 3]
                }],
                       key=txKey),
                "elements": [
                    {
                        "descendant": "DataDifficulty",
                        "holder": 3,
                        "nonce": 0,
                        "difficulty": 8
                    },
                    {
                        "descendant": "SendDifficulty",
                        "holder": 0,
                        "nonce": 0,
                        "difficulty": 1
                    },
                    {
                        "descendant": "DataDifficulty",
                        "holder": 3,
                        "nonce": 0,
                        "difficulty": 4
                    },
                    {
                        "descendant": "DataDifficulty",
                        "holder": 4,
                        "nonce": 2,
                        "difficulty": 1
                    },
                    {
                        "descendant": "SendDifficulty",
                        "holder": 4,
                        "nonce": 1,
                        "difficulty": 3
                    },
                    {
                        "descendant": "SendDifficulty",
                        "holder": 2,
                        "nonce": 1,
                        "difficulty": 2
                    },
                    {
                        "descendant": "DataDifficulty",
                        "holder": 0,
                        "nonce": 0,
                        "difficulty": 7
                    },
                ],
                "removals": [0, 3],
                "aggregate":
                blockchain.blocks[-1].body.aggregate.serialize().hex().upper()
        }:
            raise TestError("Final Block wasn't correct.")

        #Test invalid calls.
        try:
            rpc.call("merit", "getBlock", {"block": 100}, False)
            raise Exception("")
        except Exception as e:
            if str(e) != "-2 Block not found.":
                raise TestError(
                    "getBlock didn't error when we used a non-existent nonce.")

        try:
            rpc.call("merit", "getBlock", {"block": -5}, False)
            raise Exception("")
        except Exception as e:
            if str(e) != "-32602 Invalid params.":
                raise TestError(
                    "getBlock didn't error when we used a negative (signed) integer for a nonce."
                )

        try:
            rpc.call(
                "merit", "getBlock", {
                    "block":
                    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
                }, False)
            raise Exception("")
        except Exception as e:
            if str(e) != "-2 Block not found.":
                raise TestError(
                    "getBlock didn't error when we used a non-existent hash.")

        try:
            rpc.call("merit", "getBlock", {"block": ""}, False)
            raise Exception("")
        except Exception as e:
            if str(e) != "-32602 Invalid params.":
                raise TestError(
                    "getBlock didn't error when we used an invalid hash.")

    with open("e2e/Vectors/RPC/Merit/GetBlock.json", "r") as file:
        vectors: Dict[str, Any] = json.loads(file.read())
        blockchain = Blockchain.fromJSON(vectors["blockchain"])
        claim = Claim.fromJSON(vectors["claim"])
        send = Send.fromJSON(vectors["send"])
        datas = [Data.fromJSON(data) for data in vectors["datas"]]
        transactions: Transactions = Transactions.fromJSON(
            vectors["transactions"])
        Liver(rpc, vectors["blockchain"], transactions, {
            (len(blockchain.blocks) - 1): verify
        }).live()
Exemple #21
0
    def restOfTest() -> None:
        #Move expected into scope.
        expected: str = getAddress(mnemonic, password, 0)

        #Send to the new address, then call getAddress again. Verify a new address appears.
        last: Send = createSend(
            rpc,
            Claim.fromTransaction(iter(transactions.txs.values()).__next__()),
            expected)
        hashes: List[bytes] = [last.hash]

        expected = getAddress(mnemonic, password, 1)
        if rpc.call("personal", "getAddress") != expected:
            raise TestError(
                "Meros didn't move to the next address once the existing one was used."
            )

        #Send to the new unused address, spending the funds before calling getAddress again.
        #Checks address usage isn't defined as having an UTXO, yet rather any TXO.
        #Also confirm the spending TX with full finalization before checking.
        #Ensures the TXO isn't unspent by any definition.
        last = createSend(rpc, last, expected)
        hashes.append(last.hash)

        #Spending TX.
        send: Send = Send([(hashes[-1], 0)], [(bytes(32), 1)])
        send.signature = ed.sign(b"MEROS" + send.hash,
                                 getPrivateKey(mnemonic, password, 1))
        send.beat(SpamFilter(3))
        if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
            raise TestError("Meros didn't broadcast back the spending Send.")
        hashes.append(send.hash)

        #In order to finalize, we need to mine 6 Blocks once this Transaction and its parent have Verifications.
        for txHash in hashes:
            sv: SignedVerification = SignedVerification(txHash)
            sv.sign(0, PrivateKey(0))
            if rpc.meros.signedElement(sv) != rpc.meros.live.recv():
                raise TestError("Meros didn't broadcast back a Verification.")

        #Close the sockets while we mine.
        rpc.meros.live.connection.close()
        rpc.meros.sync.connection.close()

        #Mine these to the Wallet on the node so we can test getMeritHolderNick.
        privKey: PrivateKey = PrivateKey(
            bytes.fromhex(rpc.call("personal", "getMeritHolderKey")))
        blockchain: Blockchain = Blockchain.fromJSON(vectors["blockchain"])
        for _ in range(6):
            template: Dict[str, Any] = rpc.call(
                "merit", "getBlockTemplate",
                {"miner": privKey.toPublicKey().serialize().hex()})
            proof: int = -1
            tempHash: bytes = bytes()
            tempSignature: bytes = bytes()
            while ((proof == -1)
                   or ((int.from_bytes(tempHash, "little") *
                        (blockchain.difficulty() * 11 // 10)) > int.from_bytes(
                            bytes.fromhex("FF" * 32), "little"))):
                proof += 1
                tempHash = RandomX(
                    bytes.fromhex(template["header"]) +
                    proof.to_bytes(4, "little"))
                tempSignature = privKey.sign(tempHash).serialize()
                tempHash = RandomX(tempHash + tempSignature)

            rpc.call(
                "merit", "publishBlock", {
                    "id":
                    template["id"],
                    "header":
                    template["header"] + proof.to_bytes(4, "little").hex() +
                    tempSignature.hex()
                })
            blockchain.add(
                Block.fromJSON(
                    rpc.call("merit", "getBlock",
                             {"block": len(blockchain.blocks)})))

        #Verify a new address is returned.
        expected = getAddress(mnemonic, password, 2)
        if rpc.call("personal", "getAddress") != expected:
            raise TestError(
                "Meros didn't move to the next address once the existing one was used."
            )

        #Get a new address after sending to the address after it.
        #Use both, and then call getAddress.
        #getAddress should detect X is used, move to Y, detect Y is used, and move to Z.
        #It shouldn't assume the next address after an used address is unused.
        #Actually has two Ys as one iteration of the code only ran for the next address; not all future addresses.

        #Send to the next next addresses.
        for i in range(2):
            addy: str = getAddress(mnemonic, password, 3 + i)

            #Reopen the sockets. This isn't done outside of the loop due to the time deriving the final address can take.
            #Due to how slow the reference Python code is, it is necessary to redo the socket connections.
            sleep(65)
            rpc.meros.liveConnect(Blockchain().blocks[0].header.hash)
            rpc.meros.syncConnect(Blockchain().blocks[0].header.hash)

            last = createSend(rpc, last, addy)
            if MessageType(rpc.meros.live.recv()
                           [0]) != MessageType.SignedVerification:
                raise TestError(
                    "Meros didn't create and broadcast a SignedVerification for this Send."
                )

            if i == 0:
                #Close them again.
                rpc.meros.live.connection.close()
                rpc.meros.sync.connection.close()

        #Verify getAddress returns the existing next address.
        if rpc.call("personal", "getAddress") != expected:
            raise TestError(
                "Sending to the address after this address caused Meros to consider this address used."
            )

        #Send to the next address.
        last = createSend(rpc, last, expected)
        if MessageType(
                rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
            raise TestError(
                "Meros didn't create and broadcast a SignedVerification for this Send."
            )

        #Verify getAddress returns the address after the next next addresses.
        expected = getAddress(mnemonic, password, 5)
        if rpc.call("personal", "getAddress") != expected:
            raise TestError(
                "Meros didn't return the correct next address after using multiple addresses in a row."
            )

        #Now that we have mined a Block as part of this test, ensure the Merit Holder nick is set.
        if rpc.call("personal", "getMeritHolderNick") != 1:
            raise TestError(
                "Merit Holder nick wasn't made available despite having one.")

        #Sanity check off Mnemonic.
        if rpc.call("personal", "getMnemonic") != mnemonic:
            raise TestError("getMnemonic didn't return the correct Mnemonic.")

        #Existing values used to test getMnemonic/getMeritHolderKey/getMeritHolderNick/getAccount/getAddress consistency.
        existing: Dict[str, Any] = {
            #Should be equal to the mnemonic variable, which is verified in a check above.
            "getMnemonic": rpc.call("personal", "getMnemonic"),
            "getMeritHolderKey": rpc.call("personal", "getMeritHolderKey"),
            "getMeritHolderNick": rpc.call("personal", "getMeritHolderNick"),
            "getAccount": rpc.call("personal", "getAccount"),
            #Should be equal to expected, which is also verified in a check above.
            "getAddress": rpc.call("personal", "getAddress")
        }

        #Set a new seed and verify the Merit Holder nick is cleared.
        rpc.call("personal", "setWallet")
        try:
            rpc.call("personal", "getMeritHolderNick")
            raise TestError("")
        except TestError as e:
            if str(
                    e
            ) != "-2 Wallet doesn't have a Merit Holder nickname assigned.":
                raise TestError(
                    "getMeritHolderNick returned something or an unexpected error when a new Mnemonic was set."
                )

        #Set back the old seed and verify consistency.
        rpc.call("personal", "setWallet", {
            "mnemonic": mnemonic,
            "password": password
        })
        for method in existing:
            if rpc.call("personal", method) != existing[method]:
                raise TestError(
                    "Setting an old seed caused the WalletDB to improperly reload."
                )

        #Verify calling getAddress returns the expected address.
        if rpc.call("personal", "getAddress") != expected:
            raise TestError(
                "Meros returned an address that wasn't next after reloading the seed."
            )

        #Reboot the node and verify consistency.
        rpc.quit()
        sleep(3)
        rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc)
        for method in existing:
            if rpc.call("personal", method) != existing[method]:
                raise TestError(
                    "Rebooting the node caused the WalletDB to improperly reload."
                )

        #Used so Liver doesn't run its own post-test checks.
        #Since we added our own Blocks, those will fail.
        raise SuccessError()
Exemple #22
0
def TwoHundredThirtyTwoTest(rpc: RPC) -> None:
    file: IO[Any] = open(
        "e2e/Vectors/Merit/Reorganizations/TwoHundredThirtyTwo.json", "r")
    chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read())
    main: Blockchain = Blockchain.fromJSON(chains["main"])
    alt: Blockchain = Blockchain.fromJSON(chains["alt"])
    file.close()

    def sendBlock(toSend: Block) -> None:
        rpc.meros.liveBlockHeader(toSend.header)
        if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() +
                                     toSend.header.hash):
            raise TestError("Meros didn't ask for this Block's body.")
        rpc.meros.blockBody(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 rpc.meros.sync.recv()[0] != MessageType.BlockListRequest.toByte()[0]:
        raise TestError(
            "Meros didn't ask for the Block List of the alternate chain.")
    rpc.meros.blockList([alt.blocks[2].header.hash, 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)
def TwoHundredSixtyOneTest(rpc: RPC) -> None:
    merit: Merit = Merit()

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

    #Get a template.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": blsPubKey})
    template["header"] = bytes.fromhex(template["header"])

    #Mine it.
    #Ignores the template except for the ID needed to publish it.
    #We could publish it over the socket to an identical effect, practically.
    #That said, this is more accurate to flow.
    block: Block = PrototypeBlock(merit.blockchain.blocks[-1].header.time +
                                  1200,
                                  minerID=blsPrivKey).finish(0, merit)
    merit.add(block)

    #Connect in order to receive their Verification of the Block's Data.
    rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

    #Publish it.
    rpc.call("merit", "publishBlock", {
        "id": template["id"],
        "header": block.header.serialize().hex()
    })
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
        raise TestError("Meros didn't broadcast a published Block.")

    #Receive their Verification.
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't verify the Block's Data.")

    #Reorg past the chain with them as the nick.
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        blankBlocks: Blockchain = Blockchain.fromJSON(json.loads(file.read()))
        rpc.meros.liveBlockHeader(blankBlocks.blocks[2].header)

        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest:
            raise TestError(
                "Meros didn't request the Block List needed to reorg.")
        rpc.meros.blockList([blankBlocks.blocks[0].header.hash])

        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.BlockHeaderRequest:
            raise TestError(
                "Meros didn't request the next Block Header on the list.")
        rpc.meros.syncBlockHeader(blankBlocks.blocks[1].header)

        for b in range(2):
            rpc.meros.handleBlockBody(blankBlocks.blocks[b + 1])

    #Close the connection to give us time to mine Blocks without worrying about the handshake.
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()
    sleep(65)

    #Mine Blocks so we can re-org back to the original chain.
    merit.add(
        PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                       minerID=PrivateKey(1)).finish(0, merit))
    merit.add(
        PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                       minerID=1).finish(0, merit))

    #Reconnect.
    rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

    #Send the header for the original chain.
    rpc.meros.liveBlockHeader(merit.blockchain.blocks[3].header)

    if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest:
        raise TestError("Meros didn't request the Block List needed to reorg.")
    rpc.meros.blockList([
        merit.blockchain.blocks[1].header.hash,
        merit.blockchain.blocks[0].header.hash
    ])

    for h in range(2):
        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.BlockHeaderRequest:
            raise TestError(
                "Meros didn't request the next Block Header on the list.")
        rpc.meros.syncBlockHeader(merit.blockchain.blocks[h + 1].header)

    for b in range(3):
        rpc.meros.handleBlockBody(merit.blockchain.blocks[b + 1])

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

    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError(
            "Meros didn't verify the Block's Data after the re-org.")