def test() -> None: recipient: ed25519.SigningKey = ed25519.SigningKey(b'\1' * 32) recipientPub: bytes = recipient.get_verifying_key().to_bytes() address: str = bech32_encode( "mr", convertbits(bytes([0]) + recipientPub, 8, 5)) otherRecipient: bytes = ed25519.SigningKey( b'\2' * 32).get_verifying_key().to_bytes() otherAddress: str = bech32_encode( "mr", convertbits(bytes([0]) + otherRecipient, 8, 5)) #Create a Send. send: Send = Send.fromJSON(vectors["send"]) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros considered an unconfirmed Transaction's outputs as UTXOs." ) verify(rpc, send.hash) #Spend it. spendingSend: Send = Send.fromJSON(vectors["spendingSend"]) if rpc.meros.liveTransaction(spendingSend) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a Transaction's inputs as spent.") #Verify with another party, so it won't be majority verified, yet will still have a Verification. mineBlock(rpc, 1) verify(rpc, spendingSend.hash, 1) #Verify it didn't create a UTXO. if rpc.call("transactions", "getUTXOs", {"address": otherAddress}) != []: raise TestError("Unverified Transaction created a UTXO.") #Finalize. for _ in range(6): mineBlock(rpc) #Check the UTXOs were created. if rpc.call("transactions", "getUTXOs", {"address": otherAddress}) != [ { "hash": spendingSend.hash.hex().upper(), "nonce": 0 } ]: raise TestError( "Meros didn't consider a finalized Transaction's outputs as UTXOs." ) raise SuccessError()
def ImpossibleFamilyTest( rpc: RPC ) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/Families/ImpossibleFamily.json", "r") as file: vectors = json.loads(file.read()) sends: List[Send] = [Send.fromJSON(send) for send in vectors["sends"]] def sendSends() -> None: for s in range(len(sends)): if rpc.meros.liveTransaction(sends[s]) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Send.") def verifyPossibleWon() -> None: if rpc.call("consensus", "getStatus", [sends[1].hash.hex()])["verified"]: raise TestError("Meros verified an impossible Transaction.") if not rpc.call("consensus", "getStatus", [sends[0].hash.hex()])["verified"]: raise TestError("Meros didn't verify the only possible Transaction.") Liver( rpc, vectors["blockchain"], Transactions.fromJSON(vectors["transactions"]), callbacks={ 42: sendSends, 48: verifyPossibleWon } ).live()
def DescendantHighestUnverifiedParentTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open( "e2e/Vectors/Consensus/Families/DescendantHighestUnverifiedParent.json", "r") as file: vectors = json.loads(file.read()) sends: List[Send] = [Send.fromJSON(send) for send in vectors["sends"]] def sendSends() -> None: for s in range(len(sends)): if rpc.meros.liveTransaction(sends[s]) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Send.") def verifyDescendantLost() -> None: for send in sends[1:]: if rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["verified"]: raise TestError( "Meros verified a beaten transaction or one of its children (one of which is impossible)." ) if not rpc.call("consensus", "getStatus", {"hash": sends[0].hash.hex()})["verified"]: raise TestError( "Meros either didn't verify the descendant or its parent.") Liver(rpc, vectors["blockchain"], Transactions.fromJSON(vectors["transactions"]), callbacks={ 45: sendSends, 51: verifyDescendantLost }).live()
def UnionedFamiliesSingleWinnerTest( rpc: RPC ) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/Families/UnionedFamiliesSingleWinner.json", "r") as file: vectors = json.loads(file.read()) sends: List[Send] = [Send.fromJSON(send) for send in vectors["sends"]] def sendSends() -> None: for s in range(len(sends)): if rpc.meros.liveTransaction(sends[s]) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Send.") def verifyUnionizingWon() -> None: for send in sends[:-1]: if rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["verified"]: raise TestError("Meros verified a transaction which was beaten by a unionizing transaction.") if not rpc.call("consensus", "getStatus", {"hash": sends[-1].hash.hex()})["verified"]: raise TestError("Meros didn't verify the verified unionizing transaction.") Liver( rpc, vectors["blockchain"], Transactions.fromJSON(vectors["transactions"]), callbacks={ 45: sendSends, 51: verifyUnionizingWon } ).live()
def UnionedFamiliesMultipleWinnersTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open( "e2e/Vectors/Consensus/Families/UnionedFamiliesMultipleWinners.json", "r") as file: vectors = json.loads(file.read()) sends: List[Send] = [Send.fromJSON(send) for send in vectors["sends"]] def sendSends() -> None: for s in range(len(sends)): if rpc.meros.liveTransaction(sends[s]) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Send.") def verifyMultipleWon() -> None: for send in [sends[1], *sends[3:]]: if rpc.call("consensus", "getStatus", [send.hash.hex()])["verified"]: raise TestError( "Meros verified a transaction which was beaten by another transaction." ) for send in [sends[0], sends[2]]: if not rpc.call("consensus", "getStatus", [send.hash.hex()])["verified"]: raise TestError( "Meros didn't verify the verified transaction for each original family." ) Liver(rpc, vectors["blockchain"], Transactions.fromJSON(vectors["transactions"]), callbacks={ 44: sendSends, 50: verifyMultipleWon }).live()
def test() -> None: recipient: Ristretto.SigningKey = Ristretto.SigningKey(b'\1' * 32) recipientPub: bytes = recipient.get_verifying_key() address: str = bech32_encode("mr", convertbits(bytes([0]) + recipientPub, 8, 5)) #Create a Send. send: Send = Send.fromJSON(vectors["send"]) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") verify(rpc, send.hash) if rpc.call("transactions", "getUTXOs", {"address": address}) != [{"hash": send.hash.hex().upper(), "nonce": 0}]: raise TestError("Meros didn't consider a confirmed Transaction's outputs as UTXOs.") #Spend it, with a newer Mint as an input as well so we can prune it without pruning the original. newerSend: Send = createSend(rpc, [Claim.fromJSON(vectors["newerMintClaim"])], recipientPub) _: Send = createSend(rpc, [send, newerSend], bytes(32), recipient) if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError("Meros thinks the recipient has UTXOs.") #Remove the spending Send by pruning its ancestor (a Mint). reorg(rpc, Blockchain.fromJSON(vectors["blocksWithoutNewerMint"])) #Meros should add back its parent as an UTXO. if rpc.call("transactions", "getUTXOs", {"address": address}) != [{"hash": send.hash.hex().upper(), "nonce": 0}]: raise TestError("Meros didn't consider a Transaction without spenders as an UTXO.") #Remove the original Send and verify its outputs are no longer considered UTXOs. reorg(rpc, Blockchain.fromJSON(vectors["blocksWithoutOlderMint"])) if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError("Meros didn't remove the outputs of a pruned Transaction as UTXOs.") raise SuccessError()
def checkSend(rpc: RPC, sendHash: str, expected: Dict[str, Any]) -> None: send: Dict[str, Any] = rpc.call("transactions", "getTransaction", {"hash": sendHash}) serialized: bytes = Send.fromJSON(send).serialize() del send["signature"] del send["proof"] expected["descendant"] = "Send" expected["hash"] = sendHash if sortUTXOs(send["inputs"]) != sortUTXOs(expected["inputs"]): raise TestError("Send inputs weren't as expected.") del send["inputs"] del expected["inputs"] if send != expected: raise TestError("Send wasn't as expected.") if rpc.meros.live.recv() != (MessageType.Send.toByte() + serialized): raise TestError("Meros didn't broadcast a Send it created.")
def TGUImmediatelyTest(rpc: RPC) -> None: recipient: ed25519.SigningKey = ed25519.SigningKey(b'\1' * 32) recipientPub: bytes = recipient.get_verifying_key().to_bytes() address: str = bech32_encode("mr", convertbits(bytes([0]) + recipientPub, 8, 5)) vectors: Dict[str, Any] with open("e2e/Vectors/RPC/Transactions/GetUTXOs.json", "r") as file: vectors = json.loads(file.read()) transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) send: Send = Send.fromJSON(vectors["send"]) spendingSend: Send = Send.fromJSON(vectors["spendingSend"]) def start() -> None: #Send the Send. if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros considered an unconfirmed Transaction's outputs as UTXOs." ) #Immediately spend it. if rpc.meros.liveTransaction(spendingSend) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a Transaction's inputs as spent.") #Verify the Send and make sure it's not considered as a valid UTXO. verify(rpc, send.hash) if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros considered a just confirmed Transaction with a spender's outputs as UTXOs." ) def verified() -> None: #Verify the spender and verify the state is unchanged. verify(rpc, spendingSend.hash) if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a verified Transaction's inputs as spent." ) def finalizedSend() -> None: #Sanity check the spending TX has yet to also finalize. if rpc.call("consensus", "getStatus", {"hash": spendingSend.hash.hex()})["finalized"]: raise Exception( "Test meant to only finalize the first Send, not both.") #Verify the state is unchanged. if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a verified Transaction's inputs as spent after the input finalized." ) def finalizedSpendingSend() -> None: #Verify the state is unchanged. if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a finalized Transaction's inputs as spent." ) Liver(rpc, vectors["blockchain"], transactions, { 50: start, 51: verified, 56: finalizedSend, 57: finalizedSpendingSend }).live()
def BeatenTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/Beaten.json", "r") as file: vectors = json.loads(file.read()) sends: List[Send] = [Send.fromJSON(send) for send in vectors["sends"]] verif: SignedVerification = SignedVerification.fromSignedJSON( vectors["verification"]) #Used to get the Block Template. blsPubKey: str = PrivateKey(0).toPublicKey().serialize().hex() def sendSends() -> None: for send in sends[:4]: if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Send.") if rpc.meros.signedElement(verif) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast a Verification.") #Sanity check to verify the Block Template contains the Verification. def verifyTemplate() -> None: if bytes.fromhex( rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey })["header"])[36:68] != BlockHeader.createContents( [VerificationPacket(sends[2].hash, [1])]): raise TestError( "Meros didn't add a SignedVerification to the Block Template.") def verifyBeaten() -> None: #Verify beaten was set. The fourth Transaction is also beaten, yet should be pruned. #That's why we don't check its status. for send in sends[1:3]: if not rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["beaten"]: raise TestError( "Meros didn't mark a child and its descendant as beaten.") #Check the pending Verification for the beaten descendant was deleted. if ((rpc.call("consensus", "getStatus", {"hash": sends[2].hash.hex()})["verifiers"] != [0]) or (bytes.fromhex( rpc.call("merit", "getBlockTemplate", {"miner": blsPubKey})["header"])[36:68] != bytes(32))): raise TestError("Block template still has the Verification.") #Verify the fourth Transaction was pruned. with raises(TestError): rpc.call("transactions", "getTransaction", {"hash": sends[3].hash.hex()}) #Verify neither the second or third Transaction tree can be appended to. #Publishes a never seen-before Send for the descendant. #Re-broadcasts the pruned Transaction for the parent. for send in sends[3:]: #Most of these tests use a socket connection for this. #This has identical effects, returns an actual error instead of a disconnect, #and doesn't force us to wait a minute for our old socket to be cleared. with raises(TestError): rpc.call("transactions", "publishTransaction", { "type": "Send", "transaction": send.serialize().hex() }) #Not loaded above as it can only be loqaded after the chain starts, which is done by the Liver. #RandomX cache keys and all that. blockWBeatenVerif: Block = Block.fromJSON( vectors["blockWithBeatenVerification"]) #The following code used to test behavior which was removed, in order to be more forgiving for nodes a tad behind. #Verify we can't add that SignedVerification now. #rpc.meros.signedElement(verif) #try: # rpc.meros.live.recv() # #Hijacks a random Exception type for our purposes. # raise MessageException("Meros didn't disconnect us after we sent a Verification for a beaten Transaction.") #except TestError: # pass #except MessageException as e: # raise TestError(e.message) #sleep(65) #rpc.meros.liveConnect(blockWBeatenVerif.header.last) #Verify we can't add a Block containing that Verification. rpc.meros.liveBlockHeader(blockWBeatenVerif.header) #BlockBody sync request. rpc.meros.handleBlockBody(blockWBeatenVerif) #Sketch hash sync request. hashReqs: bytes = rpc.meros.sync.recv()[37:] for h in range(0, len(hashReqs), 8): for packet in blockWBeatenVerif.body.packets: if int.from_bytes(hashReqs[h:h + 8], byteorder="little") == Sketch.hash( blockWBeatenVerif.header.sketchSalt, packet): rpc.meros.packet(packet) break try: rpc.meros.live.recv() raise MessageException( "Meros didn't disconnect us after we sent a Block containing a Verification of a beaten Transaction." ) except TestError: pass except MessageException as e: raise TestError(e.message) sleep(65) rpc.meros.liveConnect(blockWBeatenVerif.header.last) rpc.meros.syncConnect(blockWBeatenVerif.header.last) Liver(rpc, vectors["blockchain"], Transactions.fromJSON(vectors["transactions"]), callbacks={ 42: sendSends, 43: verifyTemplate, 48: verifyBeaten }).live()
def test() -> None: recipient: ed25519.SigningKey = ed25519.SigningKey(b'\1' * 32) recipientPub: bytes = recipient.get_verifying_key().to_bytes() address: str = bech32_encode( "mr", convertbits(bytes([0]) + recipientPub, 8, 5)) otherRecipient: bytes = ed25519.SigningKey( b'\2' * 32).get_verifying_key().to_bytes() otherAddress: str = bech32_encode( "mr", convertbits(bytes([0]) + otherRecipient, 8, 5)) #Create a Send. send: Send = Send.fromJSON(vectors["send"]) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros considered an unconfirmed Transaction's outputs as UTXOs." ) verify(rpc, send.hash) #Finalize the parent. for _ in range(6): mineBlock(rpc) #Spend it. spendingSend: Send = Send.fromJSON(vectors["spendingSend"]) if rpc.meros.liveTransaction(spendingSend) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") verify(rpc, spendingSend.hash) if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a verified Transaction's inputs as spent." ) if rpc.call("transactions", "getUTXOs", {"address": otherAddress}) != [ { "hash": spendingSend.hash.hex().upper(), "nonce": 0 } ]: raise TestError( "Meros didn't consider a verified Transaction's outputs as UTXOs." ) #Unverify the spending Send. This would also unverify the parent if it wasn't finalized. #This is done via causing a Merit Removal. #Uses two competing Datas to not change the Send's status to competing. datas: List[Data] = [Data(bytes(32), recipientPub)] for _ in range(2): datas.append(Data(datas[0].hash, datas[-1].hash)) for data in datas: data.sign(recipient) data.beat(SpamFilter(5)) if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Data.") verify(rpc, data.hash, mr=(datas[-1].hash == data.hash)) #Verify the MeritRemoval happened and the spending Send is no longer verified. #These first two checks are more likely to symbolize a failure in testing methodology than Meros. if not rpc.call("merit", "getMerit", {"nick": 0})["malicious"]: raise TestError("Meros didn't create a Merit Removal.") if not rpc.call("consensus", "getStatus", {"hash": send.hash.hex()})["verified"]: raise TestError("Finalized Transaction became unverified.") if rpc.call("consensus", "getStatus", {"hash": spendingSend.hash.hex()})["verified"]: raise TestError( "Meros didn't unverify a Transaction which is currently below the required threshold." ) #Even after unverification, since the Transaction still exists, the input shouldn't be considered a UTXO. if rpc.call("transactions", "getUTXOs", {"address": address}) != []: raise TestError( "Meros didn't consider a unverified yet existing Transaction's inputs as spent." ) #That said, its outputs should no longer be considered a UTXO. if rpc.call("transactions", "getUTXOs", {"address": otherAddress}) != []: raise TestError( "Meros considered a unverified Transaction's outputs as UTXOs." ) raise SuccessError()
def GetBlockTest(rpc: RPC) -> None: blockchain: Blockchain claim: Claim send: Send datas: List[Data] txKey: Callable[[Dict[str, Any]], str] = lambda tx: tx["hash"] def verify() -> None: for b in range(len(blockchain.blocks)): block: Dict[str, Any] = rpc.call( "merit", "getBlock", {"block": blockchain.blocks[b].header.hash.hex().upper()}, False) if rpc.call("merit", "getBlock", {"block": b}, False) != block: raise TestError( "Meros reported different Blocks depending on if nonce/hash indexing." ) #Python doesn't keep track of the removals. #That said, they should all be empty except for the last one. if b != (len(blockchain.blocks) - 1): if block["removals"] != []: raise TestError("Meros reported the Block had removals.") del block["removals"] if blockchain.blocks[b].toJSON() != block: raise TestError( "Meros's JSON serialization of Blocks differs from Python's." ) #Test the key serialization of the first Block. #The final Block uses a nick, hence the value in this. if rpc.call("merit", "getBlock", {"block": 1}, False)["header"]["miner"] != PrivateKey( 0).toPublicKey().serialize().hex().upper(): raise TestError("Meros didn't serialize a miner's key properly.") #Manually test the final, and most complex, block. final: Dict[str, Any] = rpc.call("merit", "getBlock", {"block": len(blockchain.blocks) - 1}, False) final["transactions"].sort(key=txKey) final["removals"].sort() if final != { "hash": blockchain.blocks[-1].header.hash.hex().upper(), "header": { "version": blockchain.blocks[-1].header.version, "last": blockchain.blocks[-1].header.last.hex().upper(), "contents": blockchain.blocks[-1].header.contents.hex().upper(), "packets": blockchain.blocks[-1].header.packetsQuantity, "sketchSalt": blockchain.blocks[-1].header.sketchSalt.hex().upper(), "sketchCheck": blockchain.blocks[-1].header.sketchCheck.hex().upper(), "miner": blockchain.blocks[-1].header.minerKey.hex().upper() if blockchain.blocks[-1].header.newMiner else blockchain.blocks[-1].header.minerNick, "time": blockchain.blocks[-1].header.time, "proof": blockchain.blocks[-1].header.proof, "signature": blockchain.blocks[-1].header.signature.hex().upper() }, "transactions": sorted([{ "hash": claim.hash.hex().upper(), "holders": [0] }, { "hash": send.hash.hex().upper(), "holders": [0, 1, 2] }, { "hash": datas[0].hash.hex().upper(), "holders": [0, 2] }, { "hash": datas[1].hash.hex().upper(), "holders": [0, 1, 3] }, { "hash": datas[2].hash.hex().upper(), "holders": [0, 1, 2, 3, 4] }, { "hash": datas[3].hash.hex().upper(), "holders": [0, 1, 2, 3] }], key=txKey), "elements": [ { "descendant": "DataDifficulty", "holder": 3, "nonce": 0, "difficulty": 8 }, { "descendant": "SendDifficulty", "holder": 0, "nonce": 0, "difficulty": 1 }, { "descendant": "DataDifficulty", "holder": 3, "nonce": 0, "difficulty": 4 }, { "descendant": "DataDifficulty", "holder": 4, "nonce": 2, "difficulty": 1 }, { "descendant": "SendDifficulty", "holder": 4, "nonce": 1, "difficulty": 3 }, { "descendant": "SendDifficulty", "holder": 2, "nonce": 1, "difficulty": 2 }, { "descendant": "DataDifficulty", "holder": 0, "nonce": 0, "difficulty": 7 }, ], "removals": [0, 3], "aggregate": blockchain.blocks[-1].body.aggregate.serialize().hex().upper() }: raise TestError("Final Block wasn't correct.") #Test invalid calls. try: rpc.call("merit", "getBlock", {"block": 100}, False) raise Exception("") except Exception as e: if str(e) != "-2 Block not found.": raise TestError( "getBlock didn't error when we used a non-existent nonce.") try: rpc.call("merit", "getBlock", {"block": -5}, False) raise Exception("") except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError( "getBlock didn't error when we used a negative (signed) integer for a nonce." ) try: rpc.call( "merit", "getBlock", { "block": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, False) raise Exception("") except Exception as e: if str(e) != "-2 Block not found.": raise TestError( "getBlock didn't error when we used a non-existent hash.") try: rpc.call("merit", "getBlock", {"block": ""}, False) raise Exception("") except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError( "getBlock didn't error when we used an invalid hash.") with open("e2e/Vectors/RPC/Merit/GetBlock.json", "r") as file: vectors: Dict[str, Any] = json.loads(file.read()) blockchain = Blockchain.fromJSON(vectors["blockchain"]) claim = Claim.fromJSON(vectors["claim"]) send = Send.fromJSON(vectors["send"]) datas = [Data.fromJSON(data) for data in vectors["datas"]] transactions: Transactions = Transactions.fromJSON( vectors["transactions"]) Liver(rpc, vectors["blockchain"], transactions, { (len(blockchain.blocks) - 1): verify }).live()