def __init__(self, genesis: bytes, blockTime: int, startDifficulty: int, lifetime: int) -> None: self.blockchain: Blockchain = Blockchain(genesis, blockTime, startDifficulty) self.state: State = State(lifetime) self.epochs = Epochs() self.mints: List[Mint] = []
def DepthOneTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Merit/Reorganizations/DepthOne.json", "r") chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read()) file.close() #Load the alternate blockchain. alt: Blockchain = Blockchain.fromJSON(chains["alt"]) #Send the alternate tip. def sendAlternateTip() -> None: header: bytes = rpc.meros.liveBlockHeader(alt.blocks[-1].header) req: bytes = rpc.meros.sync.recv() if req != (MessageType.BlockBodyRequest.toByte() + alt.blocks[-1].header.hash): raise TestError("Meros didn't request the BlockBody.") rpc.meros.blockBody(alt.blocks[-1]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the BlockHeader.") #Verify the alternate Blockchain. verifyBlockchain(rpc, alt) #Raise SuccessError so the Liver doesn't fail when verifying the original chain. raise SuccessError("Meros re-organized to the alternate chain.") #Create and execute a Liver. Liver( rpc, chains["main"], callbacks={ 3: sendAlternateTip } ).live()
def VCompetingTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/Verification/Competing.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.fromJSON( bytes.fromhex( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ), bytes.fromhex( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" ), vectors["consensus"]) #Transactions. transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) #Create and execute a Liver/Syncer. Liver(rpc, blockchain, consensus, transactions).live() Syncer(rpc, blockchain, consensus, transactions).sync()
def LANPeersTest(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) #Verify that sending a PeersRequest returns 0 peers. rpc.meros.peersRequest() if len(rpc.meros.sync.recv()) != 2: raise TestError("Meros sent peers.") #Create a new connection which identifies as a server. serverConnection: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverConnection.connect(("127.0.0.1", rpc.meros.tcp)) serverConnection.send( MessageType.Syncing.toByte() + (254).to_bytes(1, "big") + (254).to_bytes(1, "big") + (128).to_bytes(1, "big") + (6000).to_bytes(2, "big") + blockchain.blocks[0].header.hash, False) serverConnection.recv(38) sleep(1) #Verify Meros ignores us as a peer since we're only available over the local network. rpc.meros.peersRequest() res: bytes = rpc.meros.sync.recv() if len(res) != 2: raise TestError("Meros sent peers.") #Close the new connection. serverConnection.close()
def HundredTwentyFiveTest(rpc: RPC) -> None: #Meros allows connections from its own IP if they identify as 127.0.0.1. #We need to connect either through the LAN or through the public IP for this test to be valid. #The following code grabs the computer's 192 IP. lanIPFinder = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) lanIPFinder.connect(("meroscrypto.io", 443)) lanIP = lanIPFinder.getsockname()[0] lanIPFinder.close() if not (lanIP.split(".")[0] in {"10", "172", "192"}): raise Exception("Failed to get the LAN IP.") #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #Connect to Meros. connection: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect((lanIP, rpc.meros.tcp)) try: connection.send( MessageType.Syncing.toByte() + (254).to_bytes(1, "big") + (254).to_bytes(1, "big") + (128).to_bytes(1, "big") + (6000).to_bytes(2, "big") + blockchain.blocks[0].header.hash, False) if len(connection.recv(38)) == 0: raise Exception("") except Exception: raise SuccessError( "Meros closed a connection from the same IP as itself which wasn't 127.0.0.1." ) raise TestError( "Meros allowed a connection from the same IP as itself which wasn't 127.0.0.1." )
def DelayedMeritHolderTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Merit/Reorganizations/DelayedMeritHolder.json", "r") chains: Dict[str, List[Dict[str, Any]]] = json.loads(file.read()) file.close() #Load the alternate blockchain. alt: Blockchain = Blockchain.fromJSON(chains["alt"]) #Send the alternate tip. 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.") #Create and execute a Liver. Liver( rpc, chains["main"], callbacks={ 6: sendAlternateTip } ).live()
def DifficultyTest(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() #Blockchain. blockchain: Blockchain = Blockchain.fromJSON(blocks) def checkDifficulty(block: int) -> None: if int(rpc.call("merit", "getDifficulty"), 16) != blockchain.difficulties[block][0]: raise TestError("Difficulty doesn't match.") Liver(rpc, blocks, everyBlock=checkDifficulty).live()
def fromJSON(json: List[Dict[str, Any]]) -> Any: result: Merit = Merit.__new__(Merit) result.blockchain = Blockchain.fromJSON(json) result.state = State() result.epochs = Epochs() result.mints = [] for b in range(1, len(result.blockchain.blocks)): mint: Optional[Mint] = result.epochs.shift(result.state, result.blockchain, b) if mint is not None: result.mints.append(mint) result.state.add(result.blockchain, b) return result
def verifyBlockchain( rpc: RPC, blockchain: Blockchain ) -> None: #Verify the height. if rpc.call("merit", "getHeight") != len(blockchain.blocks): raise TestError("Height doesn't match.") #Verify the difficulty. if blockchain.difficulty() != int(rpc.call("merit", "getDifficulty"), 16): raise TestError("Difficulty doesn't match.") #Verify the blocks. for block in blockchain.blocks: if rpc.call("merit", "getBlock", [block.header.nonce]) != block.toJSON(): raise TestError("Block doesn't match.")
def ChainAdvancementTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(file.read()) file.close() blockchain: Blockchain = Blockchain.fromJSON( b"MEROS_DEVELOPER_NETWORK", 60, int("FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16), blocks ) #Create and execute a Liver/Syncer. Liver(rpc, blockchain).live() Syncer(rpc, blockchain).sync()
def fromJSON(genesis: bytes, blockTime: int, startDifficulty: int, lifetime: int, transactions: Transactions, consensus: Consensus, json: List[Dict[str, Any]]) -> Any: result: Merit = Merit(bytes(), 0, 0, 0) result.blockchain = Blockchain.fromJSON(genesis, blockTime, startDifficulty, json) result.state = State(lifetime) result.epochs = Epochs() for b in range(1, len(result.blockchain.blocks)): mints: List[Mint] = result.epochs.add(transactions, consensus, result.state, result.blockchain.blocks[b]) result.mints += mints result.state.add(result.blockchain, result.blockchain.blocks[b]) return result
def verifyBlockchain(rpc: RPC, blockchain: Blockchain) -> None: #Sleep to ensure data races aren't a problem. sleep(2) #Verify the height. if rpc.call("merit", "getHeight") != len(blockchain.blocks): raise TestError("Height doesn't match.") #Verify the difficulty. if blockchain.difficulty() != int(rpc.call("merit", "getDifficulty"), 16): raise TestError("Difficulty doesn't match.") #Verify the Blocks. for b in range(len(blockchain.blocks)): if rpc.call("merit", "getBlock", [b]) != blockchain.blocks[b].toJSON(): raise TestError("Block doesn't match.") if rpc.call("merit", "getBlock", [blockchain.blocks[b].header.hash.hex().upper() ]) != blockchain.blocks[b].toJSON(): raise TestError("Block doesn't match.")
def DataTest(rpc: RPC) -> None: #Get the genesis hash. genesis: bytes = Blockchain().blocks[0].header.hash #Create the Spam Filter. spamFilter: SpamFilter = SpamFilter(5) #Create the Data. data: Data = Data(bytes(32), pubKey.to_bytes()) data.sign(privKey) data.beat(spamFilter) #Handshake with the node. rpc.meros.liveConnect(genesis) #Send the Data. rpc.meros.liveTransaction(data) #Sleep for 100 milliseconds. sleep(0.1) #Verify the Data. verifyTransaction(rpc, data)
def StateTest(rpc: RPC) -> None: #Blocks. file: IO[Any] = open("PythonTests/Vectors/Merit/StateBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(file.read()) file.close() #Blockchain. blockchain: Blockchain = Blockchain.fromJSON(blocks) #State. state: State = State() def checkState(block: int) -> None: state.add(blockchain, block) for miner in state.unlocked: if rpc.call("merit", "getMerit", [miner]) != { "unlocked": True, "malicious": False, "merit": state.unlocked[miner] }: raise TestError("Merit doesn't match.") Liver(rpc, blocks, everyBlock=checkState).live()
def HundredSixSignedElementsTest(rpc: RPC) -> None: #Blockchain. Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() #BLS Key. blsPrivKey: PrivateKey = PrivateKey( blake2b(b'\0', digest_size=32).digest()) sig: Signature = blsPrivKey.sign(bytes()) #Create a Data. #This is required so the Verification isn't terminated early for having an unknown hash. data: bytes = bytes.fromhex(rpc.call("personal", "data", ["AA"])) #Create a signed Verification, SendDifficulty, and DataDifficulty. elements: List[SignedElement] = [ SignedVerification(data, 1, sig), SignedSendDifficulty(bytes.fromhex("00" * 32), 0, 1, sig), SignedDataDifficulty(bytes.fromhex("00" * 32), 0, 1, sig) ] for elem in elements: #Handshake with the node. rpc.meros.connect(254, 254, blockchain.blocks[0].header.hash) #Send the Element. rpc.meros.signedElement(elem) #Sleep for a bit. sleep(0.2) #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.")
#JSON standard lib. import json #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() #SpamFilter. spamFilter: SpamFilter = SpamFilter(5) #Blockchains. packetedChain: Blockchain = Blockchain() reorderedChain: Blockchain = Blockchain() #Generate a Block granting the holder Merit. block = Block( BlockHeader(0, packetedChain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKey.serialize(), packetedChain.blocks[-1].header.time + 1200), BlockBody()) #Mine it. block.mine(blsPrivKey, packetedChain.difficulty()) #Add it. packetedChain.add(block) reorderedChain.add(block) print("Generated Hundred Twenty Three Packet Block 1/2 " + str(len(packetedChain.blocks)) + ".")
from PythonTests.Classes.Merit.Block import Block from PythonTests.Classes.Merit.Blockchain import Blockchain #Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Miner Private Key. privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Blockchains. bbFile: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) main: Blockchain = Blockchain.fromJSON(blocks) #Only add the first 15 to the alt chain. alt: Blockchain = Blockchain() for b in range(15): alt.add(Block.fromJSON(blocks[b])) #Generate an alternative fifteen Blocks. for i in range(1, 15): #Create the next Block. block = Block( BlockHeader( 0, alt.last(), bytes(32), 1, bytes(4),
from PythonTests.Libs.BLS import PrivateKey #Merit classes. from PythonTests.Classes.Merit.BlockHeader import BlockHeader from PythonTests.Classes.Merit.BlockBody import BlockBody from PythonTests.Classes.Merit.Block import Block from PythonTests.Classes.Merit.Blockchain import Blockchain #Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blockchain. blockchain: Blockchain = Blockchain() #Miner Private Key. privKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) #Create the Block. block: Block = Block( BlockHeader(0, blockchain.last(), bytes(32), 1, bytes(4), bytes(32), privKey.toPublicKey().serialize(), blockchain.blocks[-1].header.time + 1200), BlockBody()) #Generate blocks. for i in range(1, 26): #Mine the Block. block.mine(privKey, blockchain.difficulty())
from PythonTests.Libs.BLS import PrivateKey #Merit classes. from PythonTests.Classes.Merit.BlockHeader import BlockHeader from PythonTests.Classes.Merit.BlockBody import BlockBody from PythonTests.Classes.Merit.Block import Block from PythonTests.Classes.Merit.Blockchain import Blockchain #Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blockchains. main: Blockchain = Blockchain() alt: Blockchain = Blockchain() #Miner Private Keys. privKeys: List[PrivateKey] = [ PrivateKey(blake2b(b'\0', digest_size=32).digest()), PrivateKey(blake2b(b'\1', digest_size=32).digest()) ] #Create the Block to the first miner. block: Block = Block( BlockHeader(0, main.last(), bytes(32), 1, bytes(4), bytes(32), privKeys[0].toPublicKey().serialize(), main.blocks[-1].header.time + 1200), BlockBody()) block.mine(privKeys[0], main.difficulty()) main.add(block)
#Ed25519 lib. import ed25519 #Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json cmFile: IO[Any] = open("PythonTests/Vectors/Transactions/ClaimedMint.json", "r") cmVectors: Dict[str, Any] = json.loads(cmFile.read()) #Transactions. transactions: Transactions = Transactions.fromJSON(cmVectors["transactions"]) #Blockchain. blockchain: Blockchain = Blockchain.fromJSON(cmVectors["blockchain"]) cmFile.close() #SpamFilter. sendFilter: SpamFilter = SpamFilter(3) #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() #Grab the Claim hash. claim: bytes = blockchain.blocks[-1].body.packets[0].hash
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 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 PartialTest( rpc: RPC ) -> None: file: IO[Any] = open("PythonTests/Vectors/Consensus/MeritRemoval/Partial.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"] ) #MeritRemoval. removal: PartiallySignedMeritRemoval = PartiallySignedMeritRemoval.fromJSON(vectors["removal"]) #Consensus. consensus: Consensus = Consensus( bytes.fromhex("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), bytes.fromhex("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC") ) consensus.add(removal.e1) consensus.add(removal) #Transactions. transactions: Transactions = Transactions() transactions.add(Data.fromJSON(vectors["data"])) #Create and execute a Liver to cause a Partial MeritRemoval. def sendElement() -> None: #Send the second Element. rpc.meros.signedElement(removal.se2) #Verify the MeritRemoval. if rpc.meros.recv() != (MessageType.SignedMeritRemoval.toByte() + removal.signedSerialize()): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 2, 200, removal, True) Liver( rpc, blockchain, consensus, transactions, callbacks={ 2: sendElement, 3: lambda: verifyMeritRemoval(rpc, 2, 200, removal, False) } ).live() #Create and execute a Liver to handle a Partial MeritRemoval. def sendMeritRemoval() -> None: #Send and verify the MeritRemoval. if rpc.meros.signedElement(removal) != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 2, 200, removal, True) Liver( rpc, blockchain, consensus, transactions, callbacks={ 2: sendMeritRemoval, 3: lambda: verifyMeritRemoval(rpc, 2, 200, removal, False) } ).live() #Create and execute a Syncer to handle a Partial MeritRemoval. Syncer(rpc, blockchain, consensus, transactions).sync() verifyMeritRemoval(rpc, 2, 200, removal, False)
def SameNonceTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/MeritRemoval/SameNonce.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"]) #MeritRemoval. removal: SignedMeritRemoval = SignedMeritRemoval.fromJSON( vectors["removal"]) #Consensus. consensus: Consensus = Consensus( bytes.fromhex( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ), bytes.fromhex( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" )) consensus.add(removal) #Data. data: Data = Data.fromJSON(vectors["data"]) #Create and execute a Liver to cause a SameNonce MeritRemoval. def sendElements() -> None: #Send the Data/SignedVerifications. if rpc.meros.transaction(data) != rpc.meros.recv(): raise TestError("Unexpected message sent.") if rpc.meros.signedElement(removal.se1) != rpc.meros.recv(): raise TestError("Unexpected message sent.") rpc.meros.signedElement(removal.se2) #Verify the MeritRemoval. if rpc.meros.recv() != (MessageType.SignedMeritRemoval.toByte() + removal.signedSerialize()): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal, True) Liver(rpc, blockchain, consensus, callbacks={ 1: sendElements, 2: lambda: verifyMeritRemoval(rpc, 1, 100, removal, False) }).live() #Create and execute a Liver to handle a SameNonce MeritRemoval. def sendMeritRemoval() -> None: #Send and verify the MeritRemoval. if rpc.meros.signedElement(removal) != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal, True) Liver(rpc, blockchain, consensus, callbacks={ 1: sendMeritRemoval, 2: lambda: verifyMeritRemoval(rpc, 1, 100, removal, False) }).live() #Create and execute a Syncer to handle a SameNonce MeritRemoval. Syncer(rpc, blockchain, consensus).sync() verifyMeritRemoval(rpc, 1, 100, removal, False)
def MultipleTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/MeritRemoval/Multiple.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"]) #MeritRemovals. removal1: SignedMeritRemoval = SignedMeritRemoval.fromJSON( vectors["removal1"]) removal2: SignedMeritRemoval = SignedMeritRemoval.fromJSON( vectors["removal2"]) #Data. data: Data = Data.fromJSON(vectors["data"]) #Create and execute a Liver to cause multiple MeritRemovals. def sendElements() -> None: #Send the Data. if rpc.meros.transaction(data) != rpc.meros.recv(): raise TestError("Unexpected message sent.") #Send the first SignedElement. if rpc.meros.signedElement(removal1.se1) != rpc.meros.recv(): raise TestError("Unexpected message sent.") #Send the second. rpc.meros.signedElement(removal1.se2) #Verify the first MeritRemoval. if rpc.meros.recv() != (MessageType.SignedMeritRemoval.toByte() + removal1.signedSerialize()): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal1, True) #Send the third SignedElement. rpc.meros.signedElement(removal2.se2) #Meros should treat the first created MeritRemoval as the default MeritRemoval. if rpc.meros.recv() != (MessageType.SignedMeritRemoval.toByte() + removal1.signedSerialize()): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal1, True) Liver(rpc, blockchain, callbacks={ 1: sendElements, 2: lambda: verifyMeritRemoval(rpc, 1, 100, removal2, False) }).live() #Create and execute a Liver to handle multiple MeritRemovals. def sendMeritRemovals() -> None: #Send and verify the first MeritRemoval. msg = rpc.meros.signedElement(removal1) if msg != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal1, True) #Send the second MeritRemoval. rpc.meros.signedElement(removal2) #Meros should treat the first created MeritRemoval as the default. if msg != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal1, True) Liver(rpc, blockchain, callbacks={ 1: sendMeritRemovals, 2: lambda: verifyMeritRemoval(rpc, 1, 100, removal2, False) }).live()
def ULimitTest( #pylint: disable=unused-argument rpc: RPC ) -> None: #Sleep 60 seconds so Meros can correct its FD count. sleep(60) #Create a Blockchain so we have the genesis Block hash. blockchain: Blockchain = Blockchain() #Create peers until Meros sends us busy. sockets: List[MerosSocket] = [] while True: #Only create live sockets to trigger new peers for each socket. try: sockets.append(MerosSocket(5132, 254, 254, True, blockchain.blocks[0].header.hash)) except BusyError as e: if e.handshake != (MessageType.Busy.toByte() + bytes(1)): raise TestError("Meros sent an invalid Busy message.") break #Trigger busy 32 more times to verify Meros doesn't still allocate file handles. for _ in range(32): try: MerosSocket(5132, 254, 254, True, blockchain.blocks[0].header.hash) except BusyError as e: if e.handshake != (MessageType.Busy.toByte() + bytes(1)): raise TestError("Meros sent an invalid Busy message.") continue raise TestError("Meros didn't send Busy despite being at capacity.") #Disconnect the last 50 sockets. for _ in range(50): sockets[-1].connection.shutdown(socket.SHUT_RDWR) sockets[-1].connection.close() del sockets[-1] #Send a Handshake over every remaining socket every 20 seconds for a minute. #Then Meros should update the amount of files it has open and accept 50 new sockets. for _ in range(3): for lSocket in sockets: lSocket.send( MessageType.Handshake.toByte() + (254).to_bytes(1, "big") + (254).to_bytes(1, "big") + b'\0\0\0' + blockchain.blocks[0].header.hash ) sleep(20) #Connect 50 sockets and verify Meros doesn't think it's still at capacity. for _ in range(50): try: sockets.append(MerosSocket(5132, 254, 254, True, blockchain.blocks[0].header.hash)) except BusyError: raise TestError("Meros thought it was at capcity when it wasn't.") #Verify connecting one more socket returns Busy. try: MerosSocket(5132, 254, 254, True, blockchain.blocks[0].header.hash) except BusyError: return raise TestError("Meros accepted a socket despite being at capcity.")
def __init__(self) -> None: self.blockchain: Blockchain = Blockchain() self.state: State = State() self.epochs = Epochs() self.mints: List[Mint] = []
def PendingActionsTest(rpc: RPC) -> None: file: IO[Any] = open( "PythonTests/Vectors/Consensus/MeritRemoval/PendingActions.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"]) #SignedVerifications. verifs: List[SignedVerification] = [] for verif in vectors["verifications"]: verifs.append(SignedVerification.fromJSON(verif)) #Removal. removal: SignedMeritRemoval = SignedMeritRemoval.fromJSON( vectors["removal"]) #Datas. datas: List[Data] = [] for data in vectors["datas"]: datas.append(Data.fromJSON(data)) #Send every Data/Verification. def sendDatasAndVerifications() -> None: #Send the Datas. for data in datas: if rpc.meros.transaction(data) != rpc.meros.recv(): raise TestError("Unexpected message sent.") #Send the Verifications. for verif in verifs: if rpc.meros.signedElement(verif) != rpc.meros.recv(): raise TestError("Unexpected message sent.") #Verify every Data has 100 Merit. for data in datas: if rpc.call("consensus", "getStatus", [data.hash.hex()])["merit"] != 100: raise TestError( "Meros didn't verify Transactions with received Verifications." ) #Cause a MeritRemoval. def causeMeritRemoval() -> None: #Send every Data/;Verification. sendDatasAndVerifications() #Send the problem Verification and verify the MeritRemoval. rpc.meros.signedElement(removal.se2) if rpc.meros.recv() != MessageType.SignedMeritRemoval.toByte( ) + removal.signedSerialize(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal, True) #Verify every Data has 0 Merit. for data in datas: if rpc.call("consensus", "getStatus", [data.hash.hex()])["merit"] != 0: raise TestError( "Meros didn't revert pending actions of a malicious MeritHolder." ) #Send a MeritRemoval. def sendMeritRemoval() -> None: #Send every Data/;Verification. sendDatasAndVerifications() #Send and verify the MeritRemoval. if rpc.meros.signedElement(removal) != rpc.meros.recv(): raise TestError("Meros didn't send us the Merit Removal.") verifyMeritRemoval(rpc, 1, 100, removal, True) #Verify every Data has 0 Merit. for data in datas: if rpc.call("consensus", "getStatus", [data.hash.hex()])["merit"] != 0: raise TestError( "Meros didn't revert pending actions of a malicious MeritHolder." ) #Check the Data's finalized with the proper amount of Merit and update the MeritRemoval. def check() -> None: #Verify the Datas have the Merit they should. for data in datas: if rpc.call("consensus", "getStatus", [data.hash.hex()])["merit"] != 0: raise TestError( "Meros didn't finalize with the reverted pending actions of a malicious MeritHolder." ) #Update the MeritRemoval's nonce. removal.nonce = 6 #Verify the MeritRemoval is now accessible with a nonce of 6. verifyMeritRemoval(rpc, 7, 0, removal, False) #Create and execute a Liver to cause a MeritRemoval. Liver(rpc, blockchain, callbacks={1: causeMeritRemoval, 7: check}).live() #Reset the MeritRemoval nonce. removal.nonce = 0 #Create and execute a Liver to handle a MeritRemoval. Liver(rpc, blockchain, callbacks={1: sendMeritRemoval, 7: check}).live()
#Blockchain classes. from PythonTests.Classes.Merit.BlockHeader import BlockHeader from PythonTests.Classes.Merit.BlockBody import BlockBody from PythonTests.Classes.Merit.Block import Block from PythonTests.Classes.Merit.Blockchain import Blockchain #Blake2b standard function. from hashlib import blake2b #JSON standard lib. import json #Blockchain. bbFile: IO[Any] = open("PythonTests/Vectors/Merit/BlankBlocks.json", "r") blocks: List[Dict[str, Any]] = json.loads(bbFile.read()) blockchain: Blockchain = Blockchain.fromJSON(blocks) bbFile.close() #BLS Keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Create a DataDifficulty. dataDiffs: List[SignedDataDifficulty] = [ SignedDataDifficulty(3, 0), SignedDataDifficulty(1, 1) ] for dataDiff in dataDiffs: dataDiff.sign(0, blsPrivKey) #Generate a Block containing the DataDifficulty.
#JSON standard lib. import json #Consensus. consensus: Consensus = Consensus( bytes.fromhex( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ), bytes.fromhex( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" ), ) #Blockchain. blockchain: Blockchain = Blockchain( b"MEROS_DEVELOPER_NETWORK", 60, int( "FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16)) #BLS Keys. privKey: blspy.PrivateKey = blspy.PrivateKey.from_seed(b'\0') pubKey: blspy.PublicKey = privKey.get_public_key() #Load a Multiple Block and load their MeritRemoval. snFile: IO[Any] = open( "PythonTests/Vectors/Consensus/MeritRemoval/SameNonce.json", "r") snVectors: Dict[str, Any] = json.loads(snFile.read()) blockchain.add(Block.fromJSON(snVectors["blockchain"][0])) removal1: SignedMeritRemoval = SignedMeritRemoval.fromJSON(