def fromSignedJSON(jsonArg: Dict[str, Any]) -> Any:
        json: Dict[str, Any] = dict(jsonArg)
        json["elements"] = list(json["elements"])
        json["elements"][0] = dict(json["elements"][0])
        json["elements"][1] = dict(json["elements"][1])
        json["elements"][0]["holder"] = json["holder"]
        json["elements"][1]["holder"] = json["holder"]

        e1: MeritRemovalElement = SignedVerification(bytes(32), 0)
        if json["elements"][0]["descendant"] == "Verification":
            e1 = SignedVerification.fromSignedJSON(json["elements"][0])
        elif json["elements"][0]["descendant"] == "VerificationPacket":
            e1 = SignedMeritRemovalVerificationPacket.fromSignedJSON(
                json["elements"][0])
        elif json["elements"][0]["descendant"] == "SendDifficulty":
            e1 = SignedSendDifficulty.fromSignedJSON(json["elements"][0])
        elif json["elements"][0]["descendant"] == "DataDifficulty":
            e1 = SignedDataDifficulty.fromSignedJSON(json["elements"][0])

        e2: MeritRemovalElement = SignedVerification(bytes(32), 0)
        if json["elements"][1]["descendant"] == "Verification":
            e2 = SignedVerification.fromSignedJSON(json["elements"][1])
        elif json["elements"][1]["descendant"] == "VerificationPacket":
            e2 = SignedMeritRemovalVerificationPacket.fromSignedJSON(
                json["elements"][1])
        elif json["elements"][1]["descendant"] == "SendDifficulty":
            e2 = SignedSendDifficulty.fromSignedJSON(json["elements"][1])
        elif json["elements"][1]["descendant"] == "DataDifficulty":
            e2 = SignedDataDifficulty.fromSignedJSON(json["elements"][1])

        return SignedMeritRemoval(e1, e2, json["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.")
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.")
Exemple #4
0
def signElement(elem: Element) -> Signature:
    if isinstance(elem, Verification):
        verif: SignedVerification = SignedVerification(elem.hash, elem.holder)
        verif.sign(elem.holder, PrivateKey(elem.holder))
        return verif.signature

    if isinstance(elem, SignedMeritRemovalVerificationPacket):
        return elem.signature

    if isinstance(elem, SendDifficulty):
        sendDiff: SignedSendDifficulty = SignedSendDifficulty(
            elem.difficulty, elem.nonce)
        sendDiff.sign(elem.holder, PrivateKey(elem.holder))
        return sendDiff.signature

    if isinstance(elem, DataDifficulty):
        dataDiff: SignedDataDifficulty = SignedDataDifficulty(
            elem.difficulty, elem.nonce)
        dataDiff.sign(elem.holder, PrivateKey(elem.holder))
        return dataDiff.signature

    if isinstance(elem, MeritRemoval):
        result: Signature = signElement(elem.e2)
        if not elem.partial:
            result = Signature.aggregate([result, signElement(elem.e1)])
        return result

    raise GenerationError(
        "Tried to sign an Element in a Block we didn't recognize the type of.")
def HundredTwentyTest(
  rpc: RPC
) -> None:
  file: IO[Any] = open("e2e/Vectors/Consensus/MeritRemoval/HundredTwenty.json", "r")
  vectors: Dict[str, Any] = json.loads(file.read())
  file.close()

  #DataDifficulty for the mempool.
  mempoolDataDiff: SignedDataDifficulty = SignedDataDifficulty.fromSignedJSON(vectors["mempoolDataDiff"])
  #DataDifficulty for the Blockchain.
  blockchainDataDiff: DataDifficulty = DataDifficulty.fromJSON(vectors["blockchainDataDiff"])

  def sendDataDifficulty() -> None:
    #Send the Data Difficulty for the mempool.
    rpc.meros.signedElement(mempoolDataDiff)

    #Verify its sent back.
    if rpc.meros.live.recv() != (
      MessageType.SignedDataDifficulty.toByte() +
      mempoolDataDiff.signedSerialize()
    ):
      raise TestError("Meros didn't send us the mempool Data Difficulty.")

  def receiveMeritRemoval() -> None:
    #We should receive a MeritRemoval, which is partial.
    #The unsigned Element should be the Block's DataDifficulty.
    #The signed Element should be the mempool's DataDifficulty.
    if rpc.meros.live.recv() != (
      MessageType.SignedMeritRemoval.toByte() +
      PartialMeritRemoval(
        blockchainDataDiff,
        mempoolDataDiff,
        0
      ).signedSerialize()
    ):
      raise TestError("Meros didn't create the partial Merit Removal.")

    #Verify Meros didn't just broadcast it, yet also added it.
    verifyMeritRemoval(rpc, 2, 2, 0, True)

  Liver(
    rpc,
    vectors["blockchain"],
    callbacks={
      1: sendDataDifficulty,
      2: receiveMeritRemoval
    }
  ).live()
Exemple #6
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.")
Exemple #7
0
def signElement(elem: Element) -> Signature:
    if isinstance(elem, Verification):
        verif: SignedVerification = SignedVerification(elem.hash, elem.holder)
        verif.sign(elem.holder, PrivateKey(elem.holder))
        return verif.signature
    if isinstance(elem, SendDifficulty):
        sendDiff: SignedSendDifficulty = SignedSendDifficulty(
            elem.difficulty, elem.nonce)
        sendDiff.sign(elem.holder, PrivateKey(elem.holder))
        return sendDiff.signature
    if isinstance(elem, DataDifficulty):
        dataDiff: SignedDataDifficulty = SignedDataDifficulty(
            elem.difficulty, elem.nonce)
        dataDiff.sign(elem.holder, PrivateKey(elem.holder))
        return dataDiff.signature

    raise GenerationError(
        "Tried to sign an Element in a Block we didn't recognize the type of.")
Exemple #8
0
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

#Generate a Block granting the holder Merit.
block = Block(
    BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32),
                blsPubKey.serialize(),
                blockchain.blocks[-1].header.time + 1200), BlockBody())
