def handleSync() -> None: reqHash: bytes = bytes() bH: int = 0 bB: int = 1 while True: if bB == 3: break msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[-32:] for b in range(len(blockchain.blocks)): if blockchain.blocks[b].header.hash == reqHash: blockList: List[bytes] = [] for bl in range(1, msg[2] + 2): if b - bl < 0: break blockList.append(blockchain.blocks[b - bl].header.hash) if blockList == []: rpc.meros.dataMissing() break rpc.meros.blockList(blockList) break if b == len(blockchain.blocks): rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:33] if reqHash != blockchain.blocks[2 - bH].header.hash: raise TestError( "Meros asked for a Block Header that didn't belong to the next Block." ) #Send the BlockHeader. rpc.meros.syncBlockHeader(blockchain.blocks[2 - bH].header) bH += 1 elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if reqHash != blockchain.blocks[bB].header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the next Block." ) rpc.meros.rawBlockBody(blockchain.blocks[bB], len(blockchain.blocks[bB].body.packets)) bB += 1 else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(rpc, blockchain)
def sendAlternateTip() -> None: header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header) rpc.meros.handleBlockBody(alt.blocks[-1]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the BlockHeader.") #Verify the alternate Blockchain. verifyBlockchain(rpc, alt) #Raise SuccessError so the Liver doesn't fail when verifying the original chain. raise SuccessError("Meros re-organized to the alternate chain.")
def sendAlternateTip() -> None: rpc.meros.liveBlockHeader(alt.blocks[-1].header) req: bytes = rpc.meros.sync.recv() if MessageType(req[0]) != MessageType.BlockListRequest: raise TestError( "Meros didn't request the list of previous BlockHeaders.") if req[-32:] != alt.blocks[-2].header.hash: raise TestError( "Meros didn't request the list of previous BlockHeaders for THIS header." ) blockList: List[bytes] = [] b: int = len(alt.blocks) - 3 while b != -1: blockList.append(alt.blocks[b].header.hash) b -= 1 rpc.meros.blockList(blockList) diff = -14 while diff != -1: if rpc.meros.sync.recv() != ( MessageType.BlockHeaderRequest.toByte() + alt.blocks[diff].header.hash): raise TestError("Meros didn't request a previous BlockHeader.") rpc.meros.syncBlockHeader(alt.blocks[diff].header) diff += 1 #Meros will now attempt the re-org, having verified the work. #Break the chain early via a data missing. diff = -14 while diff != 0: if diff == -2: if rpc.meros.sync.recv()[:-4] != ( MessageType.BlockBodyRequest.toByte() + alt.blocks[diff].header.hash): raise TestError( "Meros didn't request a previous BlockBody.") rpc.meros.dataMissing() sleep(35) for socket in [rpc.meros.live, rpc.meros.sync]: socket.connection.close() #We could just edit the condition above, yet this keeps parity with the other reorg tests. break else: rpc.meros.handleBlockBody(alt.blocks[diff]) diff += 1 #Verify Meros at least went back to the fork point. #Ideally, it'd go back to the original chain. #Or if we synced enough blocks where we still have a chain with more work, we should remain on it. verifyBlockchain(rpc, forkPoint) raise SuccessError("Meros reverted back to the fork point.")
def sendAlternateTip() -> None: header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header) req: bytes = rpc.meros.sync.recv() if req != (MessageType.BlockBodyRequest.toByte() + alt.blocks[-1].header.hash): raise TestError("Meros didn't request the BlockBody.") rpc.meros.blockBody(alt.blocks[-1]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the BlockHeader.") #Verify the alternate Blockchain. verifyBlockchain(rpc, alt) #Raise SuccessError so the Liver doesn't fail when verifying the original chain. raise SuccessError("Meros re-organized to the alternate chain.")
def sendAlternateTip() -> None: header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header) req: bytes = rpc.meros.sync.recv() if MessageType(req[0]) != MessageType.BlockListRequest: raise TestError( "Meros didn't request the list of previous BlockHeaders.") if req[3:35] != alt.blocks[-1].header.hash: raise TestError( "Meros didn't request the list of previous BlockHeaders for THIS header." ) blockList: List[bytes] = [] b: int = len(alt.blocks) - 2 while b != -1: blockList.append(alt.blocks[b].header.hash) b -= 1 rpc.meros.blockList(blockList) diff = -2 while diff != -1: req = rpc.meros.sync.recv() if req != (MessageType.BlockHeaderRequest.toByte() + alt.blocks[diff].header.hash): raise TestError("Meros didn't request a previous BlockHeader.") rpc.meros.syncBlockHeader(alt.blocks[diff].header) diff += 1 diff = -2 while diff != 0: req = rpc.meros.sync.recv() if req != (MessageType.BlockBodyRequest.toByte() + alt.blocks[diff].header.hash): raise TestError("Meros didn't request a previous BlockBody.") rpc.meros.blockBody(alt.blocks[diff]) diff += 1 if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the BlockHeader.") #Verify the alternate Blockchain. verifyBlockchain(rpc, alt) #Raise SuccessError so the Liver doesn't fail when verifying the original chain. raise SuccessError("Meros re-organized to the alternate chain.")
def TwoHundredThirtyTwoTest(rpc: RPC) -> None: file: IO[Any] = open( "e2e/Vectors/Merit/Reorganizations/TwoHundredThirtyTwo.json", "r") chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read()) main: Blockchain = Blockchain.fromJSON(chains["main"]) alt: Blockchain = Blockchain.fromJSON(chains["alt"]) file.close() def sendBlock(toSend: Block) -> None: rpc.meros.liveBlockHeader(toSend.header) if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() + toSend.header.hash): raise TestError("Meros didn't ask for this Block's body.") rpc.meros.blockBody(toSend) if toSend.body.packets: if rpc.meros.sync.recv() != ( MessageType.SketchHashRequests.toByte() + toSend.header.hash + (1).to_bytes(4, byteorder="little") + Sketch.hash( toSend.header.sketchSalt, toSend.body.packets[0]).to_bytes( 8, byteorder="little")): raise TestError( "Meros didn't ask for this BlockBody's VerificationPacket." ) rpc.meros.packet(toSend.body.packets[0]) #Make the initial connection and sync the main chain. rpc.meros.liveConnect(main.blocks[0].header.hash) rpc.meros.syncConnect(main.blocks[0].header.hash) sendBlock(main.blocks[1]) sendBlock(main.blocks[2]) #Trigger the reorganization to the alternate chain. #We only want the revert aspect of this. rpc.meros.liveBlockHeader(alt.blocks[3].header) if rpc.meros.sync.recv()[0] != MessageType.BlockListRequest.toByte()[0]: raise TestError( "Meros didn't ask for the Block List of the alternate chain.") rpc.meros.blockList([alt.blocks[2].header.hash, alt.blocks[1].header.hash]) if rpc.meros.sync.recv() != (MessageType.BlockHeaderRequest.toByte() + alt.blocks[2].header.hash): raise TestError( "Meros didn't ask for the other BlockHeader in this alternate chain." ) rpc.meros.syncBlockHeader(alt.blocks[2].header) #Cause the re-organization to fail. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() rpc.socket.close() sleep(35) #Reboot the node to reload the database. rpc.meros.quit() rpc.meros.calledQuit = False rpc.meros.process = Popen([ "./build/Meros", "--data-dir", rpc.meros.dataDir, "--log-file", rpc.meros.log, "--db", rpc.meros.db, "--network", "devnet", "--tcp-port", str(rpc.meros.tcp), "--rpc-port", str(rpc.meros.rpc), "--no-gui" ]) while True: try: connection: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect(("127.0.0.1", rpc.meros.rpc)) connection.shutdown(socket.SHUT_RDWR) connection.close() break except ConnectionRefusedError: sleep(1) rpc.meros.liveConnect(main.blocks[0].header.hash) rpc.meros.syncConnect(main.blocks[0].header.hash) sendBlock(main.blocks[2]) rpc.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) rpc.socket.connect(("127.0.0.1", rpc.meros.rpc)) verifyBlockchain(rpc, main)
def live(self, ignorePackets: List[bytes] = []) -> None: #Handshake with the node. self.rpc.meros.liveConnect(self.merit.blockchain.blocks[0].header.hash) self.rpc.meros.syncConnect(self.merit.blockchain.blocks[0].header.hash) #Send each Block. for b in range(1, len(self.merit.blockchain.blocks)): block: Block = self.merit.blockchain.blocks[b] #Set loop variables with pending data. pendingBody: bool = True pendingPackets: List[bytes] = [] pendingTXs: List[bytes] = [] for packet in block.body.packets: if packet.hash in ignorePackets: continue pendingPackets.append(packet.hash) #Don't include sent Transactions or independently created Block Data. if not ((packet.hash in self.rpc.meros.sentTXs) or (packet.hash == (Data(self.merit.blockchain.genesis, block.header.last).hash))): pendingTXs.append(packet.hash) for elem in block.body.elements: if isinstance(elem, MeritRemoval): if (isinstance(elem.e1, (Verification, VerificationPacket)) and (elem.e1.hash not in self.rpc.meros.sentTXs) and (elem.e1.hash not in pendingTXs)): pendingTXs.append(elem.e1.hash) if (isinstance(elem.e2, (Verification, VerificationPacket)) and (elem.e2.hash not in self.rpc.meros.sentTXs) and (elem.e2.hash not in pendingTXs)): pendingTXs.append(elem.e2.hash) self.rpc.meros.liveBlockHeader(block.header) reqHash: bytes = bytes() while True: #If we sent every bit of data, break. if ((not pendingBody) and (not pendingPackets) and (not pendingTXs)): break #Receive the next message. msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if not pendingBody: raise TestError( "Meros asked for the same Block Body multiple times." ) if reqHash != block.header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the Block we just sent it." ) self.rpc.meros.blockBody(block) pendingBody = False elif MessageType(msg[0]) == MessageType.SketchHashesRequest: reqHash = msg[1:33] if not block.body.packets: raise TestError( "Meros asked for Sketch Hashes from a Block without any." ) if reqHash != block.header.hash: raise TestError( "Meros asked for Sketch Hashes that didn't belong to the Block we just sent it." ) #Create the haashes. hashes: List[int] = [] for packet in block.body.packets: hashes.append( Sketch.hash(block.header.sketchSalt, packet)) self.rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: reqHash = msg[1:33] if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) 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.") self.rpc.meros.packet(packets[sketchHash]) #Delete the VerificationPacket from pending. del pendingPackets[pendingPackets.index( packets[sketchHash].hash)] #Make sure Meros asked for every packet. if pendingPackets: raise TestError( "Meros didn't ask for every Verification Packet.") elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if self.transactions is None: raise TestError( "Meros asked for a Transaction when we have none.") if reqHash not in pendingTXs: raise TestError( "Meros asked for a non-existent Transaction, a Transaction part of a different Block, or an already sent Transaction." ) self.rpc.meros.syncTransaction( self.transactions.txs[reqHash]) #Delete the Transaction from pending. del pendingTXs[pendingTXs.index(reqHash)] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Receive the BlockHeader from Meros. if MessageType( self.rpc.meros.live.recv()[0]) != MessageType.BlockHeader: raise TestError("Meros didn't broadcast the new BlockHeader.") #Add any new nicks to the lookup table. if self.merit.blockchain.blocks[b].header.newMiner: self.merit.state.nicks.append( self.merit.blockchain.blocks[b].header.minerKey) #If there's a callback at this height, call it. if b in self.callbacks: self.callbacks[b]() #Execute the every-Block callback, if it exists. if self.everyBlock is not None: self.everyBlock(b) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) #Reset the node. self.rpc.reset()
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)
def verify() -> None: verifyMeritRemoval(rpc, 1, 1, removal.holder, False) verifyBlockchain(rpc, Blockchain.fromJSON(vectors["blockchain"])) raise SuccessError( "MeritRemoval and Blockchain were properly handled.")
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)
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)
def EightyEightTest(rpc: RPC) -> None: edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key() blsPrivKey: PrivateKey = PrivateKey(0) blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex() 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 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 two Datas. datas: List[Data] = [Data(bytes(32), edPubKey)] 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(1.5) #Verify the block template has no verifications. if bytes.fromhex( rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey})["header"])[36:68] != bytes(32): raise TestError("Block template has Verification Packets.") #Transmit the first signed verification. rpc.meros.signedElement(verifs[0]) sleep(1.5) #Verify the block template has both verifications. template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", {"miner": 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), len(packets), 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.") 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)
def sync( self ) -> None: #Handshake with the node. self.rpc.meros.syncConnect(self.merit.blockchain.blocks[self.settings["height"]].header.hash) #Handle sync requests. reqHash: bytes = bytes() while True: #Break out of the for loop if the sync finished. #This means we sent every Block, every Element, every Transaction... if ( (self.blockHashes == set()) and (self.packets == {}) and (self.txs == set()) ): break msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[3 : 35] for b in range(len(self.merit.blockchain.blocks)): if self.merit.blockchain.blocks[b].header.hash == reqHash: blockList: List[bytes] = [] for bl in range(1, msg[2] + 2): if msg[1] == 0: if b - bl < 0: break blockList.append(self.merit.blockchain.blocks[b - bl].header.hash) if b - bl != 0: self.blocks.append(self.merit.blockchain.blocks[b - bl]) elif msg[1] == 1: if b + bl > self.settings["height"]: break blockList.append(self.merit.blockchain.blocks[b + bl].header.hash) self.blocks.append(self.merit.blockchain.blocks[b + bl]) else: raise TestError("Meros asked for an invalid direction in a BlockListRequest.") if blockList == []: self.rpc.meros.dataMissing() break self.rpc.meros.blockList(blockList) break if b == self.settings["height"]: self.rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: if (self.txs != set()) or (self.packets != {}): raise TestError("Meros asked for a new Block before syncing the last Block's Transactions and Packets.") reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for a BlockHeader other than the next Block's on the last BlockList.") self.rpc.meros.syncBlockHeader(self.blocks[-1].header) elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for a BlockBody other than the next Block's on the last BlockList.") self.rpc.meros.blockBody(self.blocks[-1]) self.blockHashes.remove(self.blocks[-1].header.hash) #Set packets/transactions. self.packets = {} for packet in self.blocks[-1].body.packets: if packet.hash not in self.synced: self.txs.add(packet.hash) self.packets[Sketch.hash(self.blocks[-1].header.sketchSalt, packet)] = packet #Update the list of mentioned Transactions. noVCMRs: bool = True for elem in self.blocks[-1].body.elements: if isinstance(elem, MeritRemoval): if isinstance(elem.e1, (Verification, VerificationPacket)): self.txs.add(elem.e1.hash) noVCMRs = False if isinstance(elem.e2, (Verification, VerificationPacket)): self.txs.add(elem.e2.hash) noVCMRs = False if (self.packets == {}) and noVCMRs: del self.blocks[-1] elif MessageType(msg[0]) == MessageType.SketchHashesRequest: reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for Sketch Hashes that didn't belong to the header we just sent it.") #Get the haashes. hashes: List[int] = list(self.packets) #Send the Sketch Hashes. self.rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: if not self.packets: raise TestError("Meros asked for Verification Packets from a Block without any.") reqHash = msg[1 : 33] if reqHash != self.blocks[-1].header.hash: raise TestError("Meros asked for Verification Packets that didn't belong to the Block we just sent it.") #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33 : 37], byteorder="big")): sketchHash: int = int.from_bytes(msg[37 + (h * 8) : 45 + (h * 8)], byteorder="big") if sketchHash not in self.packets: raise TestError("Meros asked for a non-existent Sketch Hash.") self.rpc.meros.packet(self.packets[sketchHash]) del self.packets[sketchHash] elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1 : 33] if self.transactions is None: raise TestError("Meros asked for a Transaction when we have none.") if reqHash not in self.transactions.txs: raise TestError("Meros asked for a Transaction we don't have.") if reqHash not in self.txs: raise TestError("Meros asked for a Transaction we haven't mentioned.") self.rpc.meros.syncTransaction(self.transactions.txs[reqHash]) self.synced.add(reqHash) self.txs.remove(reqHash) if self.txs == set(): del self.blocks[-1] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) if self.settings["playback"]: #Playback their messages. self.rpc.meros.sync.playback()
def sync(self) -> None: self.rpc.meros.syncConnect( self.merit.blockchain.blocks[self.settings["height"]].header.hash) reqHash: bytes = bytes() while True: #Break out of the for loop if the sync finished. #This means we sent every Block, every Element, every Transaction... if ((self.blockHashes == set()) and (self.packets == {}) and (self.txs == set())): break msg: bytes = self.rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[-32:] for b in range(len(self.merit.blockchain.blocks)): if self.merit.blockchain.blocks[b].header.hash == reqHash: if self.blocks[-1].header.hash != reqHash: self.blocks.append(self.merit.blockchain.blocks[b]) blockList: List[bytes] = [] for bl in range(1, msg[1] + 2): if b - bl < 0: break blockList.append( self.merit.blockchain.blocks[b - bl].header.hash) if b - bl != 0: self.blocks.append( self.merit.blockchain.blocks[b - bl]) if blockList == []: self.rpc.meros.dataMissing() break self.rpc.meros.blockList(blockList) break if b == self.settings["height"]: self.rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:33] if (self.txs != set()) or (self.packets != {}): raise TestError( "Meros asked for a new Block before syncing the last Block's Transactions and Packets." ) if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for a BlockHeader other than the next Block's on the last BlockList." ) self.rpc.meros.syncBlockHeader(self.blocks[-1].header) elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:33] if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for a BlockBody other than the next Block's on the last BlockList." ) if int.from_bytes(msg[33:37], "little") != len( self.blocks[-1].body.packets): raise Exception( "Meros didn't request 100% of packets in the BlockBody when syncing." ) self.rpc.meros.rawBlockBody(self.blocks[-1], len(self.blocks[-1].body.packets)) self.blockHashes.remove(self.blocks[-1].header.hash) #Set the packets/transactions which should be synced. self.packets = {} for packet in self.blocks[-1].body.packets: if not ((packet.hash in self.rpc.meros.sentTXs) or (packet.hash == (Data( self.merit.blockchain.genesis, self.blocks[-1].header.last).hash))): self.txs.add(packet.hash) self.packets[Sketch.hash(self.blocks[-1].header.sketchSalt, packet)] = packet #Update the list of mentioned Transactions. noVCMRs: bool = True if (self.packets == {}) and noVCMRs: del self.blocks[-1] elif MessageType(msg[0]) == MessageType.SketchHashRequests: reqHash = msg[1:33] if not self.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) if reqHash != self.blocks[-1].header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #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 self.packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") self.rpc.meros.packet(self.packets[sketchHash]) del self.packets[sketchHash] if (self.packets == {}) and (self.txs == set()): del self.blocks[-1] elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if self.transactions is None: raise TestError( "Meros asked for a Transaction when we have none.") if reqHash not in self.transactions.txs: raise TestError( "Meros asked for a Transaction we don't have.") if reqHash not in self.txs: raise TestError( "Meros asked for a Transaction we haven't mentioned.") self.rpc.meros.syncTransaction(self.transactions.txs[reqHash]) self.txs.remove(reqHash) if (self.packets == {}) and (self.txs == set()): del self.blocks[-1] else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(self.rpc, self.merit.blockchain) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) #Playback their messages. #Verifies Meros can respond as it can receive. if self.settings["playback"]: self.rpc.meros.sync.playback() #Reset the node. self.rpc.reset()