def PersonalDataTest(rpc: RPC) -> None: #Create a Data. firstData: str = rpc.call("personal", "data", {"data": "a"}) initial: str = checkData(rpc, firstData, b"a") #Meros should've also created an initial Data. if checkData(rpc, initial, decodeAddress(rpc.call( "personal", "getAddress"))) != bytes(32).hex(): raise TestError("Initial Data didn't have a 0-hash input.") #Create a Data using hex data. Also tests upper case hex. if checkData(rpc, rpc.call("personal", "data", { "data": "AABBCC", "hex": True }), b"\xAA\xBB\xCC") != firstData: raise TestError( "Newly created Data wasn't a descendant of the existing Data.") #Should support using 256 bytes of Data. Also tests lower case hex. checkData( rpc, rpc.call("personal", "data", { "data": bytes([0xaa] * 256).hex(), "hex": True }), bytes([0xaa] * 256)) #Should properly error when we input no data. All Datas must have at least 1 byte of Data. try: rpc.call("personal", "data", {"data": ""}) raise Exception() except Exception as e: if str(e) != "-3 Data is too small or too large.": raise TestError("Meros didn't handle Data that was too small.") #Should properly error when we supply more than 256 bytes of data. try: rpc.call("personal", "data", {"data": "a" * 257}) raise Exception() except Exception as e: if str(e) != "-3 Data is too small or too large.": raise TestError("Meros didn't handle Data that was too large.") #Should properly error when we supply non-hex data with the hex flag. try: rpc.call("personal", "data", {"data": "zz", "hex": True}) raise Exception() except Exception as e: if str(e) != "-3 Invalid hex char `z` (ord 122).": raise TestError("Meros didn't properly handle invalid hex.") #Should properly error when we supply non-even hex data. try: rpc.call("personal", "data", {"data": "a", "hex": True}) raise Exception() except Exception as e: if str(e) != "-3 Incorrect hex string len.": raise TestError("Meros didn't properly handle non-even hex.") #Test Datas when the Wallet has a password. rpc.call("personal", "setWallet", {"password": "******"}) #Shouldn't work due to the lack of a password. try: rpc.call("personal", "data", {"data": "abc"}) raise Exception() except Exception as e: if str(e) != "-3 Invalid password.": raise TestError( "Meros didn't properly handle creating a Data without a password." ) #Should work due to the existence of a password. lastData: str = rpc.call("personal", "data", { "data": "abc", "password": "******" }) checkData(rpc, lastData, b"abc") #Reboot the node and verify we can create a new Data without issue. rpc.quit() sleep(3) rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc) if checkData( rpc, rpc.call("personal", "data", { "data": "def", "password": "******" }), b"def") != lastData: raise TestError("Couldn't create a new Data after rebooting.")
def GetAddressTest(rpc: RPC) -> None: password: str = "password since it shouldn't be relevant" rpc.call("personal", "setWallet", {"password": password}) mnemonic: str = rpc.call("personal", "getMnemonic") #Test getAddress. Doesn't test specific indexing, as that's handled by DerivationTest. #Not only does getAddress need to correctly derive addresses along the external chain, it needs to return new addresses. #That said, new is defined by use; use on the network. If it has a TX sent to it, it's used. #This is different than checking for UTXOs because that means any address no longer having UTXOs would be considered new again. expected: str = getAddress(mnemonic, password, 0) if rpc.call("personal", "getAddress") != expected: raise TestError( "getAddress didn't return the next unused address (the first one)." ) #It should be returned again given it's still unused. if rpc.call("personal", "getAddress") != expected: raise TestError( "getAddress didn't return the same address when there was a lack of usage." ) #Reboot the node and verify consistency around the initial address. #Added due to an edge case that appeared. rpc.quit() sleep(3) rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc) if rpc.call("personal", "getAddress") != expected: raise TestError( "getAddress didn't return the initial address after a reboot.") #Send enough Blocks to have funds available to continue testing. vectors: Dict[str, Any] with open("e2e/Vectors/Transactions/ClaimedMint.json", "r") as file: vectors = json.loads(file.read()) transactions: Transactions = Transactions.fromJSON(vectors["transactions"]) def restOfTest() -> None: #Move expected into scope. expected: str = getAddress(mnemonic, password, 0) #Send to the new address, then call getAddress again. Verify a new address appears. last: Send = createSend( rpc, Claim.fromTransaction(iter(transactions.txs.values()).__next__()), expected) hashes: List[bytes] = [last.hash] expected = getAddress(mnemonic, password, 1) if rpc.call("personal", "getAddress") != expected: raise TestError( "Meros didn't move to the next address once the existing one was used." ) #Send to the new unused address, spending the funds before calling getAddress again. #Checks address usage isn't defined as having an UTXO, yet rather any TXO. #Also confirm the spending TX with full finalization before checking. #Ensures the TXO isn't unspent by any definition. last = createSend(rpc, last, expected) hashes.append(last.hash) #Spending TX. send: Send = Send([(hashes[-1], 0)], [(bytes(32), 1)]) send.signature = ed.sign(b"MEROS" + send.hash, getPrivateKey(mnemonic, password, 1)) send.beat(SpamFilter(3)) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back the spending Send.") hashes.append(send.hash) #In order to finalize, we need to mine 6 Blocks once this Transaction and its parent have Verifications. for txHash in hashes: sv: SignedVerification = SignedVerification(txHash) sv.sign(0, PrivateKey(0)) if rpc.meros.signedElement(sv) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Verification.") #Close the sockets while we mine. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() #Mine these to the Wallet on the node so we can test getMeritHolderNick. privKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMeritHolderKey"))) blockchain: Blockchain = Blockchain.fromJSON(vectors["blockchain"]) for _ in range(6): template: Dict[str, Any] = rpc.call( "merit", "getBlockTemplate", {"miner": privKey.toPublicKey().serialize().hex()}) proof: int = -1 tempHash: bytes = bytes() tempSignature: bytes = bytes() while ((proof == -1) or ((int.from_bytes(tempHash, "little") * (blockchain.difficulty() * 11 // 10)) > int.from_bytes( bytes.fromhex("FF" * 32), "little"))): proof += 1 tempHash = RandomX( bytes.fromhex(template["header"]) + proof.to_bytes(4, "little")) tempSignature = privKey.sign(tempHash).serialize() tempHash = RandomX(tempHash + tempSignature) rpc.call( "merit", "publishBlock", { "id": template["id"], "header": template["header"] + proof.to_bytes(4, "little").hex() + tempSignature.hex() }) blockchain.add( Block.fromJSON( rpc.call("merit", "getBlock", {"block": len(blockchain.blocks)}))) #Verify a new address is returned. expected = getAddress(mnemonic, password, 2) if rpc.call("personal", "getAddress") != expected: raise TestError( "Meros didn't move to the next address once the existing one was used." ) #Get a new address after sending to the address after it. #Use both, and then call getAddress. #getAddress should detect X is used, move to Y, detect Y is used, and move to Z. #It shouldn't assume the next address after an used address is unused. #Actually has two Ys as one iteration of the code only ran for the next address; not all future addresses. #Send to the next next addresses. for i in range(2): addy: str = getAddress(mnemonic, password, 3 + i) #Reopen the sockets. This isn't done outside of the loop due to the time deriving the final address can take. #Due to how slow the reference Python code is, it is necessary to redo the socket connections. sleep(65) rpc.meros.liveConnect(Blockchain().blocks[0].header.hash) rpc.meros.syncConnect(Blockchain().blocks[0].header.hash) last = createSend(rpc, last, addy) if MessageType(rpc.meros.live.recv() [0]) != MessageType.SignedVerification: raise TestError( "Meros didn't create and broadcast a SignedVerification for this Send." ) if i == 0: #Close them again. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() #Verify getAddress returns the existing next address. if rpc.call("personal", "getAddress") != expected: raise TestError( "Sending to the address after this address caused Meros to consider this address used." ) #Send to the next address. last = createSend(rpc, last, expected) if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError( "Meros didn't create and broadcast a SignedVerification for this Send." ) #Verify getAddress returns the address after the next next addresses. expected = getAddress(mnemonic, password, 5) if rpc.call("personal", "getAddress") != expected: raise TestError( "Meros didn't return the correct next address after using multiple addresses in a row." ) #Now that we have mined a Block as part of this test, ensure the Merit Holder nick is set. if rpc.call("personal", "getMeritHolderNick") != 1: raise TestError( "Merit Holder nick wasn't made available despite having one.") #Sanity check off Mnemonic. if rpc.call("personal", "getMnemonic") != mnemonic: raise TestError("getMnemonic didn't return the correct Mnemonic.") #Existing values used to test getMnemonic/getMeritHolderKey/getMeritHolderNick/getAccount/getAddress consistency. existing: Dict[str, Any] = { #Should be equal to the mnemonic variable, which is verified in a check above. "getMnemonic": rpc.call("personal", "getMnemonic"), "getMeritHolderKey": rpc.call("personal", "getMeritHolderKey"), "getMeritHolderNick": rpc.call("personal", "getMeritHolderNick"), "getAccount": rpc.call("personal", "getAccount"), #Should be equal to expected, which is also verified in a check above. "getAddress": rpc.call("personal", "getAddress") } #Set a new seed and verify the Merit Holder nick is cleared. rpc.call("personal", "setWallet") try: rpc.call("personal", "getMeritHolderNick") raise TestError("") except TestError as e: if str( e ) != "-2 Wallet doesn't have a Merit Holder nickname assigned.": raise TestError( "getMeritHolderNick returned something or an unexpected error when a new Mnemonic was set." ) #Set back the old seed and verify consistency. rpc.call("personal", "setWallet", { "mnemonic": mnemonic, "password": password }) for method in existing: if rpc.call("personal", method) != existing[method]: raise TestError( "Setting an old seed caused the WalletDB to improperly reload." ) #Verify calling getAddress returns the expected address. if rpc.call("personal", "getAddress") != expected: raise TestError( "Meros returned an address that wasn't next after reloading the seed." ) #Reboot the node and verify consistency. rpc.quit() sleep(3) rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc) for method in existing: if rpc.call("personal", method) != existing[method]: raise TestError( "Rebooting the node caused the WalletDB to improperly reload." ) #Used so Liver doesn't run its own post-test checks. #Since we added our own Blocks, those will fail. raise SuccessError() #Used so we don't have to write a sync loop. with raises(SuccessError): Liver(rpc, vectors["blockchain"], transactions, {8: restOfTest}).live()