예제 #1
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.")
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.")
예제 #3
0
def mineBlock(rpc: RPC, nick: int = 0) -> None:
    privKey: PrivateKey = PrivateKey(nick)
    template: Dict[str, Any] = rpc.call(
        "merit", "getBlockTemplate",
        {"miner": privKey.toPublicKey().serialize().hex()})
    header: bytes = bytes.fromhex(template["header"])[:-4]
    header += (rpc.call(
        "merit", "getBlock",
        {"block": rpc.call("merit", "getHeight") - 1})["header"]["time"] +
               1200).to_bytes(4, "little")

    proof: int = -1
    tempHash: bytes = bytes()
    signature: bytes = bytes()
    while ((proof == -1)
           or ((int.from_bytes(tempHash, "little") * template["difficulty"]) >
               int.from_bytes(bytes.fromhex("FF" * 32), "little"))):
        proof += 1
        tempHash = RandomX(header + proof.to_bytes(4, "little"))
        signature = privKey.sign(tempHash).serialize()
        tempHash = RandomX(tempHash + signature)

    rpc.call(
        "merit", "publishBlock", {
            "id":
            template["id"],
            "header":
            header.hex() + proof.to_bytes(4, "little").hex() + signature.hex()
        })
    if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header +
                                 proof.to_bytes(4, "little") + signature):
        raise TestError("Meros didn't broadcast back the BlockHeader.")
예제 #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.")
예제 #5
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.")
예제 #6
0
def test(rpc: RPC, address: Union[bytes, str], invalid: bool,
         msg: str) -> None:
    try:
        if isinstance(address, bytes):
            address = encodeAddress(address)
        rpc.call("personal", "send", [address, "1"])
        #Raise a TestError with a different code than expected to ensure the below check is run and fails.
        raise TestError("0 ")
    except TestError as e:
        if int(e.message.split(" ")[0]) != (-3 if invalid else 1):
            raise TestError(msg)
예제 #7
0
def verifyMeritRemoval(rpc: RPC, total: int, merit: int, holder: int,
                       pending: bool) -> None:
    sleep(1)

    if rpc.call("merit",
                "getTotalMerit") != total if pending else total - merit:
        raise TestError("Total Merit doesn't match.")

    if rpc.call("merit", "getMerit", {"nick": holder}) != {
            "status": "Unlocked",
            "malicious": pending,
            "merit": merit if pending else 0
    }:
        raise TestError("Holder's Merit doesn't match.")
예제 #8
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.")
def PersonalAuthorizationTest(rpc: RPC) -> None:
    #Test all these methods require authorization.
    #Doesn't test personal_data as that's not officially part of this test; just in it as a side note on key usage.
    #The actual personal_data test should handle that check.
    for method in [
            "setWallet", "setAccount", "getMnemonic", "getMeritHolderKey",
            "getMeritHolderNick", "getAccount", "getAddress", "send", "data",
            "getUTXOs", "getTransactionTemplate"
    ]:
        try:
            rpc.call("personal", method, auth=False)
            raise Exception()
        except Exception as e:
            if str(e) != "HTTP status isn't 200: 401":
                raise TestError("Could call personal_" + method +
                                " without authorization.")
예제 #10
0
def checkTemplate(rpc: RPC, mnemonic: str, req: Dict[str, Any],
                  inputs: List[Dict[str, Any]],
                  outputs: List[Dict[str, Any]]) -> None:
    template: Dict[str, Any] = rpc.call("personal", "getTransactionTemplate",
                                        req)
    if template["type"] != "Send":
        raise TestError("Template isn't of type Send.")
    if sortUTXOs(template["inputs"]) != sortUTXOs(inputs):
        raise TestError("Template inputs aren't as expected.")
    if template["outputs"] != outputs:
        raise TestError("Template outputs are incorrect.")

    keys: List[bytes] = []
    for inputJSON in template["inputs"]:
        key: bytes
        if inputJSON["change"]:
            key = getChangePublicKey(mnemonic, "", inputJSON["index"])
        else:
            key = getPublicKey(mnemonic, "", inputJSON["index"])
        if key not in keys:
            keys.append(key)

    if template["publicKey"] != Ristretto.aggregate(
        [Ristretto.RistrettoPoint(key)
         for key in keys]).serialize().hex().upper():
        if len(keys) == 1:
            raise TestError(
                "Template public key isn't correct when only a single key is present."
            )
        raise TestError("Public key aggregation isn't correct.")
