def handleSync() -> None: #Handle sync requests. 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[3 : 35] 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 msg[1] == 0: if b - bl < 0: break blockList.append(blockchain.blocks[b - bl].header.hash) elif msg[1] == 1: blockList.append(blockchain.blocks[b + bl].header.hash) else: raise TestError("Meros asked for an invalid direction in a BlockListRequest.") 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.") #Send the Block. rpc.meros.blockBody(blockchain.blocks[bB]) bB += 1 else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(rpc, blockchain)
def sendDataAndBlock() -> None: #Send the Data. if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) != MessageType.SketchHashRequests: raise TestError("Meros didn't request the packets for this Block.") packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError("Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) try: if MessageType( rpc.meros.live.recv()[0]) == MessageType.BlockHeader: raise TestError("Meros added the Block.") except Exception as e: if str(e) != "Meros added the Block.": raise SuccessError()
def TwoHundredThirtySevenTest(rpc: RPC) -> None: chains: Dict[str, List[Dict[str, Any]]] with open("e2e/Vectors/Merit/Reorganizations/TwoHundredThirtySeven.json", "r") as file: chains = json.loads(file.read()) main: Blockchain = Blockchain.fromJSON(chains["main"]) alt: Blockchain = Blockchain.fromJSON(chains["alt"]) def sendBlock(toSend: Block) -> None: rpc.meros.liveBlockHeader(toSend.header) rpc.meros.handleBlockBody(toSend) #Send 0 through 3 of 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]) sendBlock(main.blocks[3]) #Send the alt chain, which won't have enough work to trigger a reorganization. rpc.meros.liveBlockHeader(alt.blocks[2].header) if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest: raise TestError( "Meros didn't request for the Block List for this alternate chain." ) rpc.meros.blockList([alt.blocks[0].header.hash]) if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockHeaderRequest: raise TestError( "Meros didn't request a BlockHeader in this alternate chain.") rpc.meros.syncBlockHeader(alt.blocks[1].header) #Verify Meros can sync the final Block. sendBlock(main.blocks[4])
def checkFail() -> None: #Grab the Block. #pylint: disable=cell-var-from-loop block: Block = merit.blockchain.blocks[12] #Send the Block. rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) #Handle sync requests. while True: msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.SketchHashRequests: if msg[1:33] != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range( int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash: bytes = msg[1:33] #pylint: disable=cell-var-from-loop if reqHash not in transactions.txs: raise TestError( "Meros asked for a non-existent Transaction.") #pylint: disable=cell-var-from-loop rpc.meros.syncTransaction(transactions.txs[reqHash]) #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.") else: raise TestError("Unexpected message sent: " + msg.hex().upper())
def VUnknownSignedTest(rpc: RPC) -> None: file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") chain: Blockchain = Blockchain.fromJSON(json.loads(file.read())) file.close() #Send a single block so we have a miner. rpc.meros.liveConnect(chain.blocks[0].header.hash) rpc.meros.syncConnect(chain.blocks[0].header.hash) header: bytes = rpc.meros.liveBlockHeader(chain.blocks[1].header) if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest: raise TestError("Meros didn't ask for the body.") rpc.meros.blockBody(chain.blocks[1]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't broadcast the header.") #Create a valid Data. #Uneccessary at this time, but good preparation for the future. privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) data: Data = Data(bytes(32), privKey.get_verifying_key().to_bytes()) data.sign(privKey) data.beat(SpamFilter(5)) #Sign the Data. verif: SignedVerification = SignedVerification(data.hash) verif.sign(0, PrivateKey(0)) #Run twice. The first shouldn't send the Transaction. The second should. for i in range(2): rpc.meros.signedElement(verif) if MessageType( rpc.meros.sync.recv()[0]) != MessageType.TransactionRequest: raise TestError("Meros didn't request the transaction.") if i == 0: #When we send DataMissing, we should be disconnected within a few seconds. rpc.meros.dataMissing() start: int = int(time()) try: rpc.meros.sync.recv() except Exception: #More than a few seconds is allowed as Meros's own SyncRequest must timeout. if int(time()) - start > 10: raise TestError( "Meros didn't disconnect us for sending a Verification of a non-existent Transaction." ) #Clear our invalid connections. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() sleep(65) #Init new ones. rpc.meros.liveConnect(chain.blocks[0].header.hash) rpc.meros.syncConnect(chain.blocks[0].header.hash) else: rpc.meros.syncTransaction(data) sleep(2) if not rpc.call("consensus", "getStatus", [data.hash.hex()])["verifiers"]: raise TestError("Meros didn't add the Verification.")
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 MissingOneSketchTest(meros: Meros) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Merit/Sketches/MissingOne.json", "r") as file: vectors = json.loads(file.read()) blockchain: Blockchain = Blockchain.fromJSON(vectors["blockchain"]) meros.liveConnect(blockchain.blocks[0].header.hash) meros.syncConnect(blockchain.blocks[0].header.hash) header: bytes = meros.liveBlockHeader(blockchain.blocks[1].header) meros.handleBlockBody(blockchain.blocks[1]) if meros.live.recv() != header: raise TestError( "Meros didn't broadcast a BlockHeader for a Block it just added.") for data in vectors["datas"]: if meros.liveTransaction(Data.fromJSON(data)) != meros.live.recv(): raise TestError("Meros didn't broadcast back a Data Transaction.") for verif in vectors["verifications"]: if meros.signedElement( SignedVerification.fromSignedJSON(verif)) != meros.live.recv(): raise TestError("Meros didn't broadcast back a Verification.") header = meros.liveBlockHeader(blockchain.blocks[2].header) meros.handleBlockBody(blockchain.blocks[2], 1) if MessageType(meros.sync.recv()[0]) != MessageType.SketchHashRequests: raise TestError("Meros didn't request the packet it's missing.") meros.packet(VerificationPacket.fromJSON(vectors["packet"])) if meros.live.recv() != header: raise TestError( "Meros didn't broadcast a BlockHeader for a Block it just added.")
def test() -> None: #Data. dataHash: str = rpc.call("personal", "data", {"data": "abc"}) #Read the initial Data. if MessageType(rpc.meros.live.recv()[0]) != MessageType.Data: raise TestError("Meros didn't broadcast the initial Data.") #Read the actual Data. serializedData: bytes = rpc.meros.live.recv() if serializedData[1:] != Data.fromJSON(rpc.call("transactions", "getTransaction", {"hash": dataHash})).serialize(): raise TestError("Meros didn't broadcast the created Data.") res: Any = rpc.call("network", "broadcast", {"transaction": dataHash}) if not (isinstance(res, bool) and res): raise TestError("Broadcast didn't return true.") if rpc.meros.live.recv() != serializedData: raise TestError("Meros didn't broadcast a Transaction when told to.") #Block. header: BlockHeader = Block.fromJSON(vectors["blockchain"][0]).header rpc.call("network", "broadcast", {"block": header.hash.hex()}) if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()): raise TestError("Meros didn't broadcast the Blockheader.") #Data and Block. rpc.call("network", "broadcast", {"block": header.hash.hex(), "transaction": dataHash}) if rpc.meros.live.recv() != serializedData: raise TestError("Meros didn't broadcast a Transaction when told to.") if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()): raise TestError("Meros didn't broadcast the Blockheader.") #Non-existent Transaction. try: rpc.call("network", "broadcast", {"transaction": bytes(32).hex()}) raise TestError() except TestError as e: if str(e) != "-2 Transaction not found.": raise TestError("Meros didn't error when told to broadcast a non-existent Transaction.") #Non-existent Block. try: rpc.call("network", "broadcast", {"block": bytes(32).hex()}) raise TestError() except TestError as e: if str(e) != "-2 Block not found.": raise TestError("Meros didn't error when told to broadcast a non-existent Block.") #Mint. try: rpc.call("network", "broadcast", {"transaction": mint.hex()}) raise TestError() except TestError as e: if str(e) != "-3 Transaction is a Mint.": raise TestError("Meros didn't error when told to broadcast a Mint.") #Genesis Block. try: rpc.call("network", "broadcast", {"block": Blockchain().blocks[0].header.hash.hex()}) raise TestError() except TestError as e: if str(e) != "-3 Block is the genesis Block.": raise TestError("Meros didn't error when told to broadcast the genesis Block.")
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 TwoHundredFourTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/TwoHundredFour.json", "r") as file: vectors = json.loads(file.read()) #Instantiate a Blockchain to set the RandomX key. chain: Blockchain = Blockchain() blank: Block = Block.fromJSON(vectors["blank"]) if len(vectors["blocks"]) != 1: raise Exception( "Misread this test's vectors, creating an invalid test due to that." ) block: Block = Block.fromJSON(vectors["blocks"][0]) rpc.meros.liveConnect(chain.last()) rpc.meros.syncConnect(chain.last()) #Send a blank block so Meros acknowledges a holder. sentHeader: bytes = rpc.meros.liveBlockHeader(blank.header) rpc.meros.handleBlockBody(blank) if rpc.meros.live.recv() != sentHeader: raise TestError( "Meros didn't rebroadcast the header for a blank Block.") rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) != MessageType.SketchHashRequests: raise TestError("Unexpected message sent: " + msg.hex().upper()) if msg[1:33] != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) if int.from_bytes(msg[33:37], byteorder="little") != 1: raise TestError("Meros didn't ask for one VerificationPacket.") if int.from_bytes(msg[37:45], byteorder="little") != Sketch.hash( block.header.sketchSalt, block.body.packets[0]): raise TestError("Meros didn't ask for the VerificationPacket.") rpc.meros.packet(block.body.packets[0]) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. sleep(1) if rpc.meros.process.poll() is not None: raise TestError( "Node crashed trying to handle a VerificationPacket with no holders." ) except Exception: raise TestError( "Meros didn't disconnect us after sending a VerificationPacket with no holders; it also didn't crash." )
def reorg( rpc: RPC, alt: Blockchain ) -> None: #Sync up the new Blockchain. header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header) #Use the genesis as the default value for this outer defined variable. lastBlock: Block = alt.blocks[0] while True: req: bytes = rpc.meros.sync.recv() if MessageType(req[0]) == MessageType.BlockListRequest: blockList: List[bytes] = [] for block in alt.blocks: if block.header.hash == req[2:]: break blockList.append(block.header.hash) blockList = blockList[-req[1]:] blockList.reverse() rpc.meros.blockList(blockList) elif MessageType(req[0]) == MessageType.BlockHeaderRequest: reqHash: bytes = req[1:] for block in alt.blocks: if reqHash == block.header.hash: rpc.meros.syncBlockHeader(block.header) break elif MessageType(req[0]) == MessageType.BlockBodyRequest: reqHash: bytes = req[1:-4] for block in alt.blocks: if reqHash == block.header.hash: lastBlock = block rpc.meros.rawBlockBody(block, 5) break elif MessageType(req[0]) == MessageType.SketchHashRequests: rpc.meros.packet(lastBlock.body.packets[0]) #If we've sent the last BlockBody, and its packets, we've synced the chain. if lastBlock.header.hash == alt.blocks[-1].header.hash: break if header != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back the alt chain's header.")
def verify(rpc: RPC, txHash: bytes, nick: int = 0, mr: bool = False) -> None: sv: SignedVerification = SignedVerification(txHash) sv.sign(nick, PrivateKey(nick)) temp: bytes = rpc.meros.signedElement(sv) if mr: if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedMeritRemoval: raise TestError("Meros didn't create a MeritRemoval.") elif temp != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Verification.")
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 VarIntTest( meros: Meros ) -> None: #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Handshake with the node using a pointless VarInt for the protocol/network/services. live: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) live.connect(("127.0.0.1", meros.tcp)) live.send( MessageType.Handshake.toByte() + int("1000000000000000", 2).to_bytes(2, "big") + int("11111111100000001000000000000000", 2).to_bytes(4, "big") + int("100000101001101000111010", 2).to_bytes(3, "big") + b'\0\0' + blockchain.blocks[0].header.hash ) sync: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sync.connect(("127.0.0.1", meros.tcp)) sync.send( MessageType.Syncing.toByte() + int("1000000000000000", 2).to_bytes(2, "big") + int("11111111100000001000000000000000", 2).to_bytes(4, "big") + int("100000101001101000111010", 2).to_bytes(3, "big") + b'\0\0' + blockchain.blocks[0].header.hash ) #Verify Meros considers us valid by receiving its initial handshake, and then waiting for the next one. if ( (MessageType(live.recv(38)[0]) != MessageType.Handshake) or (MessageType(sync.recv(38)[0]) != MessageType.Syncing) ): raise TestError("Meros didn't handshake with us.") if MessageType(live.recv(38)[0]) != MessageType.Handshake: raise TestError("Meros didn't handshake with us to stop us from timing out.")
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 TwoHundredSeventyThreeTest(meros: Meros) -> None: blockchain: Blockchain = Blockchain() vectors: Dict[str, Any] with open("e2e/Vectors/Merit/TwoHundredSeventyThree.json", "r") as file: vectors = json.loads(file.read()) header: BlockHeader = BlockHeader.fromJSON(vectors) meros.liveConnect(blockchain.last()) meros.syncConnect(blockchain.last()) #Sanity check on the behavior of select. readable, _, _ = select([meros.live.connection, meros.sync.connection], [], [], 65) if len(readable) != 1: raise Exception( "Misuse of select; multiple sockets reported readable.") if MessageType(meros.live.recv()[0]) != MessageType.Handshake: raise Exception( "Misuse of select; it didn't return the live socket trying to Handshake. Keep-alives could also be broken." ) meros.live.send(MessageType.BlockchainTail.toByte() + blockchain.last()) #Send the header. meros.liveBlockHeader(header) #Meros should disconnect us immediately. If it doesn't, it'll either send a keep-alive or a BlockBodyRequest. #One is inefficient as it doesn't properly protect against spam attacks. #One is invalid completely. readable, _, _ = select([meros.live.connection, meros.sync.connection], [], [], 65) #On Linux, both sockets immediately appear as readable. #That is why we iterate, instead of just checking length == 0. for s in readable: try: temp: str = s.recv(1) if len(temp) != 0: raise TestError( "Meros tried to send us something instead of immediately disconnecting us." ) except TestError as e: raise e except Exception: pass
def send16AndInvalidAlt() -> None: #Send the Block after the fork from the main chain. header: bytes = rpc.meros.liveBlockHeader(main.blocks[16].header) req: bytes = rpc.meros.sync.recv() if req != (MessageType.BlockBodyRequest.toByte() + main.blocks[16].header.hash): raise TestError("Meros didn't request the BlockBody for this Block from the main chain.") rpc.meros.blockBody(main.blocks[16]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the BlockHeader.") #Send the headers of the alt chain to trigger a re-org. header = rpc.meros.liveBlockHeader(alt.blocks[-1].header) req = 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[-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 = -4 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 #Meros should now revert and attempt the re-org. #Disconnect to make sure it fails. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() #Sleep long enough to get counted as disconnected and removed from the list of connections. sleep(35) #Reconnect. rpc.meros.live = MerosSocket(rpc.meros.tcp, 254, 254, True, main.blocks[15].header.hash) rpc.meros.sync = MerosSocket(rpc.meros.tcp, 254, 254, False, main.blocks[15].header.hash)
def sendMeritRemoval() -> None: #Send and verify the MeritRemoval. removalBytes: bytes = rpc.meros.signedElement(removal) sent: int = 0 while True: if sent == 2: break msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.TransactionRequest: rpc.meros.syncTransaction(transactions.txs[msg[1 : 33]]) sent += 1 else: raise TestError("Unexpected message sent: " + msg.hex().upper()) if removalBytes != rpc.meros.live.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 1, removal.holder, True)
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 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 TwoHundredThirtyFiveTest(rpc: RPC) -> None: blockchain: Blockchain = Blockchain() dataFilter: SpamFilter = SpamFilter(5) edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #Mine one Block to the node. blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMiner"))) blsPubKey: bytes = blsPrivKey.toPublicKey().serialize() #Call getBlockTemplate just to get an ID. #Skips the need to write a sync loop for the BlockBody. template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey.hex()]) #Mine a Block. block = Block( BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1, bytes(4), bytes(32), blsPubKey, blockchain.blocks[0].header.time + 1200, 0), BlockBody()) block.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(block) rpc.call("merit", "publishBlock", [template["id"], block.serialize().hex()]) #Send Meros a Data and receive its Verification to make sure it's verifying Transactions in the first place. data: Data = Data(bytes(32), edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) rpc.meros.liveConnect(blockchain.blocks[0].header.hash) if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't send us its SignedVerification.") #Close our connection and mine 8 Blocks so its Merit is locked. rpc.meros.live.connection.close() for _ in range(8): block = Block( BlockHeader(0, blockchain.blocks[-1].header.hash, bytes(32), 1, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200, 0), BlockBody()) #Reusing its key is fine as mining doesn't count as participation. block.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(block) #Sleep 30 seconds to make sure Meros noted we disconnected, and then reconnect. sleep(30) rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Sync the Blocks. for b in range(8): header: bytes = rpc.meros.liveBlockHeader(blockchain.blocks[b + 2].header) if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() + blockchain.blocks[b + 2].header.hash): raise TestError("Meros didn't request the BlockBody.") rpc.meros.blockBody(blockchain.blocks[b + 2]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the header.") if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't verify this Block's data.") #Verify its Merit is locked. #Theoretically, all code after this check is unecessary. #Meros verifies a Block's Data after updating its State. #Therefore, if the above last Block had its Data verified, this issue should be closed. #That said, the timing is a bit too tight for comfort. #Better safe than sorry. Hence why the code after this check exists. if rpc.call("merit", "getMerit", [0])["status"] != "Locked": raise TestError("Merit wasn't locked when it was supposed to be.") #Send it a Transaction and make sure Meros verifies it, despite having its Merit locked. data = Data(data.hash, edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't send us its SignedVerification.")
def HundredSixBlockElementsTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/HundredSix/BlockElements.json", "r") as file: vectors = json.loads(file.read()) #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) blocks: List[Block] = [] for block in vectors["blocks"]: blocks.append(Block.fromJSON(block)) for block in blocks: #Handshake with the node. rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Send the Block. rpc.meros.liveBlockHeader(block.header) rpc.meros.handleBlockBody(block) #Flag of if the Block's Body synced. doneSyncing: bool = len(block.body.packets) == 0 #Handle sync requests. reqHash: bytes = bytes() while True: if doneSyncing: #Sleep for a second so Meros handles the Block. sleep(1) #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. try: if rpc.call("merit", "getHeight") != 1: raise Exception() except Exception: raise TestError( "Node crashed after being sent a malformed Element." ) #Since the node didn't crash, break out of this loop to trigger the next test case. break except Exception: raise TestError("Meros sent a keep-alive.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.SketchHashRequests: if not block.body.packets: raise TestError( "Meros asked for Verification Packets from a Block without any." ) reqHash = msg[1:33] if reqHash != block.header.hash: raise TestError( "Meros asked for Verification Packets that didn't belong to the Block we just sent it." ) #Create a lookup of hash to packets. packets: Dict[int, VerificationPacket] = {} for packet in block.body.packets: packets[Sketch.hash(block.header.sketchSalt, packet)] = packet #Look up each requested packet and respond accordingly. for h in range(int.from_bytes(msg[33:37], byteorder="little")): sketchHash: int = int.from_bytes(msg[37 + (h * 8):45 + (h * 8)], byteorder="little") if sketchHash not in packets: raise TestError( "Meros asked for a non-existent Sketch Hash.") rpc.meros.packet(packets[sketchHash]) doneSyncing = True elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:33] if reqHash not in transactions.txs: raise TestError( "Meros asked for a non-existent Transaction.") rpc.meros.syncTransaction(transactions.txs[reqHash]) else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node so we can test the next invalid Block. rpc.reset()
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 HundredFiftyFiveTest(rpc: RPC) -> None: edPrivKeys: List[ed25519.SigningKey] = [ ed25519.SigningKey(b'\0' * 32), ed25519.SigningKey(b'\1' * 32) ] edPubKeys: List[ed25519.VerifyingKey] = [ edPrivKeys[0].get_verifying_key(), edPrivKeys[1].get_verifying_key() ] blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMiner"))) blsPubKey: bytes = blsPrivKey.toPublicKey().serialize() blockchain: Blockchain = Blockchain() dataFilter: SpamFilter = SpamFilter(5) #Handshake with the node. rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Call getBlockTemplate just to get an ID. #Skips the need to write a sync loop for the BlockBody. template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey.hex()]) #Mine a Block. block = Block( BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1, bytes(4), bytes(32), blsPubKey, blockchain.blocks[0].header.time + 1200, 0), BlockBody()) block.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(block) rpc.call("merit", "publishBlock", [template["id"], block.serialize().hex()]) if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader: raise TestError("Meros didn't broadcast the Block we just published.") #Ignore the Verification for the Block's Data. if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError( "Meros didn't send the SignedVerification for the Block's Data.") datas: List[Data] = [ Data(bytes(32), edPubKeys[0].to_bytes()), Data(bytes(32), edPubKeys[1].to_bytes()) ] for d in range(len(datas)): datas[d].sign(edPrivKeys[d]) datas[d].beat(dataFilter) #Send the Data and verify Meros sends it back. if rpc.meros.liveTransaction(datas[d]) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") #Verify Meros sends back a Verification. res: bytes = rpc.meros.live.recv() if MessageType(res[0]) != MessageType.SignedVerification: raise TestError("Meros didn't send a SignedVerification.") verif: SignedVerification = SignedVerification(datas[d].hash) verif.sign(0, blsPrivKey) if res[1:] != verif.signedSerialize(): raise TestError( "Meros didn't send the correct SignedVerification.")
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[3:35] != 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 != (len(alt.blocks) - 35): blockList.append(alt.blocks[b].header.hash) b -= 1 rpc.meros.blockList(blockList) diff = -20 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 #Advance the chain far enough to switch to the new key. diff = -20 while diff != -11: 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 #Cause the reorganization to fail. if MessageType( rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest: raise TestError("Meros didn't request a BlockBody.") rpc.meros.dataMissing() sleep(65) rpc.meros.liveConnect(main.blocks[0].header.hash) rpc.meros.syncConnect(main.blocks[0].header.hash) #Sync back the regular chain. rpc.meros.liveBlockHeader(main.blocks[400].header) if MessageType( rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest: raise TestError("Meros didn't request the Block list.") blockList = [] b = 398 while b != 380: blockList.append(main.blocks[b].header.hash) b -= 1 rpc.meros.blockList(blockList) for b in range(391, 400): if rpc.meros.sync.recv() != ( MessageType.BlockHeaderRequest.toByte() + main.blocks[b].header.hash): raise TestError("Meros didn't request the BlockHeader.") rpc.meros.syncBlockHeader(main.blocks[b].header) for b in range(391, 401): if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() + main.blocks[b].header.hash): raise TestError("Meros didn't request the BlockBody.") rpc.meros.blockBody(main.blocks[b])
def BusyTest(meros: Meros) -> None: #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Handshake with the node. 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 = meros.sync.recv() if MessageType(res[0]) == MessageType.Syncing: 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. meros.sync.send(MessageType.Peers.toByte() + bytes.fromhex("017F000001") + busyServer.getsockname()[1].to_bytes(2, "little")) #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, "little") + (254).to_bytes(1, "little") + (128).to_bytes(1, "little") + meros.tcp.to_bytes(2, "little") + 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, "little")) busy += 1 if busy == 2: busyServer.close() break #Make sure Meros connects to the server we redirected to. with raises(SuccessError): 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, "little") + (254).to_bytes(1, "little") + (128).to_bytes(1, "little") + meros.tcp.to_bytes(2, "little") + 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.")
def TwoHundredThirtyTwoTest( rpc: RPC ) -> None: chains: Dict[str, List[Dict[str, Any]]] with open("e2e/Vectors/Merit/Reorganizations/TwoHundredThirtyTwo.json", "r") as file: chains = json.loads(file.read()) main: Blockchain = Blockchain.fromJSON(chains["main"]) alt: Blockchain = Blockchain.fromJSON(chains["alt"]) def sendBlock( toSend: Block ) -> None: rpc.meros.liveBlockHeader(toSend.header) rpc.meros.handleBlockBody(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 MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockListRequest: raise TestError("Meros didn't ask for the Block List of the alternate chain.") rpc.meros.blockList([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 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 TwoHundredFourTest( rpc: RPC ) -> None: file: IO[Any] = open("e2e/Vectors/Consensus/TwoHundredFour.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Instantiate a Blockchain to set the RandomX key. chain: Blockchain = Blockchain() blank: Block = Block.fromJSON(vectors["blank"]) for blockJSON in vectors["blocks"]: block: Block = Block.fromJSON(blockJSON) rpc.meros.liveConnect(chain.last()) rpc.meros.syncConnect(chain.last()) #Send a blank block so Meros acknowledges a holder. sentHeader: bytes = rpc.meros.liveBlockHeader(blank.header) if rpc.meros.sync.recv() != MessageType.BlockBodyRequest.toByte() + blank.header.hash: raise TestError("Meros didn't request the body for a blank Block.") rpc.meros.blockBody(blank) if rpc.meros.live.recv() != sentHeader: raise TestError("Meros didn't rebroadcast the header for a blank Block.") with raises(SuccessError): rpc.meros.liveBlockHeader(block.header) sentProblem: bool = False while True: if sentProblem: #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: #Verify the node didn't crash. sleep(1) if rpc.meros.process.poll() is not None: raise TestError("Node crashed trying to handle a VerificationPacket with no holders.") raise SuccessError("Node disconnected us after we sent a VerificationPacket with no holders.") except Exception: raise TestError("Meros didn't disconnect us after sending a VerificationPacket with no holders; it also didn't crash.") msg: bytes = rpc.meros.sync.recv() if MessageType(msg[0]) == MessageType.BlockBodyRequest: if msg[1 : 33] != 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) #If this Block has no packets, we've already sent the problem. sentProblem = not block.body.packets elif MessageType(msg[0]) == MessageType.SketchHashRequests: if msg[1 : 33] != block.header.hash: raise TestError("Meros asked for Verification Packets that didn't belong to the Block we just sent it.") if int.from_bytes(msg[33 : 37], byteorder="little") != 1: raise TestError("Meros didn't ask for one VerificationPacket.") if int.from_bytes(msg[37 : 45], byteorder="little") != Sketch.hash(block.header.sketchSalt, block.body.packets[0]): raise TestError("Meros didn't ask for the VerificationPacket.") rpc.meros.packet(block.body.packets[0]) sentProblem = True else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node for the next test case. rpc.reset()
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())