def sendBlock() -> None: #Send the Block with the MeritRemoval archived again. block: Block = Block.fromJSON(vectors[-1]) rpc.meros.liveBlockHeader(block.header) #Flag of if the Block's Body synced. blockBodySynced: bool = False #Handle sync requests. reqHash: bytes = bytes() while True: if blockBodySynced: #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: raise SuccessError("Meros didn't add the same MeritRemoval twice.") except Exception: raise TestError("Meros sent a keep-alive.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1 : 33] if reqHash != block.header.hash: raise TestError("Meros asked for a Block Body that didn't belong to the Block we just sent it.") #Send the BlockBody. blockBodySynced = True rpc.meros.blockBody(block) else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def sendMeritRemoval() -> None: #Send the Datas. for data in datas: if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send us the Data.") #Send the Block containing the modified Merit Removal. block: Block = Block.fromJSON(vectors["blockchains"][i][-1]) rpc.meros.liveBlockHeader(block.header) #Flag of if the Block's Body synced. blockBodySynced: bool = False #Handle sync requests. reqHash: bytes = bytes() while True: if blockBodySynced: #Sleep for a second so Meros handles the Block. sleep(1) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the height is 2. #The genesis Block and the Block granting Merit. try: if rpc.call("merit", "getHeight") != 2: raise Exception() except Exception: raise TestError( "Node added a Block containg a repeat MeritRemoval." ) #Since the node didn't add the Block, raise SuccessError. raise SuccessError( "Node didn't add a Block containing a repeat MeritRemoval." ) except Exception: raise TestError("Meros sent a keep-alive.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the Block we just sent it." ) #Send the BlockBody. blockBodySynced = True rpc.meros.blockBody(block) else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def requestWithCapacity() -> None: block: Block = Block.fromJSON(vectors["blockchain"][-1]) #Request 3/6 Transactions. rpc.meros.sync.send(MessageType.BlockBodyRequest.toByte() + block.header.hash + (3).to_bytes(4, "little")) if rpc.meros.sync.recv() != (MessageType.BlockBody.toByte() + block.body.serialize(block.header.sketchSalt, 3)): raise TestError("Meros didn't respond with the requested capacity.") #Request 8/6 Transactions. rpc.meros.sync.send(MessageType.BlockBodyRequest.toByte() + block.header.hash + (8).to_bytes(4, "little")) if rpc.meros.sync.recv() != (MessageType.BlockBody.toByte() + block.body.serialize(block.header.sketchSalt, 6)): raise TestError("Meros didn't respond with the requested capacity (normalized).")
def __init__(self) -> None: self.genesis: bytes = b"MEROS_DEVELOPER_NETWORK".ljust(32, b'\0') self.upcomingKey: bytes = self.genesis setRandomXKey(self.upcomingKey) self.blockTime: int = 60 self.difficulties: List[int] = [100] self.keys: Dict[bytes, int] = {} self.blocks: List[Block] = [ Block( BlockHeader(0, self.genesis, bytes(32), 0, bytes(4), bytes(32), PublicKey().serialize(), 0), BlockBody()) ]
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." )
def HundredSeventySevenTest( rpc: RPC ) -> None: #Grab the keys. blsPrivKey: PrivateKey = PrivateKey(bytes.fromhex(rpc.call("personal", "getMiner"))) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Faux Blockchain used to calculate the difficulty. blockchain: Blockchain = Blockchain() #Mine 8 Blocks. #The first creates the initial Data. #The next 6 pop it from the Epochs. #One more is to verify the next is popped as well. for b in range(0, 8): template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey.serialize().hex()]) template["header"] = bytes.fromhex(template["header"]) header: BlockHeader = BlockHeader( 0, template["header"][4 : 36], template["header"][36 : 68], int.from_bytes(template["header"][68 : 70], byteorder="big"), template["header"][70 : 74], template["header"][74 : 106], 0, int.from_bytes(template["header"][-4:], byteorder="big") ) if b == 0: header.newMiner = True header.minerKey = blsPubKey.serialize() else: header.newMiner = False header.minerNick = 0 if header.serializeHash()[:-4] != template["header"]: raise TestError("Failed to recreate the header.") header.mine(blsPrivKey, blockchain.difficulty()) #Sleep for just over two thirty-second cycles to make sure we can connect to the node. sleep(65) rpc.meros.liveConnect(blockchain.blocks[-1].header.hash) blockchain.add(Block(header, BlockBody())) rpc.call( "merit", "publishBlock", [ template["id"], ( template["header"] + header.proof.to_bytes(4, byteorder="big") + header.signature + bytes.fromhex(template["body"]) ).hex() ] ) if rpc.meros.live.recv() != rpc.meros.liveBlockHeader(header): raise TestError("Meros didn't broadcast the BlockHeader.") #If there's supposed to be a Mint, verify Meros claimed it. if b >= 6: #Artificially create the Mint since the Blockchain won't create one without a recreated BlockBody. #It's faster to create a faux Mint than to handle the BlockBodies. mint: Mint = Mint(header.hash, [(0, 50000)]) claim: Claim = Claim([(mint.hash, 0)], bytes(32)) claim.sign([blsPrivKey]) if rpc.meros.live.recv()[0 : -80] != (MessageType.Claim.toByte() + claim.serialize())[0 : -80]: raise TestError("Meros didn't claim its Mint.") if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't verify its Claim.") #Create the matching Data. data: Data = Data(blockchain.genesis, header.hash) #Make sure Meros broadcasts a valid Verification for it. verif: SignedVerification = SignedVerification(data.hash) verif.sign(0, blsPrivKey) if rpc.meros.live.recv() != rpc.meros.signedElement(verif): raise TestError("Meros didn't verify the Block's Data.") #Close the live connection so we don't have to worry about it being disconnected for inactivity. try: rpc.meros.live.connection.shutdown(socket.SHUT_RDWR) rpc.meros.live.connection.close() except OSError: pass sleep(3)
edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #SpamFilter. spamFilter: SpamFilter = SpamFilter(5) #Blockchain. blockchain: Blockchain = Blockchain() #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 Verify Competing Block " + str(len(blockchain.blocks)) + ".") #Create the initial Data and two competing Datas. datas: List[Data] = [Data(bytes(32), edPubKey.to_bytes())] datas.append(Data(datas[0].hash, b"Initial Data.")) datas.append(Data(datas[0].hash, b"Second Data.")) for data in datas: data.sign(edPrivKey) data.beat(spamFilter)
merit: Merit = Merit() #SpamFilter. dataFilter: SpamFilter = SpamFilter(5) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Add 5 Blank Blocks. for i in range(5): merit.add(Block.fromJSON(blankBlocks[i])) #Create the Data. data: Data = Data(bytes(32), edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) transactions.add(data) #Verify it. verif: SignedVerification = SignedVerification(data.hash) verif.sign(0, blsPrivKey) #Generate another 6 Blocks. #Next block should have a packet. block: Block = Block( BlockHeader(
#Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKeys: List[PrivateKey] = [ PrivateKey(blake2b(b'\0', digest_size=32).digest()), PrivateKey(blake2b(b'\1', digest_size=32).digest()) ] blsPubKeys: List[PublicKey] = [ blsPrivKeys[0].toPublicKey(), blsPrivKeys[1].toPublicKey() ] #Add 4 Blank Blocks. for i in range(4): merit.add(Block.fromJSON(blankBlocks[i])) #Add a 5th Block to another verifier. block: Block = Block( BlockHeader(0, merit.blockchain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKeys[1].serialize(), merit.blockchain.blocks[-1].header.time + 1200), BlockBody()) #Mine it. block.mine(blsPrivKeys[1], merit.blockchain.difficulty()) #Add it. merit.add(block) print("Generated Aggregated Claim Block " + str(len(merit.blockchain.blocks) - 1) + ".") #Create the Datas.
import json #Blockchain. blockchain: Blockchain = Blockchain() #BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #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 Same Element Block " + str(len(blockchain.blocks)) + ".") #Create a SendDifficulty. sendDiff: SignedSendDifficulty = SignedSendDifficulty(4, 0) sendDiff.sign(0, blsPrivKey) #Create a DataDifficulty. dataDiff: SignedDataDifficulty = SignedDataDifficulty(4, 0) dataDiff.sign(0, blsPrivKey)
def fromJSON(blocks: List[Dict[str, Any]]) -> "Blockchain": result = Blockchain() for block in blocks: result.add(Block.fromJSON(block)) return result
bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") blankBlocks: List[Dict[str, Any]] = json.loads(bbFile.read()) bbFile.close() transactions: Transactions = Transactions() merit: Merit = Merit() dataFilter: SpamFilter = SpamFilter(5) edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() merit.add(Block.fromJSON(blankBlocks[0])) #Create the Data. data: Data = Data(bytes(32), edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) transactions.add(data) #Verify it. verif: SignedVerification = SignedVerification(data.hash) verif.sign(0, blsPrivKey) packet: List[VerificationPacket] = [VerificationPacket(data.hash, [0])] block = Block( BlockHeader(0, merit.blockchain.last(), BlockHeader.createContents(packet), 1, bytes(4),
from e2e.Classes.Merit.Blockchain import Blockchain blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() blockchain: Blockchain = Blockchain() blocks: List[Block] = [] #Generate a Block granting the holder Merit. blank: Block = Block( BlockHeader( 0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKey.serialize(), blockchain.blocks[-1].header.time + 1200 ), BlockBody() ) blank.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(blank) #Generate a Block which has a VP with no holders. vp: VerificationPacket = VerificationPacket(b'z' * 32, []) blocks.append( Block( BlockHeader( 0,
toAggregate = [] for h in order[0]: for s in order[0][h]: if s not in packets: packets[s] = VerificationPacket(sends[s].hash, []) packets[s].holders.append(h) verif: SignedVerification = SignedVerification(sends[s].hash) verif.sign(h, blsPrivKeys[h]) toAggregate.append(verif.signature) block: Block = Block( BlockHeader( 0, blockchain.last(), BlockHeader.createContents(list(packets.values())), 1, bytes(4), BlockHeader.createSketchCheck(bytes(4), list(packets.values())), order[1], blockchain.blocks[-1].header.time + 1200), BlockBody(list(packets.values()), [], Signature.aggregate(toAggregate))) miner: Union[bytes, int] = order[1] if isinstance(miner, bytes): for k in range(len(blsPubKeys)): if miner == blsPubKeys[k].serialize(): block.mine(blsPrivKeys[k], blockchain.difficulty()) break else: block.mine(blsPrivKeys[miner], blockchain.difficulty()) blockchain.add(block) print("Generated Fifty Block " + str(len(blockchain.blocks) - 1) + ".")
from hashlib import blake2b #JSON standard lib. import json #Miner Private Key. privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Blockchains. bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) main: Blockchain = Blockchain.fromJSON(blocks) #Only add the first 15 to the alt chain. alt: Blockchain = Blockchain() for b in range(15): alt.add(Block.fromJSON(blocks[b])) #Generate an alternative fifteen Blocks. for i in range(1, 15): #Create the next Block. block = Block( BlockHeader( 0, alt.last(), bytes(32), 1, bytes(4), bytes(32), 0, alt.blocks[-1].header.time + 1201 #Use a slightly different time to ensure a different hash.
#SpamFilter. spamFilter: SpamFilter = SpamFilter(5) #Blockchains. e1Chain: Blockchain = Blockchain() e2Chain: Blockchain = Blockchain() #Generate a Block granting the holder Merit. block = Block( BlockHeader( 0, e1Chain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKey.serialize(), e1Chain.blocks[-1].header.time + 1200 ), BlockBody() ) #Mine it. block.mine(blsPrivKey, e1Chain.difficulty()) #Add it. e1Chain.add(block) e2Chain.add(block) print("Generated Hundred Thirty Three Block 1/2 " + str(len(e1Chain.blocks)) + ".") #Create the initial Data and two competing Datas.
def TwoHundredFourtyTest(rpc: RPC) -> None: #Grab the keys. blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMeritHolderKey"))) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Blockchain used to calculate the difficulty. blockchain: Blockchain = Blockchain() #Mine enough blocks to lose Merit. for b in range(9): template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey.serialize().hex()}) template["header"] = bytes.fromhex(template["header"]) header: BlockHeader = BlockHeader( 0, blockchain.last(), bytes(32), 0, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200) if b == 0: header.newMiner = True header.minerKey = blsPubKey.serialize() header.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(Block(header, BlockBody())) rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) for b in range(1, 10): headerMsg: bytes = rpc.meros.liveBlockHeader( blockchain.blocks[b].header) rpc.meros.handleBlockBody(blockchain.blocks[b]) if rpc.meros.live.recv() != headerMsg: raise TestError("Meros didn't broadcast back the Block Header.") if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError( "Meros didn't sign a verification of the Block's data.") try: rpc.meros.live.connection.shutdown(socket.SHUT_RDWR) rpc.meros.live.connection.close() rpc.meros.sync.connection.shutdown(socket.SHUT_RDWR) rpc.meros.sync.connection.close() except OSError: pass #Verify our Merit is locked. if rpc.call("merit", "getMerit", {"nick": 0})["status"] != "Locked": raise Exception("Our Merit isn't locked so this test is invalid.") template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey.serialize().hex()}) template["header"] = bytes.fromhex(template["header"]) header: BlockHeader = BlockHeader( 0, template["header"][4:36], template["header"][36:68], int.from_bytes(template["header"][68:72], byteorder="little"), template["header"][72:76], template["header"][76:108], 0, int.from_bytes(template["header"][-4:], byteorder="little")) if not any(header.contents): raise TestError("Meros didn't try to unlock its Merit.") header.mine(blsPrivKey, blockchain.difficulty()) #Don't add the last block because we never provided it with a proper body. rpc.call( "merit", "publishBlock", { "id": template["id"], "header": (template["header"] + header.proof.to_bytes(4, byteorder="little") + header.signature).hex() }) #To verify the entire chain, we just need to verify this last header. #This is essential as our chain isn't equivalent. ourHeader: Dict[str, Any] = header.toJSON() if rpc.call("merit", "getBlock", {"block": header.hash.hex()})["header"] != ourHeader: raise TestError("Header wasn't added to the blockchain.")
#JSON standard lib. import json #Blockchains. main: Blockchain = Blockchain() alt: Blockchain = Blockchain() #Miner Private Keys. privKeys: List[PrivateKey] = [ PrivateKey(blake2b(b'\0', digest_size=32).digest()), PrivateKey(blake2b(b'\1', digest_size=32).digest()) ] #Create the Block to the first miner. block: Block = Block( BlockHeader(0, main.last(), bytes(32), 1, bytes(4), bytes(32), privKeys[0].toPublicKey().serialize(), main.blocks[-1].header.time + 1200), BlockBody()) block.mine(privKeys[0], main.difficulty()) main.add(block) alt.add(block) print("Generated Reorganizations Depth One Block 1.") #Create the Block to the second miner. block = Block( BlockHeader(0, main.last(), bytes(32), 1, bytes(4), bytes(32), privKeys[1].toPublicKey().serialize(), main.blocks[-1].header.time + 1200), BlockBody()) block.mine(privKeys[1], alt.difficulty()) main.add(block) alt.add(block) print("Generated Reorganizations Depth One Block 2.")
blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) blockchain: Blockchain = Blockchain.fromJSON(blocks) bbFile.close() #BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Create a SendDifficulty. sendDiff: SignedSendDifficulty = SignedSendDifficulty(5, 0) sendDiff.sign(0, blsPrivKey) #Generate a Block containing the SendDifficulty. block = Block( BlockHeader(0, blockchain.last(), BlockHeader.createContents([], [sendDiff]), 1, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200), BlockBody([], [sendDiff], sendDiff.signature)) #Mine it. block.mine(blsPrivKey, blockchain.difficulty()) #Add it. blockchain.add(block) print("Generated SendDifficulty Block " + str(len(blockchain.blocks)) + ".") #Mine 24 more Blocks until there's a vote. for _ in range(24): block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200), BlockBody()) #Mine it.
from hashlib import blake2b #JSON standard lib. import json #Miner Private Key. privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Blockchains. bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) main: Blockchain = Blockchain.fromJSON(blocks) #Only add the first 15 to the alt chain. alt: Blockchain = Blockchain() for b in range(15): alt.add(Block.fromJSON(blocks[b])) #Generate an alternative five Blocks. for i in range(1, 5): #Create the next Block. block = Block( BlockHeader( 0, alt.last(), bytes(32), 1, bytes(4), bytes(32), 0, alt.blocks[-1].header.time + 1 #Use a much shorter time to acquire more work.
#Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blockchain. blockchain: Blockchain = Blockchain() #Miner Private Key. privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Create the Block. block: Block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), privKey.toPublicKey().serialize(), blockchain.blocks[-1].header.time + 1200), BlockBody()) #Generate Blocks. for i in range(1, 26): #Mine the Block. block.mine(privKey, blockchain.difficulty()) #Add it locally. blockchain.add(block) print("Generated Blank Block " + str(i) + ".") #Create the next Block. block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200), BlockBody())
#BLS Private Key. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Create a Data for the VerificationPacket. data: Data = Data(bytes(32), edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) transactions.add(data) #Generate the VerificationPacket Block. block = Block( BlockHeader( 0, blockchain.last(), BlockHeader.createContents([VerificationPacket(data.hash, [1])]), 1, bytes(4), BlockHeader.createSketchCheck(bytes(4), [VerificationPacket(data.hash, [1])]), blsPrivKey.toPublicKey().serialize(), blockchain.blocks[-1].header.time + 1200), BlockBody([VerificationPacket(data.hash, [1])], [], blsPrivKey.sign(b""))) #Mine it. block.mine(blsPrivKey, blockchain.difficulty()) #Add it to the vectors. blocks.append(block.toJSON()) print("Generated Hundred Six Block Elements VerificationPacket Block.") #Generate the SendDifficulty Block. elements: List[Element] = [] elements.append(SendDifficulty(0, 0, 1)) block = Block(
#Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKeys: List[PrivateKey] = [ PrivateKey(blake2b(b'\0', digest_size=32).digest()), PrivateKey(blake2b(b'\1', digest_size=32).digest()) ] blsPubKeys: List[PublicKey] = [ blsPrivKeys[0].toPublicKey(), blsPrivKeys[1].toPublicKey() ] #Give the first key Merit. block: Block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKeys[0].serialize(), blockchain.blocks[-1].header.time + 1200), BlockBody()) #Mine it. block.mine(blsPrivKeys[0], blockchain.difficulty()) #Add it. blockchain.add(block) print("Generated Hundred Forty Two Block " + str(len(blockchain.blocks)) + ".") #Give the second key Merit. block: Block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKeys[1].serialize(), blockchain.blocks[-1].header.time + 1200), BlockBody()) block.mine(blsPrivKeys[1], blockchain.difficulty())
#BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Blockchains. blockchain: Blockchain = Blockchain() #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 Three Partial Block " + str(len(blockchain.blocks)) + ".") #Create conflicting Data Difficulties. dataDiffs: List[SignedDataDifficulty] = [
#Transactions. transactions: Transactions = Transactions() #Blockchain. blockchain: Blockchain = Blockchain() #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) 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 Invalid Competing Block " + str(len(blockchain.blocks)) + ".") #Create a Data using a bogus input. data: Data = Data(bytes.fromhex("11" * 32), bytes(1)) transactions.add(data) #Create a competing Data using the same input. competingData: Data = Data(bytes.fromhex("11" * 32), bytes(2)) transactions.add(competingData)
def verifyBeaten() -> None: #Verify beaten was set. The fourth Transaction is also beaten, yet should be pruned. #That's why we don't check its status. for send in sends[1:3]: if not rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["beaten"]: raise TestError( "Meros didn't mark a child and its descendant as beaten.") #Check the pending Verification for the beaten descendant was deleted. if ((rpc.call("consensus", "getStatus", {"hash": sends[2].hash.hex()})["verifiers"] != [0]) or (bytes.fromhex( rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey})["header"])[36:68] != bytes(32))): raise TestError("Block template still has the Verification.") #Verify the fourth Transaction was pruned. with raises(TestError): rpc.call("transactions", "getTransaction", {"hash": sends[3].hash.hex()}) #Verify neither the second or third Transaction tree can be appended to. #Publishes a never seen-before Send for the descendant. #Re-broadcasts the pruned Transaction for the parent. for send in sends[3:]: #Most of these tests use a socket connection for this. #This has identical effects, returns an actual error instead of a disconnect, #and doesn't force us to wait a minute for our old socket to be cleared. with raises(TestError): rpc.call("transactions", "publishTransaction", { "type": "Send", "transaction": send.serialize().hex() }) #Not loaded above as it can only be loqaded after the chain starts, which is done by the Liver. #RandomX cache keys and all that. blockWBeatenVerif: Block = Block.fromJSON( vectors["blockWithBeatenVerification"]) #The following code used to test behavior which was removed, in order to be more forgiving for nodes a tad behind. #Verify we can't add that SignedVerification now. #rpc.meros.signedElement(verif) #try: # rpc.meros.live.recv() # #Hijacks a random Exception type for our purposes. # raise MessageException("Meros didn't disconnect us after we sent a Verification for a beaten Transaction.") #except TestError: # pass #except MessageException as e: # raise TestError(e.message) #sleep(65) #rpc.meros.liveConnect(blockWBeatenVerif.header.last) #Verify we can't add a Block containing that Verification. rpc.meros.liveBlockHeader(blockWBeatenVerif.header) #BlockBody sync request. rpc.meros.handleBlockBody(blockWBeatenVerif) #Sketch hash sync request. hashReqs: bytes = rpc.meros.sync.recv()[37:] for h in range(0, len(hashReqs), 8): for packet in blockWBeatenVerif.body.packets: if int.from_bytes(hashReqs[h:h + 8], byteorder="little") == Sketch.hash( blockWBeatenVerif.header.sketchSalt, packet): rpc.meros.packet(packet) break try: rpc.meros.live.recv() raise MessageException( "Meros didn't disconnect us after we sent a Block containing a Verification of a beaten Transaction." ) except TestError: pass except MessageException as e: raise TestError(e.message) sleep(65) rpc.meros.liveConnect(blockWBeatenVerif.header.last) rpc.meros.syncConnect(blockWBeatenVerif.header.last)
#Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blockchain. blockchain: Blockchain = Blockchain() #BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) 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)
from e2e.Classes.Merit.BlockBody import BlockBody from e2e.Classes.Merit.Block import Block from e2e.Classes.Merit.Blockchain import Blockchain bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) blockchain: Blockchain = Blockchain.fromJSON(blocks) bbFile.close() 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):
merit: Merit = Merit() #SpamFilter. dataFilter: SpamFilter = SpamFilter(5) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Add 1 Blank Block. for i in range(1): merit.add(Block.fromJSON(blankBlocks[i])) #Create the Data and a successor. first: Data = Data(bytes(32), edPubKey.to_bytes()) first.sign(edPrivKey) first.beat(dataFilter) transactions.add(first) second: Data = Data(first.hash, bytes(1)) second.sign(edPrivKey) second.beat(dataFilter) transactions.add(second) #Verify them. firstVerif: SignedVerification = SignedVerification(first.hash) firstVerif.sign(0, blsPrivKey)
txs[-1].beat(dataFilter) verifs.append(SignedVerification(txs[-1].hash)) verifs[-1].sign(0, blsPrivKey) txs.append(Data(txs[-1].hash, b"\0")) del txs[-1] #Create the final Block. #Done manually as it has invalid data. packets: List[VerificationPacket] = [ VerificationPacket(tx.hash, [0]) for tx in txs ] blocks.append( Block( BlockHeader(0, blocks[0].header.hash, BlockHeader.createContents(packets), 4, bytes(4), BlockHeader.createSketchCheck(bytes(4), packets), 0, 2400), BlockBody(packets, [], Signature.aggregate([verif.signature for verif in verifs])))) #The difficulty either shouldn't change or should lower, hence why this works. blocks[-1].header.mine(blsPrivKey, Merit().blockchain.difficulty()) #Only save the Verifications the test will use. #Not a micro-opt on disk space; rather ensures this data isn't floating around to cause invalid testing methodology. #This would optimally be inlined below with the vector write, yet pylint errors on the complex statement. verifs = verifs[:2] with open("e2e/Vectors/Merit/TwoHundredSeventyFour/MatchesHeaderQuantity.json", "w") as vectors: vectors.write( json.dumps({ "blocks": [block.toJSON() for block in blocks],