예제 #11
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.")
예제 #12
0
def verifyDataDifficulty(
  rpc: RPC,
  dataDiff: int
) -> None:
  sleep(1)

  if rpc.call("consensus", "getDataDifficulty") != dataDiff:
    raise TestError("Data Difficulty doesn't match.")
예제 #13
0
def verifySendDifficulty(
  rpc: RPC,
  sendDiff: int
) -> None:
  #Sleep to ensure data races aren't a problem.
  sleep(1)

  if rpc.call("consensus", "getSendDifficulty") != sendDiff:
    raise TestError("Send Difficulty doesn't match.")
예제 #14
0
def test(rpc: RPC, address: Union[bytes, str], invalid: bool,
         msg: str) -> None:
    if isinstance(address, bytes):
        address = encodeAddress(address)

    try:
        rpc.call("transactions", "getBalance", {"address": address}, False)
        #If the call passed, and the address is invalid, raise.
        if invalid:
            raise MessageException(msg)
    except TestError as e:
        if int(e.message.split(" ")[0]) != -32602:
            raise Exception(
                "Non-ParamError was raised by this RPC call, which shouldn't be able to raise anything else."
            )
        if not invalid:
            raise TestError(msg)
    except MessageException as e:
        raise TestError(e.message)
예제 #15
0
def verifyMeritRemoval(
  rpc: RPC,
  total: int,
  merit: int,
  holder: int,
  pending: bool
) -> None:
  sleep(1)

  #Verify the total Merit.
  if rpc.call("merit", "getTotalMerit") != total if pending else total - merit:
    raise TestError("Total Merit doesn't match.")

  #Verify the holder's Merit.
  if rpc.call("merit", "getMerit", [holder]) != {
    "unlocked": True,
    "malicious": pending,
    "merit": merit if pending else 0
  }:
    raise TestError("Holder's Merit doesn't match.")
예제 #16
0
def GetDifficultyTest(
  rpc: RPC
) -> None:
  #Check the global difficulty.
  if rpc.call("consensus", "getSendDifficulty", auth=False) != 3:
    raise TestError("getSendDifficulty didn't reply properly.")
  if rpc.call("consensus", "getDataDifficulty", auth=False) != 5:
    raise TestError("getDataDifficulty didn't reply properly.")

  #Check the difficulties for a holder who doesn't exist.
  try:
    rpc.call("consensus", "getSendDifficulty", {"holder": 0}, False)
    raise TestError("")
  except TestError as e:
    if str(e) != "-2 Holder doesn't have a SendDifficulty.":
      raise TestError("getSendDifficulty didn't raise when asked about a non-existent holder.")

  try:
    rpc.call("consensus", "getDataDifficulty", {"holder": 0}, False)
    raise TestError("")
  except TestError as e:
    if str(e) != "-2 Holder doesn't have a DataDifficulty.":
      raise TestError("getDataDifficulty didn't raise when asked about a non-existent holder.")

  def voteAndVerify() ->  None:
    #Check the difficulties for a holder who has yet to vote.
    try:
      rpc.call("consensus", "getSendDifficulty", {"holder": 0}, False)
      raise TestError("")
    except TestError as e:
      if str(e) != "-2 Holder doesn't have a SendDifficulty.":
        raise TestError("getSendDifficulty didn't raise when asked about a holder who has yet to vote.")
    try:
      rpc.call("consensus", "getDataDifficulty", {"holder": 0}, False)
      raise TestError("")
    except TestError as e:
      if str(e) != "-2 Holder doesn't have a DataDifficulty.":
        raise TestError("getDataDifficulty didn't raise when asked about a holder who has yet to vote.")

    #Create the votes.
    sendDiff: SignedSendDifficulty = SignedSendDifficulty(6, 0)
    sendDiff.sign(0, PrivateKey(0))

    dataDiff: SignedDataDifficulty = SignedDataDifficulty(10, 1)
    dataDiff.sign(0, PrivateKey(0))

    #Send them.
    rpc.meros.signedElement(sendDiff)
    rpc.meros.signedElement(dataDiff)
    rpc.meros.live.recv()
    rpc.meros.live.recv()

    #Check them.
    if rpc.call("consensus", "getSendDifficulty", {"holder": 0}, False) != 6:
      raise TestError("getSendDifficulty didn't reply with the holder's current difficulty.")
    if rpc.call("consensus", "getDataDifficulty", {"holder": 0}, False) != 10:
      raise TestError("getDataDifficulty didn't reply with the holder's current difficulty.")

  with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
    Liver(rpc, json.loads(file.read())[:1], callbacks={1: voteAndVerify}).live()
