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 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 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 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 sendMeritRemoval() -> None: #Send and verify the MeritRemoval. removalBytes: bytes = rpc.meros.signedElement(removal) done: bool = False while True: try: msg: bytes = rpc.meros.recv() except TestError: raise TestError("Node disconnected us.") if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif MessageType(msg[0]) == MessageType.TransactionRequest: rpc.meros.transaction(transactions.txs[msg[1 : 33]]) elif MessageType(msg[0]) == MessageType.SyncingOver: if done: break done = True if removalBytes != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 11, 11, removal.holder, True)
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 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 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 HundredSixBlockElementsTest(rpc: RPC) -> None: #Load the vectors. file: IO[Any] = open( "PythonTests/Vectors/Consensus/HundredSix/BlockElements.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Transactions. transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) #Parse the Blocks from the vectors. blocks: List[Block] = [] for block in vectors["blocks"]: blocks.append(Block.fromJSON({}, block)) for block in blocks: #Handshake with the node. rpc.meros.connect(254, 254, blockchain.blocks[0].header.hash) #Send the Block. 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.") #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 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.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]) 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 an invalid holder.") else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Reset the node. rpc.reset()
def EightyEightTest(rpc: RPC) -> None: #Ed25519 key. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS key. blsPrivKey: PrivateKey = PrivateKey( blake2b(b'\0', digest_size=32).digest()) blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex() #Blocks. file: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(file.read()) file.close() #Merit. merit: Merit = Merit() #Spam Filter. dataFilter: SpamFilter = SpamFilter(bytes.fromhex("CC" * 32)) #Handshake with the node. rpc.meros.connect(254, 254, merit.blockchain.blocks[0].header.hash) #Send the first Block. block: Block = Block.fromJSON(merit.blockchain.keys, blocks[0]) merit.blockchain.add(block) rpc.meros.blockHeader(block.header) #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.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.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #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.transaction(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( merit.state.nicks, packets): raise TestError( "Block template doesn't have both Verification Packets.") #Mine the Block. block = Block( BlockHeader( 0, block.header.hash, BlockHeader.createContents(merit.state.nicks, packets), 1, template["header"][-43:-39], BlockHeader.createSketchCheck(template["header"][-43:-39], packets), 0, int.from_bytes(template["header"][-4:], byteorder="big"), ), 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(merit.state.nicks, 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) #Publish it. rpc.call("merit", "publishBlock", [ template["id"], (template["header"] + block.header.proof.to_bytes(4, byteorder="big") + block.header.signature + block.body.serialize( merit.state.nicks, block.header.sketchSalt, len(packets))).hex() ]) #Verify the Blockchain. verifyBlockchain(rpc, merit.blockchain)
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())
def HundredTwentyFourTest( rpc: RPC ) -> None: #Load the vectors. file: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") vectors: List[Dict[str, Any]] = json.loads(file.read()) file.close() #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Parse the Blocks from the vectors. for i in range(2): blockchain.add(Block.fromJSON({}, vectors[i])) #Handshake with the node. rpc.meros.connect(254, 254, blockchain.blocks[0].header.hash) #Send another handshake with the latest block as the tip. rpc.meros.send( MessageType.Handshake.toByte() + (254).to_bytes(1, "big") + (254).to_bytes(1, "big") + b'\0' + blockchain.last(), False ) #Verify Meros responds with their tail (the genesis). if rpc.meros.recv() != MessageType.BlockchainTail.toByte() + blockchain.blocks[0].header.hash: raise TestError("Meros didn't respond with its Blockchain's Tail.") #Handle sync requests. reqHash: bytes = bytes() bH: int = 0 bB: int = 1 while True: msg: bytes = rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.syncingAcknowledged() elif MessageType(msg[0]) == MessageType.BlockListRequest: reqHash = msg[3 : 51] 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.blockHeader(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 elif MessageType(msg[0]) == MessageType.SyncingOver: if bB == 3: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(rpc, blockchain)
def live(self) -> None: #Handshake with the node. self.rpc.meros.connect(254, 254, self.merit.blockchain.blocks[0].header.hash) #Send each Block. for b in range(1, len(self.merit.blockchain.blocks)): #Grab the Block. block: Block = self.merit.blockchain.blocks[b] #Send the Block. self.rpc.meros.blockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: msg: bytes = self.rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: self.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. self.rpc.meros.blockBody(self.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. self.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.") self.rpc.meros.packet(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 non-existent Transaction.") self.rpc.meros.transaction(self.transactions.txs[reqHash]) elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #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 live(self) -> None: #Handshake with the node. self.rpc.meros.connect(254, 254, 1) #Send each Block. for b in range(1, len(self.blockchain.blocks)): #Grab the Block. block: Block = self.blockchain.blocks[b] #Send the Block. self.rpc.meros.blockHeader(block.header) #Handle sync requests. reqHash: bytes = bytes() while True: msg: bytes = self.rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: self.rpc.meros.acknowledgeSyncing() elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:49] if reqHash != block.header.hash: raise TestError( "Meros asked for a Block Body that didn't belong to the header we just sent it." ) #Send the BlockBody. self.rpc.meros.blockBody(block.body) elif MessageType(msg[0]) == MessageType.ElementRequest: holder: bytes = msg[1:49] nonce: int = int.from_bytes(msg[49:53], "big") if self.consensus is None: raise TestError( "Meros asked for an Element when we have none.") if holder not in self.consensus.holders: raise TestError( "Meros asked for an Element from a holder we don't have." ) if nonce >= len(self.consensus.holders[holder]): raise TestError( "Meros asked for an Element we don't have.") self.rpc.meros.element( self.consensus.holders[holder][nonce]) elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:49] 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.") self.rpc.meros.transaction(self.transactions.txs[reqHash]) elif MessageType(msg[0]) == MessageType.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #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.blockchain) #Verify the Consensus. if self.consensus is not None: verifyConsensus(self.rpc, self.consensus) #Verify the Transactions. if self.transactions is not None: verifyTransactions(self.rpc, self.transactions) #Reset the RPC. self.rpc.reset()
def HundredFiftyFiveTest(rpc: RPC) -> None: #Ed25519 keys. 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() ] #BLS keys. blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMiner"))) blsPubKey: bytes = blsPrivKey.toPublicKey().serialize() #Blockchain. blockchain: Blockchain = Blockchain() #Spam Filter. 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) #Publish it. rpc.call("merit", "publishBlock", [template["id"], block.serialize().hex()]) #Handle the fact Meros will now broadcast it to us. if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader: raise TestError("Meros didn't broadcast the Block it just published.") #Create the Datas. datas: List[Data] = [ Data(bytes(32), edPubKeys[0].to_bytes()), Data(bytes(32), edPubKeys[1].to_bytes()) ] for d in range(len(datas)): #Sign, and mine the Data. 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 live(self) -> 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)): #Grab the Block. 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: pendingPackets.append(packet.hash) if packet.hash not in self.rpc.meros.sentTXs: pendingTXs.append(packet.hash) for elem in block.body.elements: if isinstance(elem, MeritRemoval): #pylint: disable=consider-merging-isinstance if ((isinstance(elem.e1, Verification) or isinstance(elem.e1, 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) or isinstance(elem.e2, VerificationPacket)) and (elem.e2.hash not in self.rpc.meros.sentTXs) and (elem.e2.hash not in pendingTXs)): pendingTXs.append(elem.e2.hash) #Send the Block. self.rpc.meros.liveBlockHeader(block.header) #Handle sync requests. 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: #If we already sent the body, raise. if not pendingBody: raise TestError( "Meros asked for the same Block Body multiple times." ) #Verify Meros asked for the right Block Body. 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. self.rpc.meros.blockBody(block) #Mark the body as sent. pendingBody = False 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. self.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.") 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 TElementTest(rpc: RPC) -> None: #BLS key. blsPrivKey: PrivateKey = PrivateKey( blake2b(b'\0', digest_size=32).digest()) blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex() #Blocks. file: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(file.read()) file.close() #Merit. merit: Merit = Merit() #Handshake with the node. rpc.meros.connect(254, 254, merit.blockchain.blocks[0].header.hash) #Send the first Block. block: Block = Block.fromJSON(merit.blockchain.keys, blocks[0]) merit.blockchain.add(block) rpc.meros.blockHeader(block.header) #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.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.SyncingOver: pass elif MessageType(msg[0]) == MessageType.BlockHeader: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Create and transmit a DataDifficulty. dataDiff: SignedDataDifficulty = SignedDataDifficulty( bytes.fromhex("00" * 32), 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( merit.state.nicks, [], [dataDiff]): raise TestError("Block template doesn't have the Data Difficulty.") #Mine the Block. block = Block( BlockHeader( 0, block.header.hash, BlockHeader.createContents(merit.state.nicks, [], [dataDiff]), 1, template["header"][-43:-39], BlockHeader.createSketchCheck(template["header"][-43:-39], []), 0, int.from_bytes(template["header"][-4:], byteorder="big"), ), BlockBody([], [dataDiff], dataDiff.signature)) if block.header.serializeHash()[:-4] != template["header"]: raise TestError("Failed to recreate the header.") if block.body.serialize(merit.state.nicks, 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="big") + block.header.signature + block.body.serialize( merit.state.nicks, block.header.sketchSalt)).hex() ]) #Create and transmit a new DataDifficulty. dataDiff = SignedDataDifficulty(bytes.fromhex("AA" * 32), 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( merit.state.nicks, [], [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(bytes.fromhex("BB" * 32), 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(bytes.fromhex("AA" * 32), 1, 0), SignedDataDifficulty(bytes.fromhex("BB" * 32), 1, 0), False) template = rpc.call("merit", "getBlockTemplate", [blsPubKey]) template["header"] = bytes.fromhex(template["header"]) if template["header"][36:68] != BlockHeader.createContents( merit.state.nicks, [], [mr]): raise TestError("Block template doesn't have the Merit Removal.") #Mine the Block. block = Block( BlockHeader( 0, block.header.hash, BlockHeader.createContents(merit.state.nicks, [], [mr]), 1, template["header"][-43:-39], BlockHeader.createSketchCheck(template["header"][-43:-39], []), 0, int.from_bytes(template["header"][-4:], byteorder="big"), ), BlockBody([], [mr], Signature.aggregate(signatures))) if block.header.serializeHash()[:-4] != template["header"]: raise TestError("Failed to recreate the header.") if block.body.serialize(merit.state.nicks, 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="big") + block.header.signature + block.body.serialize( merit.state.nicks, block.header.sketchSalt)).hex() ]) #Verify the Blockchain. verifyBlockchain(rpc, merit.blockchain)
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.")
def sync(self) -> None: #Handshake with the node. self.rpc.meros.connect(254, 254, self.settings["height"] + 1) #Handle sync requests. reqHash: bytes = bytes() lastBlock: int = 0 hadTips: bool = False while True: msg: bytes = self.rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: self.rpc.meros.acknowledgeSyncing() elif MessageType(msg[0]) == MessageType.GetBlockHash: height: int = int.from_bytes(msg[1:5], "big") if height == 0: self.rpc.meros.blockHash(self.blockchain.blocks[ self.settings["height"]].header.hash) else: if height > self.settings["height"]: raise TestError( "Meros asked for a Block Hash we don't have.") self.rpc.meros.blockHash( self.blockchain.blocks[height].header.hash) elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:49] for block in self.blockchain.blocks: if block.header.hash == reqHash: self.rpc.meros.blockHeader(block.header) break if block.header.hash == self.blockchain.blocks[ self.settings["height"]].header.hash: raise TestError( "Meros asked for a Block Header we don't have.") elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:49] if self.tips != {}: raise TestError( "Meros requested a new BlockBody despite not finishing syncing the existing tips." ) for block in self.blockchain.blocks: if block.header.hash == reqHash: #Update the tips. hadTips = self.updateTips(block.header.nonce, block.body.records) #Send the BlockBody. self.rpc.meros.blockBody(block.body) lastBlock = block.header.nonce + 1 #We check to load the tail tips AFTER an ElementRequest, #Therefore, if there aren't any tips before in in the second to last block, we won't load them. #This handles that case. if ((not hadTips) and (lastBlock == self.settings["height"])): self.loadTailTips() break if block.header.hash == self.blockchain.blocks[ self.settings["height"]].header.hash: raise TestError( "Meros asked for a Block Body we don't have.") elif MessageType(msg[0]) == MessageType.ElementRequest: holder: bytes = msg[1:49] nonce: int = int.from_bytes(msg[49:53], "big") if self.consensus is None: raise TestError( "Meros asked for an Element when we have none.") if holder not in self.consensus.holders: raise TestError( "Meros asked for an Element from a holder we don't have." ) if nonce >= len(self.consensus.holders[holder]): raise TestError( "Meros asked for an Element we don't have.") if holder not in self.tips: raise TestError( "Meros asked for an Element from a holder we haven't mentioned/they already fully synced." ) self.rpc.meros.element(self.consensus.holders[holder][nonce]) if nonce == self.tips[holder]: del self.tips[holder] #If this is the Block before the tail, and tips is empty, correct the tips/TXs. if ((lastBlock == self.settings["height"]) and (self.tips == {})): self.loadTailTips() elif MessageType(msg[0]) == MessageType.TransactionRequest: reqHash = msg[1:49] 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/they already synced." ) self.rpc.meros.transaction(self.transactions.txs[reqHash]) self.synced[reqHash] = True del self.txs[reqHash] elif MessageType(msg[0]) == MessageType.SyncingOver: #Break out of the foor loop if the sync finished. #This means we sent every Block, every Element, every Transaction... if ((lastBlock == self.settings["height"]) and (self.tips == {}) and (self.txs == {})): #Make sure we handled the tail tips. handled: bool = True for holder in self.tailTips: if holder not in self.allTips: handled = False break if self.allTips[holder] != self.tailTips[holder]: handled = False break if handled: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Blockchain. verifyBlockchain(self.rpc, self.blockchain) #Verify the Consensus. try: verifyConsensus(self.rpc, self.consensus) except AttributeError: pass #Verify the Transactions. try: verifyTransactions(self.rpc, self.transactions) except AttributeError: pass if self.settings["playback"]: #Playback their messages. self.rpc.meros.playback()
def VParsableTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/Verification/Parsable.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Blockchain. blockchain: Blockchain = Blockchain.fromJSON( b"MEROS_DEVELOPER_NETWORK", 60, int( "FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16), vectors["blockchain"]) #Consensus. consensus: Consensus = Consensus( bytes.fromhex( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ), bytes.fromhex( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" ), ) consensus.add(SignedVerification.fromJSON(vectors["verification"])) #Transactions. transactions: Transactions = Transactions() transactions.add(Data.fromJSON(vectors["data"])) #Handshake with the node. rpc.meros.connect(254, 254, 3) sentLast: bool = False reqHash: bytes = bytes() msg: bytes = bytes() height: int = 0 while True: try: msg = rpc.meros.recv() except TestError as e: if (not sentLast) or (str(e) != "Node disconnected us as a peer."): raise e break if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.acknowledgeSyncing() elif MessageType(msg[0]) == MessageType.GetBlockHash: height = int.from_bytes(msg[1:5], "big") if height == 0: rpc.meros.blockHash(blockchain.last()) else: if height >= len(blockchain.blocks): raise TestError( "Meros asked for a Block Hash we do not have.") rpc.meros.blockHash(blockchain.blocks[height].header.hash) elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1:49] for block in blockchain.blocks: if block.header.hash == reqHash: rpc.meros.blockHeader(block.header) break if block.header.hash == blockchain.last(): raise TestError( "Meros asked for a Block Header we do not have.") elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1:49] for block in blockchain.blocks: if block.header.hash == reqHash: rpc.meros.blockBody(block.body) break if block.header.hash == blockchain.last(): raise TestError( "Meros asked for a Block Body we do not have.") elif MessageType(msg[0]) == MessageType.ElementRequest: holder: bytes = msg[1:49] rpc.meros.element(consensus.holders[holder][int.from_bytes( msg[49:53], "big")]) elif MessageType(msg[0]) == MessageType.TransactionRequest: sentLast = True rpc.meros.transaction(transactions.txs[msg[1:49]]) elif MessageType(msg[0]) == MessageType.SyncingOver: pass else: raise TestError("Unexpected message sent: " + msg.hex().upper())
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 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 VUnknownTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Consensus/Verification/Parsable.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) #SignedVerification. sv: SignedVerification = SignedVerification.fromJSON(vectors["verification"]) #Blockchain. blockchain: Blockchain = Blockchain.fromJSON( b"MEROS_DEVELOPER_NETWORK", 60, int("FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16), vectors["blockchain"] ) file.close() #Handshake with the node. rpc.meros.connect(254, 254, len(blockchain.blocks)) sentLast: bool = False reqHash: bytes = bytes() while True: msg: bytes = rpc.meros.recv() if MessageType(msg[0]) == MessageType.Syncing: rpc.meros.acknowledgeSyncing() elif MessageType(msg[0]) == MessageType.GetBlockHash: height: int = int.from_bytes(msg[1 : 5], "big") if height == 0: rpc.meros.blockHash(blockchain.last()) else: if height >= len(blockchain.blocks): raise TestError("Meros asked for a Block Hash we do not have.") rpc.meros.blockHash(blockchain.blocks[height].header.hash) elif MessageType(msg[0]) == MessageType.BlockHeaderRequest: reqHash = msg[1 : 49] for block in blockchain.blocks: if block.header.hash == reqHash: rpc.meros.blockHeader(block.header) break if block.header.hash == blockchain.last(): raise TestError("Meros asked for a Block Header we do not have.") elif MessageType(msg[0]) == MessageType.BlockBodyRequest: reqHash = msg[1 : 49] for block in blockchain.blocks: if block.header.hash == reqHash: rpc.meros.blockBody(block.body) break if block.header.hash == blockchain.last(): raise TestError("Meros asked for a Block Body we do not have.") elif MessageType(msg[0]) == MessageType.ElementRequest: rpc.meros.element(sv) elif MessageType(msg[0]) == MessageType.TransactionRequest: sentLast = True rpc.meros.dataMissing() elif MessageType(msg[0]) == MessageType.SyncingOver: if sentLast: break else: raise TestError("Unexpected message sent: " + msg.hex().upper()) #Verify the Verification and Block were not added. if rpc.call("consensus", "getHeight", [sv.holder.hex()]) != 0: raise TestError("Meros added an unknown Verification.") if rpc.call("merit", "getHeight") != 2: raise TestError("Meros added a Block with an unknown Verification.")