#Mine it.
block.mine(blsPrivKey, blockchain.difficulty())

#Add it.
blockchain.add(block)
print("Generated Hundred Twenty Block " + str(len(blockchain.blocks)) + ".")

#Create a DataDifficulty.
dataDiff: SignedDataDifficulty = SignedDataDifficulty(3, 0)
dataDiff.sign(0, blsPrivKey)

#Create a conflicting DataDifficulty with the same nonce.
dataDiffConflicting = SignedDataDifficulty(1, 0)
dataDiffConflicting.sign(0, blsPrivKey)

#Generate a Block containing the competing Data difficulty.
block = Block(
    BlockHeader(0, blockchain.last(),
                BlockHeader.createContents([], [dataDiffConflicting]), 1,
                bytes(4), bytes(32), 0,
                blockchain.blocks[-1].header.time + 1200),
    BlockBody([], [dataDiffConflicting], dataDiffConflicting.signature))
#Mine it.
block.mine(blsPrivKey, blockchain.difficulty())
Exemple #9
0
from e2e.Libs.BLS import PrivateKey, PublicKey

from e2e.Classes.Consensus.DataDifficulty import SignedDataDifficulty
from e2e.Classes.Consensus.MeritRemoval import SignedMeritRemoval

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

blsPrivKey: PrivateKey = PrivateKey(0)
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

proto: PrototypeChain = PrototypeChain(1, False)

#Create conflicting Data Difficulties.
dataDiffs: List[SignedDataDifficulty] = [
    SignedDataDifficulty(3, 0),
    SignedDataDifficulty(4, 0)
]
dataDiffs[0].sign(0, blsPrivKey)
dataDiffs[1].sign(0, blsPrivKey)