예제 #17
0
def IntegerBoundTest(
  rpc: RPC
) -> None:
  #uint16.
  try:
    rpc.call("merit", "getPublicKey", {"nick": -1})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a negative integer for an unsigned integer.")

  try:
    rpc.call("merit", "getPublicKey", {"nick": 65536})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a too large unsigned integer.")

  #uint.
  try:
    rpc.call("merit", "getBlock", {"block": -1})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a negative integer for an unsigned integer.")

  try:
    rpc.call("merit", "getBlock", {"block": (2 ** 63)})
    raise TestError()
  except Exception as e:
    if str(e) != "-32700 Parse error.":
      raise TestError("Meros parsed an integer outside of the int64 bounds.")

  try:
    rpc.call("merit", "getBlock", {"block": (2 ** 64)})
    raise TestError()
  except Exception as e:
    if str(e) != "-32700 Parse error.":
      raise TestError("Meros parsed an integer outside of the uint64 bounds.")
예제 #18
0
def HundredSixtyTwoTest(rpc: RPC) -> None:
    #Create the first Datas.
    mnemonic: str = rpc.call("personal", "getMnemonic")
    abcData: str = rpc.call("personal", "data", {"data": "abc"})

    #Create a Data on a different account.
    rpc.call("personal", "setWallet")
    defData: str = rpc.call("personal", "data", {"data": "def"})

    #Verify the def Data was created.
    if rpc.call("transactions", "getTransaction",
                {"hash": defData})["descendant"] != "Data":
        raise TestError(
            "Meros didn't create a Data for an imported account when the existing account had Datas."
        )

    #Switch back to the old Mnemonic.
    rpc.call("personal", "setWallet", {"mnemonic": mnemonic})

    #Ensure we can create new Datas on it as well, meaning switching to a Mnemonic ports the chain.
    ghiDataHash: str = rpc.call("personal", "data", {"data": "ghi"})
    ghiData: Dict[str, Any] = rpc.call("transactions", "getTransaction",
                                       {"hash": ghiDataHash})
    del ghiData["signature"]
    del ghiData["proof"]
    if ghiData != {
            "descendant": "Data",
            "inputs": [{
                "hash": abcData
            }],
            "outputs": [],
            "hash": ghiDataHash,
            "data": b"ghi".hex()
    }:
        raise TestError(
            "Data created for an imported account with Datas isn't correct.")
예제 #19
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.")
예제 #20
0
def checkSend(rpc: RPC, sendHash: str, expected: Dict[str, Any]) -> None:
    send: Dict[str, Any] = rpc.call("transactions", "getTransaction",
                                    {"hash": sendHash})
    serialized: bytes = Send.fromJSON(send).serialize()
    del send["signature"]
    del send["proof"]

    expected["descendant"] = "Send"
    expected["hash"] = sendHash
    if sortUTXOs(send["inputs"]) != sortUTXOs(expected["inputs"]):
        raise TestError("Send inputs weren't as expected.")
    del send["inputs"]
    del expected["inputs"]
    if send != expected:
        raise TestError("Send wasn't as expected.")

    if rpc.meros.live.recv() != (MessageType.Send.toByte() + serialized):
        raise TestError("Meros didn't broadcast a Send it created.")
