Example #1
0
  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()
Example #2
0
    def test() -> None:
        #Send to the new address and get the next address.
        dest: str = rpc.call("personal", "getAddress")
        last: Send = createSend(rpc, Claim.fromJSON(vectors["newerMintClaim"]),
                                dest)

        utxos: List[Dict[str, Any]] = rpc.call("personal", "getUTXOs")
        if utxos != [{
                "address": dest,
                "hash": last.hash.hex().upper(),
                "nonce": 0
        }]:
            raise TestError(
                "personal_getUTXOs didn't return the correct UTXOs.")

        #Set a different mnemonic to verify the tracked addresses is cleared.
        rpc.call("personal", "setWallet")
        if rpc.call("personal", "getUTXOs") != []:
            raise TestError(
                "Setting a new Mnemonic didn't clear the tracked addresses.")

        #Reload the Mnemonic and verify the UTXOs are correct.
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            #This error message points out how no addresses are really being discovered yet; this is account zero's address.
            #That said, if the test started at the next address, there'd be a gap.
            #As that's an extra factor, this is tested before gaps are.
            raise TestError("Meros didn't recover the very first address.")

        #Now send to the next address and check accuracy.
        dest = rpc.call("personal", "getAddress")
        last = createSend(rpc, last, dest)
        utxos.append({
            "address": dest,
            "hash": last.hash.hex().upper(),
            "nonce": 0
        })
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError("Meros didn't track an implicitly gotten address.")
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError(
                "Meros didn't recover the first address after the initial address."
            )

        #Send funds to the address after the next address; tests a gap when discovering addresses.
        last = createSend(rpc, last, getAddress(mnemonic, "", 3))
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError(
                "Meros magically recognized UTXOs as belonging to this Wallet or someone implemented an address cache."
            )
        utxos.append({
            "address": getAddress(mnemonic, "", 3),
            "hash": last.hash.hex().upper(),
            "nonce": 0
        })
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError(
                "Meros didn't discover a used address in the Wallet when there was a gap."
            )

        #Finally, anything 10+ unused addresses out shouldn't be recovered.
        last = createSend(rpc, last, getAddress(mnemonic, "", 14))
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError(
                "Meros recovered an address's UTXOs despite it being 10 unused addresses out."
            )

        #Explicitly generating this address should start tracking it though.
        rpc.call("personal", "getAddress",
                 {"index": getIndex(mnemonic, "", 14)})
        utxos.append({
            "address": getAddress(mnemonic, "", 14),
            "hash": last.hash.hex().upper(),
            "nonce": 0
        })
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError(
                "personal_getUTXOs didn't track an address explicitly indexed."
            )

        #If asked for an address, Meros should return the 5th (skip 4).
        #It's the first unused address AFTER used addresss EXCEPT ones explicitly requested.
        #This can, in the future, be juwst the first unused address/include ones explicitly requested (see DerivationTest for commentary on that).
        #This is really meant to ensure consistent behavior until we properly decide otherwise.
        if rpc.call("personal", "getAddress") != getAddress(mnemonic, "", 4):
            raise TestError(
                "Meros didn't return the next unused address (with conditions; see comment)."
            )

        #Mine a Block to flush the Transactions and Verifications to disk.
        sleep(65)
        rpc.meros.liveConnect(Blockchain().blocks[0].header.hash)
        mineBlock(rpc)

        #Existing values used to test getAddress/getUTXOs consistency.
        #The former is thoroughly tested elsewhere, making it quite redundant.
        existing: Dict[str, Any] = {
            "getAddress": rpc.call("personal", "getAddress"),
            "getUTXOs": rpc.call("personal", "getUTXOs")
        }

        #Reboot the node and verify consistency.
        rpc.quit()
        sleep(3)
        rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc)
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(
                existing["getUTXOs"]):
            raise TestError(
                "Rebooting the node caused the WalletDB to improperly reload UTXOs."
            )
        if rpc.call("personal", "getAddress") != existing["getAddress"]:
            raise TestError(
                "Rebooting the node caused the WalletDB to improperly reload the next address."
            )

        #Used so Liver doesn't run its own post-test checks.
        #Since we added our own Blocks, those will fail.
        raise SuccessError()
Example #3
0
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()