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 __init__(self, rpc: RPC, blockchain: List[Dict[str, Any]], transactions: Optional[Transactions] = None, callbacks: Dict[int, Callable[[], None]] = {}, everyBlock: Optional[Callable[[int], None]] = None) -> None: #RPC. self.rpc: RPC = rpc #Arguments. self.merit: Merit = Merit.fromJSON(blockchain) self.transactions: Optional[Transactions] = transactions self.callbacks: Dict[int, Callable[[], None]] = dict(callbacks) self.everyBlock: Optional[Callable[[int], None]] = everyBlock
def HundredFortySevenTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Transactions/ClaimedMint.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Merit. merit: Merit = Merit.fromJSON(vectors["blockchain"]) #Transactions. transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) #Ed25519 keys. privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) pubKey: ed25519.VerifyingKey = privKey.get_verifying_key() #Grab the Claim hash, claim: bytes = merit.blockchain.blocks[-1].body.packets[0].hash #Create a Send which underflows. send: Send = Send( [(claim, 0)], [ (pubKey.to_bytes(), 18446744073709551231), (pubKey.to_bytes(), 385 + Claim.fromTransaction(transactions.txs[claim]).amount) ] ) send.sign(privKey) send.beat(SpamFilter(3)) #Custom function to send the last Block and verify it errors at the right place. def checkFail() -> None: #Send the Send. rpc.meros.liveTransaction(send) #Handle sync requests. while True: #Try receiving from the Live socket, where Meros sends keep-alives. try: if len(rpc.meros.live.recv()) != 0: raise Exception() except TestError: raise SuccessError("Node disconnected us after we sent an invalid Transaction.") except Exception: raise TestError("Meros sent a keep-alive.") #Create and execute a Liver. Liver(rpc, vectors["blockchain"], transactions, callbacks={12: checkFail}).live()
def __init__(self, rpc: RPC, blockchain: List[Dict[str, Any]], transactions: Union[Transactions, None] = None, settings: Dict[str, Any] = {}) -> None: #RPC. self.rpc: RPC = rpc #DBs/Settings. self.merit: Merit = Merit.fromJSON(blockchain) self.transactions: Union[Transactions, None] = transactions self.settings: Dict[str, Any] = dict(settings) #Provide default settings. if "height" not in self.settings: self.settings["height"] = len(self.merit.blockchain.blocks) - 1 if "playback" not in self.settings: self.settings["playback"] = True #List of Block hashes in this Blockchain. self.blockHashes: Set[bytes] = set() for b in range(1, self.settings["height"] + 1): self.blockHashes.add(self.merit.blockchain.blocks[b].header.hash) #List of mentioned Blocks. self.blocks: List[Block] = [ self.merit.blockchain.blocks[self.settings["height"]] ] #Dict of mentioned packets. self.packets: Dict[int, VerificationPacket] = {} #Set of mentioned Transactions. self.txs: Set[bytes] = set() #Dict of synced Transactions. self.synced: Set[bytes] = set()
def VUnknownTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/Verification/Parsable.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Merit. merit: Merit = Merit.fromJSON(vectors["blockchain"]) #Custom function to send the last Block and verify it errors at the right place. 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()) #Create and execute a Liver. Liver(rpc, vectors["blockchain"], callbacks={1: checkFail}).live()
#Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blank Blocks. bbFile: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blankBlocks: List[Dict[str, Any]] = json.loads(bbFile.read()) bbFile.close() #Transactions. transactions: Transactions = Transactions() #Merit. merit: Merit = Merit() #SpamFilter. dataFilter: SpamFilter = SpamFilter(bytes.fromhex("CC" * 32)) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Add 5 Blank Blocks. for i in range(5): merit.add(Block.fromJSON(merit.blockchain.keys, blankBlocks[i]))
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 CompetingFinalizedTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Transactions/CompetingFinalized.json", "r") vectors: Dict[str, Any] = json.loads(file.read()) file.close() #Merit. merit: Merit = Merit.fromJSON(vectors["blockchain"]) #Transactions. transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) #Custom function to send the last Block and verify it errors at the right place. 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()) #Create and execute a Liver. Liver(rpc, vectors["blockchain"], transactions, callbacks={7: checkFail}).live()
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)
bbFile.close() #Transactions. transactions: Transactions = Transactions() #$Consensus. consensus: Consensus = Consensus( bytes.fromhex( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ), bytes.fromhex( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" )) #Merit. merit: Merit = Merit( b"MEROS_DEVELOPER_NETWORK", 60, int( "FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16), 100) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: blspy.PrivateKey = blspy.PrivateKey.from_seed(b'\0') blsPubKey: blspy.PublicKey = blsPrivKey.get_public_key() #Add 5 Blank Blocks. for i in range(5): merit.add(transactions, consensus, Block.fromJSON(blankBlocks[i]))