예제 #21
0
def checkData(rpc: RPC, dataHash: str, expected: bytes) -> str:
    data: Dict[str, Any] = rpc.call("transactions", "getTransaction",
                                    {"hash": dataHash})

    if len(data["inputs"]) != 1:
        raise TestError("Data had multiple inputs.")
    res: str = data["inputs"][0]["hash"]
    del data["inputs"]
    del data["signature"]
    del data["proof"]

    if data != {
            "descendant": "Data",
            "outputs": [],
            "hash": dataHash,
            "data": expected.hex().upper()
    }:
        raise TestError("Data wasn't as expected.")

    return res
예제 #22
0
def TwoHundredThirtyFiveTest(rpc: RPC) -> None:
    blockchain: Blockchain = Blockchain()
    dataFilter: SpamFilter = SpamFilter(5)

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

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

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

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

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

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

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

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

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

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

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

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

    if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back the Data.")
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't send us its SignedVerification.")
def HundredSixBlockElementsTest(rpc: RPC) -> None:
    vectors: Dict[str, Any]
    with open("e2e/Vectors/Consensus/HundredSix/BlockElements.json",
              "r") as file:
        vectors = json.loads(file.read())

    #Solely used to get the genesis Block hash.
    blockchain: Blockchain = Blockchain()
    transactions: Transactions = Transactions.fromJSON(vectors["transactions"])

    blocks: List[Block] = []
    for block in vectors["blocks"]:
        blocks.append(Block.fromJSON(block))

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

        #Send the Block.
        rpc.meros.liveBlockHeader(block.header)
        rpc.meros.handleBlockBody(block)

        #Flag of if the Block's Body synced.
        doneSyncing: bool = len(block.body.packets) == 0

        #Handle sync requests.
        reqHash: bytes = bytes()
        while True:
            if doneSyncing:
                #Sleep for a second so Meros handles the Block.
                sleep(1)

                #Try receiving from the Live socket, where Meros sends keep-alives.
                try:
                    if len(rpc.meros.live.recv()) != 0:
                        raise Exception()
                except TestError:
                    #Verify the node didn't crash.
                    try:
                        if rpc.call("merit", "getHeight") != 1:
                            raise Exception()
                    except Exception:
                        raise TestError(
                            "Node crashed after being sent a malformed Element."
                        )

                    #Since the node didn't crash, break out of this loop to trigger the next test case.
                    break
                except Exception:
                    raise TestError("Meros sent a keep-alive.")

            msg: bytes = rpc.meros.sync.recv()
            if MessageType(msg[0]) == MessageType.SketchHashRequests:
                if not block.body.packets:
                    raise TestError(
                        "Meros asked for Verification Packets from a Block without any."
                    )

                reqHash = msg[1:33]
                if reqHash != block.header.hash:
                    raise TestError(
                        "Meros asked for Verification Packets that didn't belong to the Block we just sent it."
                    )

                #Create a lookup of hash to packets.
                packets: Dict[int, VerificationPacket] = {}
                for packet in block.body.packets:
                    packets[Sketch.hash(block.header.sketchSalt,
                                        packet)] = packet

                #Look up each requested packet and respond accordingly.
                for h in range(int.from_bytes(msg[33:37], byteorder="little")):
                    sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 +
                                                         (h * 8)],
                                                     byteorder="little")
                    if sketchHash not in packets:
                        raise TestError(
                            "Meros asked for a non-existent Sketch Hash.")
                    rpc.meros.packet(packets[sketchHash])

                doneSyncing = True

            elif MessageType(msg[0]) == MessageType.TransactionRequest:
                reqHash = msg[1:33]

                if reqHash not in transactions.txs:
                    raise TestError(
                        "Meros asked for a non-existent Transaction.")

                rpc.meros.syncTransaction(transactions.txs[reqHash])

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

        #Reset the node so we can test the next invalid Block.
        rpc.reset()