#Create a MeritRemoval out of the conflicting Data Difficulties.
mr: SignedMeritRemoval = SignedMeritRemoval(dataDiffs[0], dataDiffs[1])
proto.add(elements=[mr])

#Create a MeritRemoval with the Elements swapped.
swapped: SignedMeritRemoval = SignedMeritRemoval(dataDiffs[1], dataDiffs[0])
proto.add(elements=[swapped])

result: Dict[str, Any] = {"blockchain": proto.toJSON()}
vectors: IO[Any] = open(
Exemple #10
0
proto: PrototypeChain = PrototypeChain(1, False)

blsPrivKey: PrivateKey = PrivateKey(0)
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

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

#Create the SendDifficulty MR.
sendDiff: SignedSendDifficulty = SignedSendDifficulty(4, 0)
sendDiff.sign(0, blsPrivKey)
sendDiffMR: SignedMeritRemoval = SignedMeritRemoval(sendDiff, sendDiff)

#Create the DataDifficulty MR.
dataDiff: SignedDataDifficulty = SignedDataDifficulty(4, 0)
dataDiff.sign(0, blsPrivKey)
dataDiffMR: SignedMeritRemoval = SignedMeritRemoval(dataDiff, dataDiff)

#Create the Verification MR.
data: Data = Data(bytes(32), edPubKey.to_bytes())
data.sign(edPrivKey)
data.beat(SpamFilter(5))
verif: SignedVerification = SignedVerification(data.hash)
verif.sign(0, blsPrivKey)

#Transform the Verification to a SignedMeritRemovalVerificationPacket.
packet: SignedMeritRemovalVerificationPacket = SignedMeritRemovalVerificationPacket(
  SignedVerificationPacket(data.hash),
  [blsPubKey.serialize()],
  verif.signature
Exemple #11
0
def TElementTest(
  rpc: RPC
) -> None:
  file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
  blocks: List[Dict[str, Any]] = json.loads(file.read())
  file.close()
  merit: Merit = Merit()

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

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

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

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

      rpc.meros.blockBody(block)
      break

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  verifyBlockchain(rpc, merit.blockchain)
Exemple #12
0
def getBlockTemplateTest(rpc: RPC) -> None:
    edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    edPubKey: bytes = edPrivKey.get_verifying_key()
    blockchain: Blockchain = Blockchain()

    #Get multiple templates to verify they share an ID if they're requested within the same second.
    templates: List[Dict[str, Any]] = []
    startTime: float = nextSecond()
    for k in range(5):
        templates.append(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(k)},
                     False))
    if int(startTime) != int(time.time()):
        #Testing https://github.com/MerosCrypto/Meros/issues/278 has a much more forgiving timer of < 1 second each.
        #That said, this test was written on the fair assumption of all the above taking place in a single second.
        raise Exception(
            "getBlockTemplate is incredibly slow, to the point an empty Block template takes > 0.2 seconds to grab, invalidating this test."
        )

    for k, template in zip(range(5), templates):
        if template["id"] != int(startTime):
            raise TestError("Template ID isn't the time.")

        #Also check general accuracy.
        if bytes.fromhex(template["key"]) != blockchain.genesis:
            raise TestError("Template has the wrong RandomX key.")

        bytesHeader: bytes = bytes.fromhex(template["header"])
        serializedHeader: bytes = BlockHeader(
            0, blockchain.blocks[0].header.hash, bytes(32), 0, bytes(4),
            bytes(32),
            PrivateKey(k).toPublicKey().serialize(),
            int(startTime)).serialize()[:-52]
        #Skip over the randomized sketch salt.
        if (bytesHeader[:72] + bytesHeader[76:]) != (serializedHeader[:72] +
                                                     serializedHeader[76:]):
            raise TestError("Template has an invalid header.")
        #Difficulty modified as this is a new miner.
        if template["difficulty"] != (blockchain.difficulty() * 11 // 10):
            raise TestError("Template's difficulty is wrong.")

    currTime: int = int(nextSecond())
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": getMiner(0)}, False)
    if template["id"] != currTime:
        raise TestError("Template ID wasn't advanced with the time.")

    #Override the ID to enable easy comparison against a historical template.
    template["id"] = int(startTime)

    if int.from_bytes(bytes.fromhex(template["header"])[-4:],
                      "little") != currTime:
        raise TestError("The header has the wrong time.")
    template["header"] = (
        bytes.fromhex(template["header"])[:72] +
        #Use the header we'll compare to's salt.
        bytes.fromhex(templates[0]["header"])[72:76] +
        bytes.fromhex(template["header"])[76:-4] +
        #Also use its time.
        int(startTime).to_bytes(4, "little")).hex().upper()

    if template != templates[0]:
        raise TestError(
            "Template, minus the time difference, doesn't match the originally provided template."
        )

    #Test that the templates are deleted whenever a new Block appears.
    #This is done by checking the error given when we use an old template.
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        block: Block = Block.fromJSON(json.loads(file.read())[0])
        blockchain.add(block)
        rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
        rpc.meros.syncConnect(blockchain.blocks[0].header.hash)
        rpc.meros.liveBlockHeader(block.header)
        rpc.meros.rawBlockBody(block, 0)
        time.sleep(1)
    #Sanity check.
    if rpc.call("merit", "getHeight", auth=False) != 2:
        raise Exception("Didn't successfully send Meros the Block.")

    #Get a new template so Meros realizes the template situation has changed.
    rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)}, False)

    try:
        rpc.call("merit", "publishBlock", {
            "id": int(startTime),
            "header": ""
        }, False)
        raise Exception("")
    except Exception as e:
        if str(e) != "-2 Invalid ID.":
            raise TestError("Meros didn't delete old template IDs.")

    #Test VerificationPacket inclusion.
    data: Data = Data(bytes(32), edPubKey)
    data.sign(edPrivKey)
    data.beat(SpamFilter(5))
    verif: SignedVerification = SignedVerification(data.hash)
    verif.sign(0, PrivateKey(0))
    packet = VerificationPacket(data.hash, [0])

    rpc.meros.liveTransaction(data)
    rpc.meros.signedElement(verif)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet]):
        raise TestError(
            "Meros didn't include the Verification in its new template.")

    #Test Element inclusion.
    sendDiff: SignedSendDifficulty = SignedSendDifficulty(0, 0)
    sendDiff.sign(0, PrivateKey(0))
    rpc.meros.signedElement(sendDiff)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet], [sendDiff]):
        raise TestError(
            "Meros didn't include the Element in its new template.")

    #The 88 test checks for the non-inclusion of Verifications with unmentioned predecessors.
    #Test for non-inclusion of Elements with unmentioned predecessors.
    sendDiffChild: SignedSendDifficulty = SignedSendDifficulty(0, 2)
    sendDiffChild.sign(0, PrivateKey(0))
    rpc.meros.signedElement(sendDiffChild)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet], [sendDiff]):
        raise TestError(
            "Meros did include an Element with an unmentioned parent in its new template."
        )

    #If we send a block with a time in the future, yet within FTL (of course), make sure Meros can still generate a template.
    #Naively using the current time will create a negative clock, which isn't allowed.
    #Start by closing the sockets to give us time to work.
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()
    #Sleep to reset the connection state.
    time.sleep(35)

    #Create and mine the Block.
    header: BlockHeader = BlockHeader(
        0,
        blockchain.blocks[-1].header.hash,
        bytes(32),
        0,
        bytes(4),
        bytes(32),
        PrivateKey(0).toPublicKey().serialize(),
        0,
    )
    miningStart: int = 0
    #If this block takes longer than 10 seconds to mine, try another.
    #Low future time (20 seconds) is chosen due to feasibility + supporting lowering the FTL in the future.
    while time.time() > miningStart + 10:
        miningStart = int(time.time())
        header = BlockHeader(
            0,
            blockchain.blocks[-1].header.hash,
            bytes(32),
            0,
            bytes(4),
            bytes(32),
            #Mod by something is due to a 2-byte limit (IIRC -- Kayaba).
            #100 is just clean. +11 ensures an offset from the above, which really shouldn't be necessary.
            #If we did need one, +1 should work, as we only ever work with PrivateKey(0) on the blockchain.
            PrivateKey((miningStart % 100) + 10).toPublicKey().serialize(),
            int(time.time()) + 20,
        )
        header.mine(PrivateKey((miningStart % 100) + 10),
                    blockchain.difficulty() * 11 // 10)
    blockchain.add(Block(header, BlockBody()))

    #Send it and verify it.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)
    rpc.meros.liveBlockHeader(header)
    rpc.meros.rawBlockBody(Block(header, BlockBody()), 0)
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()
    time.sleep(1)

    #Ensure a stable template ID.
    currTime = int(nextSecond())
    template = rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                        False)
    if template["id"] != currTime:
        raise TestError(
            "Template ID isn't the time when the previous Block is in the future."
        )
    if int.from_bytes(bytes.fromhex(template["header"])[-4:],
                      "little") != (header.time + 1):
        raise TestError(
            "Meros didn't handle generating a template off a Block in the future properly."
        )

    #Verify a Block with three Elements from a holder, where two form a Merit Removal.
    #Only the two which cause a MeritRemoval should be included.
    #Mine a Block to a new miner and clear the current template with it (might as well).
    #Also allows us to test template clearing.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": getMiner(1)}, False)
    #Mine the Block.
    proof: int = -1
    tempHash: bytes = bytes()
    tempSignature: bytes = bytes()
    while ((proof == -1)
           or ((int.from_bytes(tempHash, "little") *
                (blockchain.difficulty() * 11 // 10)) > int.from_bytes(
                    bytes.fromhex("FF" * 32), "little"))):
        proof += 1
        tempHash = RandomX(
            bytes.fromhex(template["header"]) + proof.to_bytes(4, "little"))
        tempSignature = PrivateKey(1).sign(tempHash).serialize()
        tempHash = RandomX(tempHash + tempSignature)
    rpc.call(
        "merit", "publishBlock", {
            "id":
            template["id"],
            "header":
            template["header"] + proof.to_bytes(4, "little").hex() +
            tempSignature.hex()
        })
    time.sleep(1)

    #Verify the template was cleared.
    currTime = int(nextSecond())
    bytesHeader: bytes = bytes.fromhex(
        rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                 False)["header"])
    serializedHeader: bytes = BlockHeader(
        0,
        tempHash,
        bytes(32),
        0,
        bytes(4),
        bytes(32),
        0,
        #Ensures that the previous time manipulation doesn't come back to haunt us.
        max(currTime, blockchain.blocks[-1].header.time + 1)).serialize()[:-52]
    #Skip over the randomized sketch salt and time (which we don't currently have easy access to).
    if (bytesHeader[:72] + bytesHeader[76:-4]) != (serializedHeader[:72] +
                                                   serializedHeader[76:-4]):
        raise TestError("Template wasn't cleared.")

    #Sleep so we can reconnect.
    time.sleep(35)
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)

    #Finally create the Elements.
    dataDiff: SignedDataDifficulty = SignedDataDifficulty(1, 0)
    dataDiff.sign(2, PrivateKey(1))
    rpc.meros.signedElement(dataDiff)
    sendDiffs: List[SignedSendDifficulty] = [
        SignedSendDifficulty(1, 1),
        SignedSendDifficulty(2, 1)
    ]
    for sd in sendDiffs:
        sd.sign(2, PrivateKey(1))
        rpc.meros.signedElement(sd)
    time.sleep(1)

    #`elem for elem` is used below due to Pyright not handling inheritance properly when nested.
    #pylint: disable=unnecessary-comprehension
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [], [elem for elem in sendDiffs[::-1]]):
        raise TestError(
            "Meros didn't include just the malicious Elements in its new template."
        )
