Beispiel #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()
Beispiel #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])
Beispiel #3
0
def HundredEightySevenTest(
  meros: Meros
) -> None:
  file: IO[Any] = open("e2e/Vectors/Merit/HundredEightySeven.json", "r")
  vectors: List[Dict[str, Any]] = json.loads(file.read())
  file.close()

  meros.liveConnect(Blockchain().last())
  meros.syncConnect(Blockchain().last())

  block: Block = Block.fromJSON(vectors[0])
  sent: bytes = meros.liveBlockHeader(block.header)
  if meros.sync.recv() != MessageType.BlockBodyRequest.toByte() + block.header.hash:
    raise TestError("Meros didn't request the matching BlockBody.")
  meros.blockBody(block)
  if meros.live.recv() != sent:
    raise TestError("Meros didn't broadcast a BlockHeader.")

  meros.liveBlockHeader(Block.fromJSON(vectors[1]).header)
  with raises(SuccessError):
    try:
      if len(meros.live.recv()) != 0:
        raise Exception()
    except TestError:
      sleep(1)
      if meros.process.poll() is not None:
        raise TestError("Node crashed trying to handle a BlockHeader which re-registers a key.")
      raise SuccessError("Node disconnected us after we sent a BlockHeader which re-registers a key.")
    except Exception:
      raise TestError("Meros didn't disconnect us after we sent a BlockHeader which re-registers a key; it also didn't crash.")
Beispiel #4
0
def verifyBlockchain(
  rpc: RPC,
  blockchain: Blockchain
) -> None:
  sleep(2)

  if rpc.call("merit", "getHeight") != len(blockchain.blocks):
    raise TestError("Height doesn't match.")
  if blockchain.difficulty() != rpc.call("merit", "getDifficulty"):
    raise TestError("Difficulty doesn't match.")

  for b in range(len(blockchain.blocks)):
    ourBlock: Dict[str, Any] = blockchain.blocks[b].toJSON()
    blockJSON: Dict[str, Any] = rpc.call("merit", "getBlock", {"block": b})
    #Contextual info Python doesn't track.
    del blockJSON["removals"]
    if blockJSON != ourBlock:
      raise TestError("Block doesn't match.")

    #Test when indexing by the hash instead of the nonce.
    blockJSON = rpc.call(
      "merit",
      "getBlock",
      {"block": blockchain.blocks[b].header.hash.hex().upper()}
    )
    del blockJSON["removals"]
    if blockJSON != ourBlock:
      raise TestError("Block doesn't match.")
def HundredSixSignedElementsTest(rpc: RPC) -> None:
    #Solely used to get the genesis Block hash.
    blockchain: Blockchain = Blockchain()

    blsPrivKey: PrivateKey = PrivateKey(0)
    sig: Signature = blsPrivKey.sign(bytes())

    #Create a Data.
    #This is required so the Verification isn't terminated early for having an unknown hash.
    data: bytes = bytes.fromhex(rpc.call("personal", "data", ["AA"]))

    #Create a signed Verification, SendDifficulty, and DataDifficulty.
    elements: List[SignedElement] = [
        SignedVerification(data, 1, sig),
        SignedSendDifficulty(0, 0, 1, sig),
        SignedDataDifficulty(0, 0, 1, sig)
    ]

    for elem in elements:
        #Handshake with the node.
        rpc.meros.liveConnect(blockchain.blocks[0].header.hash)

        #Send the Element.
        rpc.meros.signedElement(elem)

        #Sleep for thirty seconds to make sure Meros realizes our connection is dead.
        sleep(30)

        #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.")
Beispiel #6
0
def verifyBlockchain(rpc: RPC, blockchain: Blockchain) -> None:
    sleep(2)

    if rpc.call("merit", "getHeight") != len(blockchain.blocks):
        raise TestError("Height doesn't match.")
    if blockchain.difficulty() != rpc.call("merit", "getDifficulty"):
        raise TestError("Difficulty doesn't match.")

    for b in range(len(blockchain.blocks)):
        ourBlock: Dict[str, Any] = blockchain.blocks[b].toJSON()
        #Info Python saves so it can properly load from the vectors yet the Meros RPC excludes.
        del ourBlock["header"]["packets"]

        blockJSON: Dict[str, Any] = rpc.call("merit", "getBlock", [b])
        #Contextual info Python doesn't track.
        del blockJSON["removals"]
        if blockJSON != ourBlock:
            raise TestError("Block doesn't match.")

        #Test when indexing by the hash instead of the nonce.
        blockJSON = rpc.call("merit", "getBlock",
                             [blockchain.blocks[b].header.hash.hex().upper()])
        del blockJSON["removals"]
        if blockJSON != ourBlock:
            raise TestError("Block doesn't match.")