예제 #24
0
def PartialArchiveTest(rpc: RPC) -> None:
    vectors: Dict[str, Any]
    with open("e2e/Vectors/Consensus/Verification/PartialArchive.json",
              "r") as file:
        vectors = json.loads(file.read())

    data: Data = Data.fromJSON(vectors["data"])
    svs: List[SignedVerification] = [
        SignedVerification.fromSignedJSON(vectors["verifs"][0]),
        SignedVerification.fromSignedJSON(vectors["verifs"][1])
    ]

    key: PrivateKey = PrivateKey(
        bytes.fromhex(rpc.call("personal", "getMiner")))

    def sendDataAndVerifications() -> None:
        if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
            raise TestError(
                "Meros didn't rebroadcast a Transaction we sent it.")
        for sv in svs:
            if rpc.meros.signedElement(sv) != rpc.meros.live.recv():
                raise TestError(
                    "Meros didn't rebroadcast a SignedVerification we sent it."
                )

        #As we don't have a quality RPC route for this, we need to use getTemplate.
        if bytes.fromhex(
                rpc.call("merit", "getBlockTemplate",
                         [key.toPublicKey().serialize().hex()
                          ])["header"])[36:68] != BlockHeader.createContents(
                              [VerificationPacket(data.hash, [0, 1])]):
            raise TestError(
                "New Block template doesn't have a properly created packet.")

    def verifyRecreation() -> None:
        template: Dict[str,
                       Any] = rpc.call("merit", "getBlockTemplate",
                                       [key.toPublicKey().serialize().hex()])
        if bytes.fromhex(
                template["header"])[36:68] != BlockHeader.createContents(
                    [VerificationPacket(data.hash, [1])]):
            raise TestError(
                "New Block template doesn't have a properly recreated packet.")

        #Mining it further verifies the internal state.
        header: bytes = bytes.fromhex(template["header"])
        proof: int = 0
        sig: bytes
        while True:
            initial: bytes = RandomX(header +
                                     proof.to_bytes(4, byteorder="little"))
            sig = key.sign(initial).serialize()
            final: bytes = RandomX(initial + sig)
            if (int.from_bytes(final, "little") *
                    template["difficulty"]) < int.from_bytes(
                        bytes.fromhex("FF" * 32), "little"):
                break
            proof += 1

        rpc.call("merit", "publishBlock", [
            template["id"],
            (header + proof.to_bytes(4, byteorder="little") + sig).hex()
        ])

        raise SuccessError(
            "Stop Liver from trying to verify the vector chain which doesn't have this Block."
        )

    #We may not want to use Liver here.
    #There's a very small Block count and we can't let it terminate (hence the SE).
    with raises(SuccessError):
        Liver(rpc,
              vectors["blockchain"],
              callbacks={
                  2: sendDataAndVerifications,
                  3: verifyRecreation
              }).live()
예제 #25
0
def PersonalDataTest(rpc: RPC) -> None:
    #Create a Data.
    firstData: str = rpc.call("personal", "data", {"data": "a"})
    initial: str = checkData(rpc, firstData, b"a")

    #Meros should've also created an initial Data.
    if checkData(rpc, initial, decodeAddress(rpc.call(
            "personal", "getAddress"))) != bytes(32).hex():
        raise TestError("Initial Data didn't have a 0-hash input.")

    #Create a Data using hex data. Also tests upper case hex.
    if checkData(rpc,
                 rpc.call("personal", "data", {
                     "data": "AABBCC",
                     "hex": True
                 }), b"\xAA\xBB\xCC") != firstData:
        raise TestError(
            "Newly created Data wasn't a descendant of the existing Data.")

    #Should support using 256 bytes of Data. Also tests lower case hex.
    checkData(
        rpc,
        rpc.call("personal", "data", {
            "data": bytes([0xaa] * 256).hex(),
            "hex": True
        }), bytes([0xaa] * 256))

    #Should properly error when we input no data. All Datas must have at least 1 byte of Data.
    try:
        rpc.call("personal", "data", {"data": ""})
        raise Exception()
    except Exception as e:
        if str(e) != "-3 Data is too small or too large.":
            raise TestError("Meros didn't handle Data that was too small.")

    #Should properly error when we supply more than 256 bytes of data.
    try:
        rpc.call("personal", "data", {"data": "a" * 257})
        raise Exception()
    except Exception as e:
        if str(e) != "-3 Data is too small or too large.":
            raise TestError("Meros didn't handle Data that was too large.")

    #Should properly error when we supply non-hex data with the hex flag.
    try:
        rpc.call("personal", "data", {"data": "zz", "hex": True})
        raise Exception()
    except Exception as e:
        if str(e) != "-3 Invalid hex char `z` (ord 122).":
            raise TestError("Meros didn't properly handle invalid hex.")

    #Should properly error when we supply non-even hex data.
    try:
        rpc.call("personal", "data", {"data": "a", "hex": True})
        raise Exception()
    except Exception as e:
        if str(e) != "-3 Incorrect hex string len.":
            raise TestError("Meros didn't properly handle non-even hex.")

    #Test Datas when the Wallet has a password.
    rpc.call("personal", "setWallet", {"password": "******"})

    #Shouldn't work due to the lack of a password.
    try:
        rpc.call("personal", "data", {"data": "abc"})
        raise Exception()
    except Exception as e:
        if str(e) != "-3 Invalid password.":
            raise TestError(
                "Meros didn't properly handle creating a Data without a password."
            )

    #Should work due to the existence of a password.
    lastData: str = rpc.call("personal", "data", {
        "data": "abc",
        "password": "******"
    })
    checkData(rpc, lastData, b"abc")

    #Reboot the node and verify we can create a new Data without issue.
    rpc.quit()
    sleep(3)
    rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc)
    if checkData(
            rpc,
            rpc.call("personal", "data", {
                "data": "def",
                "password": "******"
            }), b"def") != lastData:
        raise TestError("Couldn't create a new Data after rebooting.")