Exemple #13
0
def TElementTest(rpc: RPC) -> None:
    merit: Merit = Merit()

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

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

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

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

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

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

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

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

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

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

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

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

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

    verifyBlockchain(rpc, merit.blockchain)
Exemple #14
0
blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest())
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

#Mine 24 more Blocks so a vote is earned on the Block after this.
for _ in range(24):
    block = Block(
        BlockHeader(0, blockchain.last(), bytes(32), 0, bytes(4), bytes(32), 0,
                    blockchain.blocks[-1].header.time + 1200), BlockBody())
    block.mine(blsPrivKey, blockchain.difficulty())
    blockchain.add(block)

#Now that we have a vote, submit a Block which has two DataDifficulty updates in the same Block.
#The first Element should have a higher nonce than the second.
#This will verify Meros properly handles out-of-order Elements included in a Block.
dataDiffs: List[SignedDataDifficulty] = [
    SignedDataDifficulty(1, 1),
    SignedDataDifficulty(2, 0)
]
for dataDiff in dataDiffs:
    dataDiff.sign(0, blsPrivKey)

for _ in range(2):
    block = Block(
        BlockHeader(
            0,
            blockchain.last(),
            #Used so the type checkers realize List[SignedDataDifficulty] is a viable List[Element].
            #pylint: disable=unnecessary-comprehension
            BlockHeader.createContents([], [e for e in dataDiffs]),
            0,
            bytes(4),
Exemple #15
0
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

#Generate a Block granting the holder Merit.
block = Block(
    BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32),
                blsPubKey.serialize(),
                blockchain.blocks[-1].header.time + 1200), BlockBody())