Beispiel #7
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.")
Beispiel #8
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()
Beispiel #9
0
def verifyBlockchain(
  rpc: RPC,
  blockchain: Blockchain
) -> None:
  #Sleep to ensure data races aren't a problem.
  sleep(2)

  #Verify the height.
  if rpc.call("merit", "getHeight") != len(blockchain.blocks):
    raise TestError("Height doesn't match.")

  #Verify the difficulty.
  if blockchain.difficulty() != int(rpc.call("merit", "getDifficulty"), 16):
    raise TestError("Difficulty doesn't match.")

  #Verify the Blocks.
  for b in range(len(blockchain.blocks)):
    if rpc.call("merit", "getBlock", [b]) != blockchain.blocks[b].toJSON():
      raise TestError("Block doesn't match.")

    if rpc.call(
      "merit",
      "getBlock",
      [blockchain.blocks[b].header.hash.hex().upper()]
    ) != blockchain.blocks[b].toJSON():
      raise TestError("Block doesn't match.")
Beispiel #10
0
    def add(self,
            nick: int = 0,
            packets: List[VerificationPacket] = [],
            elements: List[Element] = []) -> None:
        #Determine if this is a new miner or not.
        miner: Union[PrivateKey, int]
        self.miners.append(self.miners[-1])
        if nick > self.miners[-1]:
            raise GenerationError(
                "Told to mine a Block with a miner nick which doesn't exist.")
        if nick == self.miners[-1]:
            miner = PrivateKey(nick)
            self.miners[-1] += 1
        else:
            miner = nick

        timeBase: int
        if len(self.blocks) == 0:
            timeBase = Blockchain().blocks[0].header.time
        else:
            timeBase = self.blocks[-1].time

        #Create and add the PrototypeBlock.
        self.blocks.append(
            PrototypeBlock(
                timeBase + self.timeOffset,
                #Create copies of the lists used as arguments to ensure we don't mutate the arguments.
                list(packets),
                list(elements),
                miner))
Beispiel #11
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.")
Beispiel #12
0
def createSend(rpc: RPC, last: Union[Claim, Send], toAddress: str) -> Send:
    funded: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    if isinstance(last, Claim):
        send: Send = Send([(last.hash, 0)],
                          [(decodeAddress(toAddress), 1),
                           (funded.get_verifying_key(), last.amount - 1)])
    else:
        send: Send = Send(
            [(last.hash, 1)],
            [(decodeAddress(toAddress), 1),
             (funded.get_verifying_key(), last.outputs[1][1] - 1)])

    send.sign(funded)
    send.beat(SpamFilter(3))
    sleep(65)
    rpc.meros.liveConnect(Blockchain().blocks[0].header.hash)
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Send.")

    sv: SignedVerification = SignedVerification(send.hash)
    sv.sign(0, PrivateKey(0))
    if rpc.meros.signedElement(sv) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Verification.")

    return send
Beispiel #13
0
def LANPeersTest(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)

    #Verify that sending a PeersRequest returns 0 peers.
    meros.peersRequest()
    if len(meros.sync.recv(True)) != 2:
        raise TestError("Meros sent peers.")

    #Create a new connection which identifies as a server.
    serverConnection: socket.socket = socket.socket(socket.AF_INET,
                                                    socket.SOCK_STREAM)
    serverConnection.connect(("127.0.0.1", meros.tcp))
    serverConnection.send(
        MessageType.Syncing.toByte() + (254).to_bytes(1, "little") +
        (254).to_bytes(1, "little") + (128).to_bytes(1, "little") +
        (6000).to_bytes(2, "little") + blockchain.blocks[0].header.hash, False)
    serverConnection.recv(38)
    sleep(1)

    #Verify Meros ignores us as a peer since we're only available over the local network.
    meros.peersRequest()
    res: bytes = meros.sync.recv(True)
    if len(res) != 2:
        raise TestError("Meros sent peers.")

    #Close the new connection.
    serverConnection.close()
Beispiel #14
0
def HundredTwentyFiveTest(rpc: RPC) -> None:
    #Meros allows connections from its own IP if they identify as 127.0.0.1.
    #We need to connect either through the LAN or through the public IP for this test to be valid.
    #The following code grabs the computer's 192 IP.
    lanIPFinder = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    lanIPFinder.connect(("meroscrypto.io", 443))
    lanIP = lanIPFinder.getsockname()[0]
    lanIPFinder.close()

    if not (lanIP.split(".")[0] in {"10", "172", "192"}):
        raise Exception("Failed to get the LAN IP.")

    #Blockchain. Solely used to get the genesis Block hash.
    blockchain: Blockchain = Blockchain()

    #Connect to Meros.
    connection: socket.socket = socket.socket(socket.AF_INET,
                                              socket.SOCK_STREAM)
    connection.connect((lanIP, rpc.meros.tcp))
    try:
        connection.send(
            MessageType.Syncing.toByte() + (254).to_bytes(1, "big") +
            (254).to_bytes(1, "big") + (128).to_bytes(1, "big") +
            (6000).to_bytes(2, "big") + blockchain.blocks[0].header.hash,
            False)
        if len(connection.recv(38)) == 0:
            raise Exception("")
    except Exception:
        raise SuccessError(
            "Meros closed a connection from the same IP as itself which wasn't 127.0.0.1."
        )
    raise TestError(
        "Meros allowed a connection from the same IP as itself which wasn't 127.0.0.1."
    )
Beispiel #15
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.")
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()
Beispiel #17
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()
Beispiel #18
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.")
Beispiel #19
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."
        )