예제 #26
0
def StringBasedTypesTest(
  rpc: RPC
) -> None:
  edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
  edPubKey: bytes = edPrivKey.get_verifying_key()
  blsPubKey: PublicKey = PrivateKey(0).toPublicKey()

  #hex.
  #Test 0x-prefixed and no-0x both work without issue.
  data: Data = Data(bytes(32), edPubKey)
  data.sign(edPrivKey)
  rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": "0x" + data.serialize()[:-4].hex()})

  data = Data(data.hash, b"abc")
  data.sign(edPrivKey)
  rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": data.serialize()[:-4].hex()})

  #Test non-hex data is properly handled.
  try:
    rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": "az"})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted non-hex data for a hex argument.")

  #Hash.
  rpc.call("transactions", "getTransaction", {"hash": data.hash.hex()})
  rpc.call("transactions", "getTransaction", {"hash": "0x" + data.hash.hex()})

  #Also call the upper form, supplementing the above hex tests (as Hash routes through hex).
  rpc.call("transactions", "getTransaction", {"hash": data.hash.hex().upper()})
  rpc.call("transactions", "getTransaction", {"hash": "0x" + data.hash.hex().upper()})

  #Improper length.
  try:
    rpc.call("transactions", "getTransaction", {"hash": data.hash.hex().upper()[:-2]})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a hex string with an improper length as a Hash.")

  #BLSPublicKey.
  try:
    rpc.call("merit", "getNickname", {"key": blsPubKey.serialize().hex()})
    raise TestError()
  except Exception as e:
    if str(e) != "-2 Key doesn't have a nickname assigned.":
      raise TestError("Meros didn't accept a valid BLSPublicKey.")
  try:
    rpc.call("merit", "getNickname", {"key": blsPubKey.serialize().hex()[:-2]})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a hex string with an improper length as a BLSPublicKey.")

  #Missing flags.
  try:
    rpc.call("merit", "getNickname", {"key": "0" + blsPubKey.serialize().hex()[1]})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted an invalid BLSPublicKey as a BLSPublicKey.")

  #EdPublicKey.
  rpc.call("personal", "setAccount", {"key": edPubKey.hex(), "chainCode": bytes(32).hex()})
  try:
    rpc.call("personal", "setAccount", {"key": edPubKey[:-2].hex(), "chainCode": bytes(32).hex()})
    raise TestError()
  except Exception as e:
    if str(e) != "-32602 Invalid params.":
      raise TestError("Meros accepted a hex string with an improper length as an EdPublicKey.")