#Mine it.
block.mine(blsPrivKey, blockchain.difficulty())

#Add it.
blockchain.add(block)
print("Generated Repeat Block " + str(len(blockchain.blocks)) + ".")

#Create a DataDifficulty.
dataDiff: SignedDataDifficulty = SignedDataDifficulty(3, 0)
dataDiff.sign(0, blsPrivKey)

#Generate a Block containing the DataDifficulty.
block = Block(
    BlockHeader(0, blockchain.last(),
                BlockHeader.createContents([], [dataDiff]), 1, bytes(4),
                bytes(32), 0, blockchain.blocks[-1].header.time + 1200),
    BlockBody([], [dataDiff], dataDiff.signature))
#Mine it.
block.mine(blsPrivKey, blockchain.difficulty())

#Add it.
blockchain.add(block)
print("Generated Repeat Block " + str(len(blockchain.blocks)) + ".")
Exemple #16
0
import json

from e2e.Libs.BLS import PrivateKey, PublicKey

from e2e.Classes.Consensus.DataDifficulty import SignedDataDifficulty
from e2e.Classes.Consensus.MeritRemoval import SignedMeritRemoval

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

proto: PrototypeChain = PrototypeChain(1, False)

blsPrivKey: PrivateKey = PrivateKey(0)
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

#Create a DataDifficulty.
dataDiff: SignedDataDifficulty = SignedDataDifficulty(3, 0)
dataDiff.sign(0, blsPrivKey)

#Create a conflicting DataDifficulty with the same nonce.
dataDiffConflicting: SignedDataDifficulty = SignedDataDifficulty(1, 0)
dataDiffConflicting.sign(0, blsPrivKey)

#Create a MeritRemoval out of the two of them.
mr: SignedMeritRemoval = SignedMeritRemoval(dataDiff, dataDiffConflicting)
proto.add(elements=[dataDiff, dataDiffConflicting])

with open("e2e/Vectors/Consensus/MeritRemoval/SameNonce.json", "w") as vectors:
    vectors.write(
        json.dumps({
            "blockchain": proto.toJSON(),
            "removal": mr.toSignedJSON()