Beispiel #20
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()
Beispiel #21
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()
Beispiel #22
0
def DataTest(rpc: RPC) -> None:
    privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    pubKey: bytes = privKey.get_verifying_key()

    genesis: bytes = Blockchain().blocks[0].header.hash
    spamFilter: SpamFilter = SpamFilter(5)

    data: Data = Data(bytes(32), pubKey)
    data.sign(privKey)
    data.beat(spamFilter)

    rpc.meros.liveConnect(genesis)
    rpc.meros.liveTransaction(data)
    verifyTransaction(rpc, data)
Beispiel #23
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
Beispiel #24
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()
Beispiel #25
0
def verifyBlockchain(rpc: RPC, blockchain: Blockchain) -> None:
    sleep(2)

    if rpc.call("merit", "getHeight") != len(blockchain.blocks):
        raise TestError("Height doesn't match.")
    if blockchain.difficulty() != int(rpc.call("merit", "getDifficulty"), 16):
        raise TestError("Difficulty doesn't match.")

    for b in range(len(blockchain.blocks)):
        if rpc.call("merit", "getBlock", [b]) != blockchain.blocks[b].toJSON():
            raise TestError("Block doesn't match.")
        if rpc.call("merit", "getBlock",
                    [blockchain.blocks[b].header.hash.hex().upper()
                     ]) != blockchain.blocks[b].toJSON():
            raise TestError("Block doesn't match.")
Beispiel #26
0
def MultiplePacketsTest(rpc: RPC) -> None:
    #Spawn a Blockchain just to set the RandomX key.
    _: Blockchain = Blockchain()

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

    data: Data = Data.fromJSON(vectors["data"])
    block: Block = Block.fromJSON(vectors["blockchain"][-1])

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

    with raises(SuccessError):
        Liver(rpc, vectors["blockchain"], callbacks={
            2: sendDataAndBlock
        }).live()
Beispiel #27
0
def HundredSixSignedElementsTest(
  rpc: RPC
) -> None:
  #Solely used to get the genesis Block hash.
  blockchain: Blockchain = Blockchain()

  edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
  blsPrivKey: PrivateKey = PrivateKey(0)
  sig: Signature = blsPrivKey.sign(bytes())

  #Create a Data for the Verification.
  data: Data = Data(bytes(32), edPrivKey.get_verifying_key())
  data.sign(edPrivKey)
  data.beat(SpamFilter(5))

  #Create a signed Verification, SendDifficulty, and DataDifficulty.
  elements: List[SignedElement] = [
    SignedVerification(data.hash, 1, sig),
    SignedSendDifficulty(0, 0, 1, sig),
    SignedDataDifficulty(0, 0, 1, sig)
  ]

  dataSent: bool = False
  for elem in elements:
    #Handshake with the node.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)

    #Send the Data if we have yet to.
    if not dataSent:
      if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Data wasn't rebroadcasted.")
      dataSent = True

    #Send the Element.
    rpc.meros.signedElement(elem)

    #Sleep for thirty seconds to make sure Meros realizes our connection is dead.
    sleep(30)

    #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.")
def ChunkedEncodingTest(rpc: RPC) -> None:
    request: requests.Response = requests.post("http://127.0.0.1:" +
                                               str(rpc.meros.rpc),
                                               data=reqIter())
    if request.status_code != 200:
        raise TestError("HTTP status isn't 200: " + str(request.status_code))
    if request.json() != [{
            "jsonrpc": "2.0",
            "id": 1,
            "result": 1
    }, {
            "jsonrpc": "2.0",
            "id": 0,
            "result": Blockchain().difficulty()
    }]:
        raise TestError(
            "Meros didn't respond to a batch request (sent as chunks) properly."
        )
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
Beispiel #30
0
def LANPeersTest(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)

    #Verify that sending a PeersRequest returns 0 peers.
    meros.peersRequest()
    if len(meros.sync.recv(True)) != 2:
        raise TestError("Meros sent peers.")

    #Create a server socket.
    server: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("", 0))
    server.listen(1)

    #Connect again.
    #Meros, if it doesn't realize we're a LAN peer, will try to connect to the above server to verify.
    #Since we're a LAN peer, it shouldn't bother.
    #Doesn't use MerosSocket so we can specify the port.
    serverConnection: socket.socket = socket.socket(socket.AF_INET,
                                                    socket.SOCK_STREAM)
    serverConnection.connect(("127.0.0.1", meros.tcp))
    serverConnection.send(
        MessageType.Syncing.toByte() + meros.protocol.to_bytes(1, "little") +
        meros.network.to_bytes(1, "little") + (0).to_bytes(1, "little") +
        server.getsockname()[1].to_bytes(2, "little") +
        blockchain.blocks[0].header.hash, False)
    serverConnection.recv(38)
    sleep(1)

    #Verify Meros ignores us as a peer since we're only available over the local network.
    meros.peersRequest()
    res: bytes = meros.sync.recv(True)
    if len(res) != 2:
        raise TestError("Meros sent peers.")

    #Close the new connection.
    serverConnection.close()