예제 #27
0
def EightyEightTest(
  rpc: RPC
) -> None:
  edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
  edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

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

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

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

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

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

  #Handle sync requests.
  reqHash: bytes = bytes()
  while True:
    msg: bytes = rpc.meros.sync.recv()
    if MessageType(msg[0]) == MessageType.BlockBodyRequest:
      reqHash = msg[1 : 33]
      if reqHash != block.header.hash:
        raise TestError("Meros asked for a Block Body that didn't belong to the Block we just sent it.")

      #Send the BlockBody.
      rpc.meros.blockBody(block)
      break

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

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

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

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

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

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

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

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

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

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

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

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

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

  verifyBlockchain(rpc, merit.blockchain)
예제 #28
0
def DerivationTest(rpc: RPC) -> None:
    #Start by testing BIP 32, 39, and 44 functionality in general.
    for _ in range(10):
        rpc.call("personal", "setWallet")
        verifyMnemonicAndAccount(rpc)

    #Set specific Mnemonics and ensure they're handled properly.
    for _ in range(10):
        mnemonic: str = getMnemonic()
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        verifyMnemonicAndAccount(rpc, mnemonic)

    #Create Mnemonics with passwords and ensure they're handled properly.
    for _ in range(10):
        password: str = os.urandom(32).hex()
        rpc.call("personal", "setWallet", {"password": password})
        verifyMnemonicAndAccount(rpc, password=password)

    #Set specific Mnemonics with passwords and ensure they're handled properly.
    for i in range(10):
        password: str = os.urandom(32).hex()
        #Non-hex string.
        if i == 0:
            password = "******"
        mnemonic: str = getMnemonic(password)
        rpc.call("personal", "setWallet", {
            "mnemonic": mnemonic,
            "password": password
        })
        verifyMnemonicAndAccount(rpc, mnemonic, password)

    #setWallet, getMnemonic, getMeritHolderKey, getMeritHolderNick's non-existent case, and getAccount have now been tested.
    #This leaves getAddress with specific indexes.

    #Clear the Wallet.
    rpc.call("personal", "setWallet")

    #Start by testing specific derivation.
    password: str = "password since it shouldn't be relevant"
    for _ in range(10):
        mnemonic: str = getMnemonic(password)
        index: int = 100
        key: bytes
        while True:
            try:
                key = BIP32.derive(
                    sha256(Bip39SeedGenerator(mnemonic).Generate(
                        password)).digest(), [
                            44 + (1 << 31), 5132 + (1 << 31), 0 +
                            (1 << 31), 0, index
                        ])
                break
            except Exception:
                index += 1

        rpc.call("personal", "setWallet", {
            "mnemonic": mnemonic,
            "password": password
        })
        addr: str = bech32_encode(
            "mr",
            convertbits(
                bytes([0]) + RistrettoScalar(key[:32]).toPoint().serialize(),
                8, 5))
        if rpc.call("personal", "getAddress", {"index": index}) != addr:
            raise TestError("Didn't get the correct address for this index.")

    #Test if a specific address is requested, it won't come up naturally.
    #This isn't explicitly required by the RPC spec, which has been worded carefully to leave this open ended.
    #The only requirement is the address was never funded and the index is sequential (no moving backwards).
    #The node offers this feature to try to make mixing implicit/explicit addresses safer, along with some internal benefits.
    #That said, said internal benefits are minimal or questionable, hence why the RPC docs are open ended.
    #This way we can decide differently in the future.
    rpc.call("personal", "setWallet")
    firstAddr: str = rpc.call("personal", "getAddress")
    #Explicitly get the first address.
    for i in range(256):
        try:
            rpc.call("personal", "getAddress", {"index": i})
            break
        except TestError:
            if i == 255:
                raise Exception(
                    "The first 256 address were invalid; this should be practically impossible."
                )
    if firstAddr == rpc.call("personal", "getAddress"):
        raise TestError("Explicitly grabbed address was naturally returned.")

    #Test error cases.

    #Mnemonic with an improper amount of entropy.
    #Runs multiple times in case the below error pops up for the sole reason the Mnemonic didn't have viable keys.
    #This should error earlier than that though.
    for _ in range(16):
        try:
            rpc.call(
                "personal", "setWallet", {
                    "mnemonic":
                    Bip39MnemonicGenerator.FromWordsNumber(
                        Bip39WordsNum.WORDS_NUM_12)
                })
            raise Exception()
        except Exception as e:
            if str(e) != "-3 Invalid mnemonic or password.":
                raise TestError(
                    "Could set a Mnemonic with too little entropy.")

    #Mnemonic with additional spaces.
    rpc.call("personal", "setWallet")
    mnemonic: str = rpc.call("personal", "getMnemonic")
    rpc.call("personal", "setWallet",
             {"mnemonic": "   " + (" " * 2).join(mnemonic.split(" ")) + " "})
    if rpc.call("personal", "getMnemonic") != mnemonic:
        raise TestError(
            "Meros didn't handle a mnemonic with extra whitespace.")

    #Negative index to getAddress.
    try:
        rpc.call("personal", "getAddress", {"index": -1})
        raise Exception()
    except Exception as e:
        if str(e) != "-32602 Invalid params.":
            raise TestError("Could call getAddress with a negative index.")
