def HundredTwentyFiveTest(rpc: RPC) -> None: #Meros allows connections from its own IP if they identify as 127.0.0.1. #We need to connect either through the LAN or through the public IP for this test to be valid. #The following code grabs the computer's 192 IP. lanIPFinder = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) lanIPFinder.connect(("meroscrypto.io", 443)) lanIP = lanIPFinder.getsockname()[0] lanIPFinder.close() if not (lanIP.split(".")[0] in {"10", "172", "192"}): raise Exception("Failed to get the LAN IP.") #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Connect to Meros. connection: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect((lanIP, rpc.meros.tcp)) try: connection.send( MessageType.Syncing.toByte() + (254).to_bytes(1, "big") + (254).to_bytes(1, "big") + (128).to_bytes(1, "big") + (6000).to_bytes(2, "big") + blockchain.blocks[0].header.hash, False) if len(connection.recv(38)) == 0: raise Exception("") except Exception: raise SuccessError( "Meros closed a connection from the same IP as itself which wasn't 127.0.0.1." ) raise TestError( "Meros allowed a connection from the same IP as itself which wasn't 127.0.0.1." )
def HundredTest(rpc: RPC) -> None: #Blocks. file: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(file.read()) file.close() #Grab only the first block. blocks = [blocks[0]] #Merit. merit: Merit = Merit.fromJSON(blocks) #The normal flow would be: #Handshake #BlockHeaderRequest #We need to delay the BlockHeader. Then, Meros should send: #Handshake #BlockHeaderRequest #Handshake #EXCEPT Meros can't handle sending Handshakes after Requests like that (see issue #100). #Therefore, Meros won't send a Handshake if it has a pending request (totally valid and acceptable behavior). #This test verifies we get disconnected without a Handshake attempt. #Handshake with the node. rpc.meros.connect(254, 254, merit.blockchain.blocks[1].header.hash) #Handle sync requests. reqHash: bytes = bytes() while True: msg: bytes = rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:33] if reqHash != merit.blockchain.blocks[-1].header.hash: raise TestError( "Meros asked for a BlockHeader other than the one in the Block from the Handshake." ) #Wait until Meros disconnects us. try: rpc.meros.recv() except TestError: raise SuccessError( "Meros didn't attempt to handshake with us while it had a pending sync request." ) raise TestError( "Meros tried to handshake with us while it had a pending sync request." )
def sendRepeatMeritRemoval() -> None: #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 3. #The genesis Block, the Block granting Merit, and the Block containing the MeritRemoval originally. try: if rpc.call("merit", "getHeight") != 3: 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 checkFail() -> None: #Send the Send. rpc.meros.liveTransaction(send) #Handle sync requests. while True: #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("Node disconnected us after we sent an invalid Transaction.") except Exception: raise TestError("Meros sent a keep-alive.")
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 sendMeritRemoval() -> None: #Send the Data. if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") rpc.meros.signedElement(removal) try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: raise SuccessError( "Meros rejected our MeritRemoval created from the same Element." ) except Exception: raise TestError( "Meros accepted our MeritRemoval created from the same Element." )
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 = -14 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 = -14 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 sendBlock() -> None: #Send the Block with the MeritRemoval archived again. block: Block = Block.fromJSON(keys, vectors[-1]) rpc.meros.blockHeader(block.header) #Flag of if the Block's Body synced. blockBodySynced: bool = False #Handle sync requests. reqHash: bytes = bytes() while True: try: msg: bytes = rpc.meros.recv() except TestError: if not blockBodySynced: raise TestError( "Node disconnected us before syncing the body.") raise SuccessError( "Meros didn't add the same MeritRemoval twice.") if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif 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) elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: #Raise a TestError if the Block was added. raise TestError( "Meros synced a Block with a repeat MeritRemoval.") else: raise TestError("Unexpected message sent: " + msg.hex().upper())
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 checkFail() -> None: #This Block should cause the node to disconnect us AFTER it attempts to sync our Transaction. syncedTX: bool = False #Grab the Block. block: Block = merit.blockchain.blocks[2] #Send the Block. rpc.meros.liveBlockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: if syncedTX: #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( "Node disconnected us after we sent a parsable, yet invalid, Verification." ) 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. rpc.meros.blockBody(block) elif MessageType(msg[0]) == MessageType.SketchHashesRequest: if not block.body.packets: raise TestError( "Meros asked for Sketch Hashes from a Block without any." ) reqHash = msg[1:33] 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)) #Send the Sketch Hashes. rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="big")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="big") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) elif MessageType(msg[0]) == MessageType.TransactionRequest: rpc.meros.dataMissing() syncedTX = True else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def checkFail() -> None: #This Block should cause the node to disconnect us AFTER it syncs our Transaction. syncedTX: bool = False #Grab the Block. block: Block = merit.blockchain.blocks[-1] #Send the Block. rpc.meros.blockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: try: msg: bytes = rpc.meros.recv() except TestError: if syncedTX: raise SuccessError("Node disconnected us after we sent an invalid Transaction.") raise TestError("Node errored before syncing our Transaction.") if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif 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(merit.state.nicks, block) elif MessageType(msg[0]) == MessageType.SketchHashesRequest: if not block.body.packets: raise TestError("Meros asked for Sketch Hashes from a Block without any.") reqHash = msg[1 : 33] 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)) #Send the Sketch Hashes. rpc.meros.sketchHashes(hashes) elif MessageType(msg[0]) == MessageType.SketchHashRequests: if not block.body.packets: raise TestError("Meros asked for Verification Packets from a Block without any.") reqHash = msg[1 : 33] if reqHash != block.header.hash: raise TestError("Meros asked for Verification Packets that didn't belong to the Block we just sent it.") #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33 : 37], byteorder="big")): sketchHash: int = int.from_bytes(msg[37 + (h * 8) : 45 + (h * 8)], byteorder="big") if sketchHash not in packets: raise TestError("Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1 : 33] if reqHash not in transactions.txs: raise TestError("Meros asked for a non-existent Transaction.") rpc.meros.transaction(transactions.txs[reqHash]) syncedTX = True elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: #Raise a TestError if the Block was added. raise TestError("Meros synced a Transaction which competed with a finalized Transaction.") else: raise TestError("Unexpected message sent: " + msg.hex().upper())
continue testsToRun.remove(test.__name__) print("\033[0;37mRunning " + test.__name__ + ".") #Message to display on a Node crash. crash: str = "\033[5;31m" + test.__name__ + " caused the node to crash!\033[0;31m" #Meros instance. meros: Meros = Meros(test.__name__, port, port + 1) sleep(5) rpc: RPC = RPC(meros) try: test(rpc) raise SuccessError() except SuccessError as e: ress.append("\033[0;32m" + test.__name__ + " succeeded.") except EmptyError as e: ress.append("\033[0;33m" + test.__name__ + " is empty.") except NodeError as e: ress.append(crash) except TestError as e: ress.append("\033[0;31m" + test.__name__ + " failed: " + str(e)) except Exception as e: ress.append("\033[0;31m" + test.__name__ + " is invalid.") ress.append(format_exc().rstrip()) finally: try: rpc.quit() meros.quit()
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 BusyTest(rpc: RPC) -> None: #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Handshake with the node. rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Create two new server sockets. def createServerSocket() -> socket.socket: result: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result.bind(("127.0.0.1", 0)) result.listen(2) return result busyServer: socket.socket = createServerSocket() server: socket.socket = createServerSocket() #Receive Syncing until Meros asks for peers. while True: res = rpc.meros.sync.recv() if MessageType(res[0]) == MessageType.Syncing: rpc.meros.sync.send(MessageType.BlockchainTail.toByte() + blockchain.blocks[0].header.hash) elif MessageType(res[0]) == MessageType.PeersRequest: break #Craft a Peers message of our own server. rpc.meros.sync.send(MessageType.Peers.toByte() + bytes.fromhex("017F000001") + busyServer.getsockname()[1].to_bytes(2, "big")) #Use select to obtain a non-blocking accept. busy: int = 0 buf: bytes for _ in select.select([busyServer], [], [], 5000): #Accept a new connection. client, _ = busyServer.accept() #Verify Meros's Handshake. buf = client.recv(38) if MessageType( buf[0]) not in {MessageType.Handshake, MessageType.Syncing}: busyServer.close() raise TestError( "Meros didn't start its connection with a Handshake.") if buf[1:] != ((254).to_bytes(1, "big") + (254).to_bytes(1, "big") + (128).to_bytes(1, "big") + (rpc.meros.tcp).to_bytes(2, "big") + blockchain.blocks[0].header.hash): busyServer.close() raise TestError("Meros had an invalid Handshake.") #Send back Busy. client.send(MessageType.Busy.toByte() + bytes.fromhex("017F000001") + server.getsockname()[1].to_bytes(2, "big")) busy += 1 if busy == 2: busyServer.close() break #Make sure Meros connects to the server we redirected to. for _ in select.select([server], [], [], 5000): #Accept a new connection. client, _ = server.accept() #Verify Meros's Handshake. buf = client.recv(38) if MessageType( buf[0]) not in {MessageType.Handshake, MessageType.Syncing}: server.close() raise TestError( "Meros didn't start its connection with a Handshake.") if buf[1:] != ((254).to_bytes(1, "big") + (254).to_bytes(1, "big") + (128).to_bytes(1, "big") + (rpc.meros.tcp).to_bytes(2, "big") + blockchain.blocks[0].header.hash): server.close() raise TestError("Meros had an invalid Handshake.") server.close() raise SuccessError( "Meros connected to the server we redirected it to with a Busy message." ) #Raise a TestError. busyServer.close() server.close() raise TestError("Meros didn't connect to the redirected server.")