예제 #29
0
def verifyMnemonicAndAccount(rpc: RPC,
                             mnemonic: str = "",
                             password: str = "") -> None:
    #If a Mnemonic wasn't specified, grab the node's.
    if mnemonic == "":
        mnemonic = rpc.call("personal", "getMnemonic")

    #Verify Mnemonic equivalence.
    if mnemonic != rpc.call("personal", "getMnemonic"):
        raise TestError("Node had a different Mnemonic.")

    #Validate it.
    if not Bip39MnemonicValidator(mnemonic).Validate():
        raise TestError("Mnemonic checksum was incorrect.")

    #Verify derivation from seed to wallet.
    seed: bytes = Bip39SeedGenerator(mnemonic).Generate(password)
    #Check the Merit Holder key.
    if rpc.call("personal", "getMeritHolderKey") != PrivateKey(
            seed[:32]).serialize().hex().upper():
        raise TestError("Meros generated a different Merit Holder Key.")
    #Verify getting the Merit Holder nick errors.
    try:
        rpc.call("personal", "getMeritHolderNick")
    except TestError as e:
        if e.message != "-2 Wallet doesn't have a Merit Holder nickname assigned.":
            raise TestError("getMeritHolderNick didn't error.")

    #Hash the seed again for the wallet seed (first is the Merit Holder seed).
    seed = sha256(seed).digest()

    #Derive the first account.
    extendedKey: bytes
    chainCode: bytes
    try:
        extendedKey, chainCode = BIP32.deriveKeyAndChainCode(
            seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31)])
    except Exception:
        raise TestError(
            "Meros gave us an invalid Mnemonic to derive (or the test generated an unusable one)."
        )

    if rpc.call("personal", "getAccount") != {
            "key":
            RistrettoScalar(
                extendedKey[:32]).toPoint().serialize().hex().upper(),
            "chainCode":
            chainCode.hex().upper()
    }:
        raise TestError("Meros generated a different account public key.")

    #Also test that the correct public key is used when creating Datas.
    #It should be the first public key of the external chain for account 0.
    data: str = rpc.call("personal", "data", {
        "data": "a",
        "password": password
    })
    initial: Data = Data(
        bytes(32),
        RistrettoScalar(getPrivateKey(mnemonic, password,
                                      0)[:32]).toPoint().serialize())
    #Checks via the initial Data.
    if bytes.fromhex(
            rpc.call("transactions", "getTransaction",
                     {"hash": data})["inputs"][0]["hash"]) != initial.hash:
        raise TestError(
            "Meros used the wrong key to create the Data Transactions.")
예제 #30
0
def HundredFiftyFiveTest(rpc: RPC) -> None:
    edPrivKeys: List[ed25519.SigningKey] = [
        ed25519.SigningKey(b'\0' * 32),
        ed25519.SigningKey(b'\1' * 32)
    ]
    edPubKeys: List[ed25519.VerifyingKey] = [
        edPrivKeys[0].get_verifying_key(), edPrivKeys[1].get_verifying_key()
    ]

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

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

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

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

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

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

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

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

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

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

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

        verif: SignedVerification = SignedVerification(datas[d].hash)
        verif.sign(0, blsPrivKey)
        if res[1:] != verif.signedSerialize():
            raise TestError(
                "Meros didn't send the correct SignedVerification.")