Пример #1
0
  def test() -> None:
    #Data.
    dataHash: str = rpc.call("personal", "data", {"data": "abc"})
    #Read the initial Data.
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.Data:
      raise TestError("Meros didn't broadcast the initial Data.")
    #Read the actual Data.
    serializedData: bytes = rpc.meros.live.recv()
    if serializedData[1:] != Data.fromJSON(rpc.call("transactions", "getTransaction", {"hash": dataHash})).serialize():
      raise TestError("Meros didn't broadcast the created Data.")
    res: Any = rpc.call("network", "broadcast", {"transaction": dataHash})
    if not (isinstance(res, bool) and res):
      raise TestError("Broadcast didn't return true.")
    if rpc.meros.live.recv() != serializedData:
      raise TestError("Meros didn't broadcast a Transaction when told to.")

    #Block.
    header: BlockHeader = Block.fromJSON(vectors["blockchain"][0]).header
    rpc.call("network", "broadcast", {"block": header.hash.hex()})
    if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()):
      raise TestError("Meros didn't broadcast the Blockheader.")

    #Data and Block.
    rpc.call("network", "broadcast", {"block": header.hash.hex(), "transaction": dataHash})
    if rpc.meros.live.recv() != serializedData:
      raise TestError("Meros didn't broadcast a Transaction when told to.")
    if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header.serialize()):
      raise TestError("Meros didn't broadcast the Blockheader.")

    #Non-existent Transaction.
    try:
      rpc.call("network", "broadcast", {"transaction": bytes(32).hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-2 Transaction not found.":
        raise TestError("Meros didn't error when told to broadcast a non-existent Transaction.")

    #Non-existent Block.
    try:
      rpc.call("network", "broadcast", {"block": bytes(32).hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-2 Block not found.":
        raise TestError("Meros didn't error when told to broadcast a non-existent Block.")

    #Mint.
    try:
      rpc.call("network", "broadcast", {"transaction": mint.hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-3 Transaction is a Mint.":
        raise TestError("Meros didn't error when told to broadcast a Mint.")

    #Genesis Block.
    try:
      rpc.call("network", "broadcast", {"block": Blockchain().blocks[0].header.hash.hex()})
      raise TestError()
    except TestError as e:
      if str(e) != "-3 Block is the genesis Block.":
        raise TestError("Meros didn't error when told to broadcast the genesis Block.")
Пример #2
0
    def syncUnknown() -> None:
        claim: Claim = Claim([(merit.mints[0], 0)], pubKey.to_bytes())
        claim.sign(PrivateKey(0))

        #Create a series of Sends, forming a diamond.
        #Cross sendB and sendC to actually force this to work in an ordered fashion to pass.
        sendA: Send = Send([(claim.hash, 0)],
                           [(pubKey.to_bytes(), claim.amount // 2),
                            (pubKey.to_bytes(), claim.amount // 2)])
        sendB: Send = Send([(sendA.hash, 0)],
                           [(pubKey.to_bytes(), sendA.outputs[0][1] // 2),
                            (pubKey.to_bytes(), sendA.outputs[0][1] // 2)])
        sendC: Send = Send(
            [(sendA.hash, 1), (sendB.hash, 1)],
            [(pubKey.to_bytes(), sendA.outputs[1][1] + sendB.outputs[1][1])])
        sendD: Send = Send([(sendB.hash, 0), (sendC.hash, 0)],
                           [(pubKey.to_bytes(), claim.amount)])
        for send in [sendA, sendB, sendC, sendD]:
            send.sign(privKey)
            send.beat(sendFilter)

        #Send the tail of the diamond, which should cause an ordered top-down sync.
        sent: bytes = rpc.meros.liveTransaction(sendD)
        for tx in [
                sendC, sendB, sendA, claim, sendA, claim, sendB, sendA, claim
        ]:
            if rpc.meros.sync.recv() != (
                    MessageType.TransactionRequest.toByte() + tx.hash):
                raise TestError("Meros didn't request one of the inputs.")
            rpc.meros.syncTransaction(tx)
        if rpc.meros.live.recv() != sent:
            raise TestError("Meros didn't broadcast the Send.")

        #Do the same for a few Data Transactions.
        datas: List[Data] = [Data(bytes(32), pubKey.to_bytes())]
        datas.append(Data(datas[-1].hash, bytes(1)))
        datas.append(Data(datas[-1].hash, bytes(1)))
        for data in datas:
            data.sign(privKey)
            data.beat(dataFilter)
Пример #3
0
from e2e.Classes.Consensus.VerificationPacket import VerificationPacket
from e2e.Classes.Consensus.SpamFilter import SpamFilter

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

transactions: Transactions = Transactions()
dataFilter: SpamFilter = SpamFilter(5)

edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

proto: PrototypeChain = PrototypeChain(1, keepUnlocked=False)

#Create the Data and a successor.
first: Data = Data(bytes(32), edPubKey.to_bytes())
first.sign(edPrivKey)
first.beat(dataFilter)
transactions.add(first)

second: Data = Data(first.hash, bytes(1))
second.sign(edPrivKey)
second.beat(dataFilter)
transactions.add(second)

proto.add(packets=[
    VerificationPacket(first.hash, [0]),
    VerificationPacket(second.hash, [0])
])

for _ in range(5):
Пример #4
0
from e2e.Classes.Consensus.Verification import SignedVerification
from e2e.Classes.Consensus.VerificationPacket import VerificationPacket
from e2e.Classes.Consensus.SpamFilter import SpamFilter

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
edPubKey: bytes = edPrivKey.get_verifying_key()

transactions: Transactions = Transactions()
spamFilter: SpamFilter = SpamFilter(5)

proto = PrototypeChain(1, False)
proto.add(1)

data: Data = Data(bytes(32), edPubKey)
data.sign(edPrivKey)
data.beat(spamFilter)
transactions.add(data)

verif: SignedVerification = SignedVerification(data.hash)
verif.sign(1, PrivateKey(1))

proto.add(1, packets=[VerificationPacket(data.hash, [0])])
for _ in range(5):
    proto.add(1)

with open("e2e/Vectors/Consensus/Verification/HundredFortyTwo.json",
          "w") as vectors:
    vectors.write(
        json.dumps({
Пример #5
0
def verifyMnemonicAndAccount(rpc: RPC,
                             mnemonic: str = "",
                             password: str = "") -> None:
    #If a Mnemonic wasn't specified, grab the node's.
    if mnemonic == "":
        mnemonic = rpc.call("personal", "getMnemonic")

    #Verify Mnemonic equivalence.
    if mnemonic != rpc.call("personal", "getMnemonic"):
        raise TestError("Node had a different Mnemonic.")

    #Validate it.
    if not Bip39MnemonicValidator(mnemonic).Validate():
        raise TestError("Mnemonic checksum was incorrect.")

    #Verify derivation from seed to wallet.
    seed: bytes = Bip39SeedGenerator(mnemonic).Generate(password)
    #Check the Merit Holder key.
    if rpc.call("personal", "getMeritHolderKey") != PrivateKey(
            seed[:32]).serialize().hex().upper():
        raise TestError("Meros generated a different Merit Holder Key.")
    #Verify getting the Merit Holder nick errors.
    try:
        rpc.call("personal", "getMeritHolderNick")
    except TestError as e:
        if e.message != "-2 Wallet doesn't have a Merit Holder nickname assigned.":
            raise TestError("getMeritHolderNick didn't error.")

    #Hash the seed again for the wallet seed (first is the Merit Holder seed).
    seed = sha256(seed).digest()

    #Derive the first account.
    extendedKey: bytes
    chainCode: bytes
    try:
        extendedKey, chainCode = BIP32.deriveKeyAndChainCode(
            seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31)])
    except Exception:
        raise TestError(
            "Meros gave us an invalid Mnemonic to derive (or the test generated an unusable one)."
        )

    if rpc.call("personal", "getAccount") != {
            "key":
            RistrettoScalar(
                extendedKey[:32]).toPoint().serialize().hex().upper(),
            "chainCode":
            chainCode.hex().upper()
    }:
        raise TestError("Meros generated a different account public key.")

    #Also test that the correct public key is used when creating Datas.
    #It should be the first public key of the external chain for account 0.
    data: str = rpc.call("personal", "data", {
        "data": "a",
        "password": password
    })
    initial: Data = Data(
        bytes(32),
        RistrettoScalar(getPrivateKey(mnemonic, password,
                                      0)[:32]).toPoint().serialize())
    #Checks via the initial Data.
    if bytes.fromhex(
            rpc.call("transactions", "getTransaction",
                     {"hash": data})["inputs"][0]["hash"]) != initial.hash:
        raise TestError(
            "Meros used the wrong key to create the Data Transactions.")
Пример #6
0
transactions: Transactions = Transactions()

claim: Claim = Claim([(merit.mints[-1], 0)],
                     ed25519.SigningKey(b'\0' *
                                        32).get_verifying_key().to_bytes())
claim.sign(PrivateKey(0))
transactions.add(claim)

send: Send = Send([(claim.hash, 0)], [(ed25519.SigningKey(
    b'\1' * 32).get_verifying_key().to_bytes(), claim.amount)])
send.sign(ed25519.SigningKey(b'\0' * 32))
send.beat(SpamFilter(3))
transactions.add(send)

datas: List[Data] = [
    Data(bytes(32),
         ed25519.SigningKey(b'\0' * 32).get_verifying_key().to_bytes())
]
for _ in range(4):
    datas[-1].sign(ed25519.SigningKey(b'\0' * 32))
    datas[-1].beat(SpamFilter(5))
    transactions.add(datas[-1])
    datas.append(Data(datas[-1].hash, b'\0'))
del datas[-1]

merit.add(
    PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                   packets=[
                       VerificationPacket(claim.hash, [0]),
                       VerificationPacket(send.hash, [0, 1, 2]),
                       VerificationPacket(datas[0].hash, [0, 2]),
                       VerificationPacket(datas[1].hash, [0, 1, 3]),
Пример #7
0
  [(merit.mints[-1], 0)],
  Ristretto.SigningKey(b'\0' * 32).get_verifying_key()
)
claim.sign(PrivateKey(0))
transactions.add(claim)

send: Send = Send(
  [(claim.hash, 0)],
  [(Ristretto.SigningKey(b'\1' * 32).get_verifying_key(), claim.amount)]
)
send.sign(Ristretto.SigningKey(b'\0' * 32))
send.beat(SpamFilter(3))
transactions.add(send)

datas: List[Data] = [
  Data(bytes(32), Ristretto.SigningKey(b'\0' * 32).get_verifying_key())
]
for _ in range(4):
  datas[-1].sign(Ristretto.SigningKey(b'\0' * 32))
  datas[-1].beat(SpamFilter(5))
  transactions.add(datas[-1])
  datas.append(Data(datas[-1].hash, b'\0'))
del datas[-1]

merit.add(
  PrototypeBlock(
    merit.blockchain.blocks[-1].header.time + 1200,
    packets=[
      VerificationPacket(claim.hash, [0]),
      VerificationPacket(send.hash, [0, 1, 2]),
      VerificationPacket(datas[0].hash, [0, 2]),
Пример #8
0
from e2e.Vectors.Generation.PrototypeChain import PrototypeBlock

merit: Merit = Merit()
blocks: List[Dict[str, Any]] = []

transactions: Transactions = Transactions()

dataFilter: SpamFilter = SpamFilter(5)

edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

blsPrivKey: PrivateKey = PrivateKey(0)

#Generate a Data to verify for the VerificationPacket Block.
data: Data = Data(bytes(32), edPubKey.to_bytes())
data.sign(edPrivKey)
data.beat(dataFilter)
transactions.add(data)
packet: VerificationPacket = VerificationPacket(data.hash, [1])

blocks.append(
    PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                   packets=[VerificationPacket(data.hash, [1])],
                   minerID=blsPrivKey).finish(0, merit).toJSON())

#Generate the SendDifficulty Block.
blocks.append(
    PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                   elements=[SendDifficulty(0, 0, 1)],
                   minerID=blsPrivKey).finish(0, merit).toJSON())
Пример #9
0
    def publishAndVerify() -> None:
        if not rpc.call("transactions", "publishTransaction", {
                "type": "Claim",
                "transaction": claim.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != claim.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call("transactions", "publishTransaction", {
                "type": "Send",
                "transaction": send.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != send.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": data.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != data.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        #Verify all three were entered properly.
        verifyTransaction(rpc, claim)
        verifyTransaction(rpc, send)
        verifyTransaction(rpc, data)

        #Create a new Send/Data and publish them without work.
        sendSentWithoutWork: Send = Send([(send.hash, 0)],
                                         [(pubKey, claim.amount)])
        sendSentWithoutWork.sign(sentToKey)
        sendSentWithoutWork.beat(sendFilter)

        dataSentWithoutWork: Data = Data(bytes(32),
                                         sentToKey.get_verifying_key())
        dataSentWithoutWork.sign(sentToKey)
        dataSentWithoutWork.beat(dataFilter)

        if not rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Send",
                    "transaction": sendSentWithoutWork.serialize()[:-4].hex()
                }, True):
            raise TestError(
                "Publishing a valid Transaction without work didn't return true."
            )
        if rpc.meros.live.recv()[1:] != sendSentWithoutWork.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Data",
                    "transaction": dataSentWithoutWork.serialize()[:-4].hex()
                }, True):
            raise TestError(
                "Publishing a valid Transaction without work didn't return true."
            )
        if rpc.meros.live.recv()[1:] != dataSentWithoutWork.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        #Call verify now, which will test ours with work against Meros's with generated work.
        #Both should terminate on the earliest valid proof, making them identical.
        verifyTransaction(rpc, sendSentWithoutWork)
        verifyTransaction(rpc, dataSentWithoutWork)

        #Re-publishing a Transaction should still return true.
        if not rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": data.serialize().hex()
        }, False):
            raise TestError(
                "Publishing an existing Transaction didn't return true.")
        if MessageType(rpc.meros.live.recv()[0]) == MessageType.Data:
            raise TestError(
                "publishTransaction broadcasted an existing Transaction.")

        #No arguments.
        try:
            rpc.call("transactions", "publishTransaction")
        except TestError as e:
            if str(e) != "-32602 Invalid params.":
                raise TestError(
                    "publishTransaction didn't error when passed no arguments."
                )

        #Invalid type.
        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "",
                "transaction": data.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(e) != "-3 Invalid Transaction type specified.":
                raise TestError(
                    "publishTransaction didn't error when passed an invalid type."
                )

        #Data sent with Send as a type.
        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Send",
                "transaction": data.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(
                    e
            ) != "-3 Transaction is invalid: parseSend handed the wrong amount of data.":
                raise TestError(
                    "publishTransaction didn't error when passed a non-parsable Send (a Data)."
                )

        #Invalid Data (signature).
        invalidData: Data = Data(bytes(32), sentToKey.get_verifying_key())
        invalidData.sign(sentToKey)
        sig: bytes = invalidData.signature

        newData: bytearray = bytearray(invalidData.data)
        newData[-1] = newData[-1] ^ 1
        #Reconstruct to rehash.
        invalidData = Data(bytes(32), bytes(newData))
        invalidData.signature = sig
        invalidData.beat(dataFilter)

        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": invalidData.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(
                    e
            ) != "-3 Transaction is invalid: Data has an invalid Signature.":
                raise TestError(
                    "publishTransaction didn't error when passed an invalid Transaction."
                )

        #Spam.
        spamData: Data = data
        if spamData.proof == 0:
            spamData = dataSentWithoutWork
        if spamData.proof == 0:
            raise Exception("Neither Data is considered as Spam.")
        spamData.proof = 0

        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": spamData.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(e) != "2 Transaction didn't beat the spam filter.":
                raise TestError(
                    "publishTransaction didn't error when passed a Transaction which didn't beat its difficulty."
                )

        #Test publishTransactionWithoutWork requires authorization.
        try:
            rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Data",
                    "transaction": dataSentWithoutWork.serialize()[:-4].hex()
                }, False)
            raise TestError("")
        except Exception as e:
            if str(e) != "HTTP status isn't 200: 401":
                raise TestError(
                    "Called publishTransactionWithoutWork despite not being authed."
                )
Пример #10
0
def PublishTransactionTest(rpc: RPC) -> None:
    privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    pubKey: bytes = privKey.get_verifying_key()

    sentToKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\1' * 32)

    sendFilter: SpamFilter = SpamFilter(3)
    dataFilter: SpamFilter = SpamFilter(5)

    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"])

    if len(transactions.txs) != 1:
        raise Exception("Transactions DAG doesn't have just the Claim.")
    claim: Claim = Claim.fromTransaction(next(iter(transactions.txs.values())))

    send: Send = Send([(claim.hash, 0)],
                      [(sentToKey.get_verifying_key(), claim.amount)])
    send.sign(privKey)
    send.beat(sendFilter)

    data: Data = Data(bytes(32), pubKey)
    data.sign(privKey)
    data.beat(dataFilter)

    def publishAndVerify() -> None:
        if not rpc.call("transactions", "publishTransaction", {
                "type": "Claim",
                "transaction": claim.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != claim.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call("transactions", "publishTransaction", {
                "type": "Send",
                "transaction": send.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != send.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": data.serialize().hex()
        }, False):
            raise TestError(
                "Publishing a valid Transaction didn't return true.")
        if rpc.meros.live.recv()[1:] != data.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        #Verify all three were entered properly.
        verifyTransaction(rpc, claim)
        verifyTransaction(rpc, send)
        verifyTransaction(rpc, data)

        #Create a new Send/Data and publish them without work.
        sendSentWithoutWork: Send = Send([(send.hash, 0)],
                                         [(pubKey, claim.amount)])
        sendSentWithoutWork.sign(sentToKey)
        sendSentWithoutWork.beat(sendFilter)

        dataSentWithoutWork: Data = Data(bytes(32),
                                         sentToKey.get_verifying_key())
        dataSentWithoutWork.sign(sentToKey)
        dataSentWithoutWork.beat(dataFilter)

        if not rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Send",
                    "transaction": sendSentWithoutWork.serialize()[:-4].hex()
                }, True):
            raise TestError(
                "Publishing a valid Transaction without work didn't return true."
            )
        if rpc.meros.live.recv()[1:] != sendSentWithoutWork.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        if not rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Data",
                    "transaction": dataSentWithoutWork.serialize()[:-4].hex()
                }, True):
            raise TestError(
                "Publishing a valid Transaction without work didn't return true."
            )
        if rpc.meros.live.recv()[1:] != dataSentWithoutWork.serialize():
            raise TestError(
                "publishTransaction didn't broadcast the Transaction.")

        #Call verify now, which will test ours with work against Meros's with generated work.
        #Both should terminate on the earliest valid proof, making them identical.
        verifyTransaction(rpc, sendSentWithoutWork)
        verifyTransaction(rpc, dataSentWithoutWork)

        #Re-publishing a Transaction should still return true.
        if not rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": data.serialize().hex()
        }, False):
            raise TestError(
                "Publishing an existing Transaction didn't return true.")
        if MessageType(rpc.meros.live.recv()[0]) == MessageType.Data:
            raise TestError(
                "publishTransaction broadcasted an existing Transaction.")

        #No arguments.
        try:
            rpc.call("transactions", "publishTransaction")
        except TestError as e:
            if str(e) != "-32602 Invalid params.":
                raise TestError(
                    "publishTransaction didn't error when passed no arguments."
                )

        #Invalid type.
        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "",
                "transaction": data.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(e) != "-3 Invalid Transaction type specified.":
                raise TestError(
                    "publishTransaction didn't error when passed an invalid type."
                )

        #Data sent with Send as a type.
        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Send",
                "transaction": data.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(
                    e
            ) != "-3 Transaction is invalid: parseSend handed the wrong amount of data.":
                raise TestError(
                    "publishTransaction didn't error when passed a non-parsable Send (a Data)."
                )

        #Invalid Data (signature).
        invalidData: Data = Data(bytes(32), sentToKey.get_verifying_key())
        invalidData.sign(sentToKey)
        sig: bytes = invalidData.signature

        newData: bytearray = bytearray(invalidData.data)
        newData[-1] = newData[-1] ^ 1
        #Reconstruct to rehash.
        invalidData = Data(bytes(32), bytes(newData))
        invalidData.signature = sig
        invalidData.beat(dataFilter)

        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": invalidData.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(
                    e
            ) != "-3 Transaction is invalid: Data has an invalid Signature.":
                raise TestError(
                    "publishTransaction didn't error when passed an invalid Transaction."
                )

        #Spam.
        spamData: Data = data
        if spamData.proof == 0:
            spamData = dataSentWithoutWork
        if spamData.proof == 0:
            raise Exception("Neither Data is considered as Spam.")
        spamData.proof = 0

        try:
            rpc.call("transactions", "publishTransaction", {
                "type": "Data",
                "transaction": spamData.serialize().hex()
            }, False)
            raise TestError("")
        except TestError as e:
            if str(e) != "2 Transaction didn't beat the spam filter.":
                raise TestError(
                    "publishTransaction didn't error when passed a Transaction which didn't beat its difficulty."
                )

        #Test publishTransactionWithoutWork requires authorization.
        try:
            rpc.call(
                "transactions", "publishTransactionWithoutWork", {
                    "type": "Data",
                    "transaction": dataSentWithoutWork.serialize()[:-4].hex()
                }, False)
            raise TestError("")
        except Exception as e:
            if str(e) != "HTTP status isn't 200: 401":
                raise TestError(
                    "Called publishTransactionWithoutWork despite not being authed."
                )

    Liver(rpc, vectors["blockchain"][:-1], transactions, {
        7: publishAndVerify
    }).live()
Пример #11
0
from e2e.Classes.Transactions.Transactions import Data, Transactions

from e2e.Classes.Consensus.VerificationPacket import VerificationPacket
from e2e.Classes.Consensus.SpamFilter import SpamFilter

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
dataFilter: SpamFilter = SpamFilter(5)
transactions: Transactions = Transactions()
proto: PrototypeChain = PrototypeChain(1)

#Create five Datas.
#Six in total, thanks to the Block Data.
data: Data = Data(bytes(32), edPrivKey.get_verifying_key())
for i in range(5):
  data.sign(edPrivKey)
  data.beat(dataFilter)
  transactions.add(data)
  data = Data(data.hash, b"\0")

#Create a Block verifying all of them.
proto.add(0, [VerificationPacket(tx.hash, [0]) for tx in transactions.txs.values()])

with open("e2e/Vectors/Merit/TwoHundredSeventyFour/RespondsWithRequestedCapacity.json", "w") as vectors:
  vectors.write(json.dumps({
    "blockchain": proto.toJSON(),
    "transactions": transactions.toJSON()
  }))
Пример #12
0
    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()
Пример #13
0
def GetTransactionTest(rpc: RPC) -> None:
    privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    pubKey: bytes = privKey.get_verifying_key()

    sendFilter: SpamFilter = SpamFilter(3)
    dataFilter: SpamFilter = SpamFilter(5)

    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"])

    if len(transactions.txs) != 1:
        raise Exception("Transactions DAG doesn't have just the Claim.")
    claim: Claim = Claim.fromTransaction(next(iter(transactions.txs.values())))

    send: Send = Send(
        [(claim.hash, 0)],
        [(Ristretto.SigningKey(b'\1' * 32).get_verifying_key(), claim.amount)])
    send.sign(privKey)
    send.beat(sendFilter)

    data: Data = Data(bytes(32), pubKey)
    data.sign(privKey)
    data.beat(dataFilter)

    def sendAndVerify() -> None:
        rpc.meros.liveTransaction(send)
        rpc.meros.liveTransaction(data)
        rpc.meros.live.recv()
        rpc.meros.live.recv()

        #We now have a Mint, a Claim, a Send, a Data, a lion, a witch, and a wardrobe.

        #Check the Mint.
        mint: Mint = Merit.fromJSON(vectors["blockchain"]).mints[0]
        #pylint: disable=invalid-name
        EXPECTED_MINT: Dict[str, Any] = {
            "descendant":
            "Mint",
            "inputs": [],
            "outputs": [{
                "amount": str(txOutput[1]),
                "nick": txOutput[0]
            } for txOutput in mint.outputs],
            "hash":
            mint.hash.hex().upper()
        }
        #Also sanity check against the in-house JSON.
        if mint.toJSON() != EXPECTED_MINT:
            raise TestError("Python's Mint toJSON doesn't match the spec.")
        if rpc.call("transactions", "getTransaction",
                    {"hash": mint.hash.hex()}, False) != EXPECTED_MINT:
            raise TestError("getTransaction didn't report the Mint properly.")

        #Check the Claim.
        #pylint: disable=invalid-name
        EXPECTED_CLAIM: Dict[str, Any] = {
            "descendant":
            "Claim",
            "inputs": [{
                "hash": txInput[0].hex().upper(),
                "nonce": txInput[1]
            } for txInput in claim.inputs],
            "outputs": [{
                "amount": str(claim.amount),
                "key": claim.output.hex().upper()
            }],
            "hash":
            claim.hash.hex().upper(),
            "signature":
            claim.signature.hex().upper()
        }
        if claim.amount == 0:
            raise Exception(
                "Python didn't instantiate the Claim with an amount, leading to invalid testing methodology."
            )
        if claim.toJSON() != EXPECTED_CLAIM:
            raise TestError("Python's Claim toJSON doesn't match the spec.")
        if rpc.call("transactions", "getTransaction",
                    {"hash": claim.hash.hex()}, False) != EXPECTED_CLAIM:
            raise TestError("getTransaction didn't report the Claim properly.")

        #Check the Send.
        #pylint: disable=invalid-name
        EXPECTED_SEND: Dict[str, Any] = {
            "descendant":
            "Send",
            "inputs": [{
                "hash": txInput[0].hex().upper(),
                "nonce": txInput[1]
            } for txInput in send.inputs],
            "outputs": [{
                "amount": str(txOutput[1]),
                "key": txOutput[0].hex().upper()
            } for txOutput in send.outputs],
            "hash":
            send.hash.hex().upper(),
            "signature":
            send.signature.hex().upper(),
            "proof":
            send.proof
        }
        if send.toJSON() != EXPECTED_SEND:
            raise TestError("Python's Send toJSON doesn't match the spec.")
        if rpc.call("transactions", "getTransaction",
                    {"hash": send.hash.hex()}, False) != EXPECTED_SEND:
            raise TestError("getTransaction didn't report the Send properly.")

        #Check the Data.
        #pylint: disable=invalid-name
        EXPECTED_DATA: Dict[str, Any] = {
            "descendant": "Data",
            "inputs": [{
                "hash": data.txInput.hex().upper()
            }],
            "outputs": [],
            "hash": data.hash.hex().upper(),
            "data": data.data.hex().upper(),
            "signature": data.signature.hex().upper(),
            "proof": data.proof
        }
        if data.toJSON() != EXPECTED_DATA:
            raise TestError("Python's Data toJSON doesn't match the spec.")
        if rpc.call("transactions", "getTransaction",
                    {"hash": data.hash.hex()}, False) != EXPECTED_DATA:
            raise TestError("getTransaction didn't report the Data properly.")

        #Non-existent hash; should cause an IndexError
        nonExistentHash: str = data.hash.hex()
        if data.hash[0] == "0":
            nonExistentHash = "1" + nonExistentHash[1:]
        else:
            nonExistentHash = "0" + nonExistentHash[1:]
        try:
            rpc.call("transactions", "getTransaction",
                     {"hash": nonExistentHash}, False)
        except TestError as e:
            if str(e) != "-2 Transaction not found.":
                raise TestError(
                    "getTransaction didn't raise IndexError on a non-existent hash."
                )

        #Invalid argument; should cause a ParamError
        #This is still a hex value
        try:
            rpc.call("transactions", "getTransaction",
                     {"hash": "00" + data.hash.hex()}, False)
            raise TestError(
                "Meros didn't error when we asked for a 33-byte hex value.")
        except TestError as e:
            if str(e) != "-32602 Invalid params.":
                raise TestError(
                    "getTransaction didn't raise on invalid parameters.")

    Liver(rpc, vectors["blockchain"], transactions, {8: sendAndVerify}).live()
Пример #14
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()
Пример #15
0
def verifyMnemonicAndAccount(rpc: RPC,
                             mnemonic: str = "",
                             password: str = "") -> None:
    #If a Mnemonic wasn't specified, grab the node's.
    if mnemonic == "":
        mnemonic = rpc.call("personal", "getMnemonic")

    #Verify Mnemonic equivalence.
    if mnemonic != rpc.call("personal", "getMnemonic"):
        raise TestError("Node had a different Mnemonic.")

    #Validate it.
    if not Bip39MnemonicValidator(mnemonic).Validate():
        raise TestError("Mnemonic checksum was incorrect.")

    #Verify derivation from seed to wallet.
    seed: bytes = Bip39SeedGenerator(mnemonic).Generate(password)
    #Check the Merit Holder key.
    if rpc.call("personal", "getMeritHolderKey") != PrivateKey(
            seed[:32]).serialize().hex().upper():
        raise TestError("Meros generated a different Merit Holder Key.")
    #Verify getting the Merit Holder nick errors.
    try:
        rpc.call("personal", "getMeritHolderNick")
    except TestError as e:
        if e.message != "-2 Wallet doesn't have a Merit Holder nickname assigned.":
            raise TestError("getMeritHolderNick didn't error.")

    #Hash the seed again for the wallet seed (first is the Merit Holder seed).
    seed = sha256(seed).digest()

    #Derive the first account.
    extendedKey: bytes
    chainCode: bytes
    try:
        extendedKey, chainCode = BIP32.deriveKeyAndChainCode(
            seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31)])
    except Exception:
        raise TestError(
            "Meros gave us an invalid Mnemonic to derive (or the test generated an unusable one)."
        )

    #For some reason, pylint decided to add in detection of stdlib members.
    #It doesn't do it properly, and thinks encodepoint returns a string.
    #It returns bytes, which does have hex as a method.
    #pylint: disable=no-member
    if rpc.call("personal", "getAccount") != {
            "key":
            ed.encodepoint(
                ed.scalarmult(
                    ed.B,
                    ed.decodeint(extendedKey[:32]) % ed.l)).hex().upper(),
            "chainCode":
            chainCode.hex().upper()
    }:
        #The Nim tests ensure accurate BIP 32 derivation thanks to vectors.
        #That leaves BIP 39/44 in the air.
        #This isn't technically true due to an ambiguity/the implementation we used the vectors of, yet it's true enough for this comment.
        raise TestError("Meros generated a different account public key.")

    #Also test that the correct public key is used when creating Datas.
    #It should be the first public key of the external chain for account 0.
    data: str = rpc.call("personal", "data", {
        "data": "a",
        "password": password
    })
    initial: Data = Data(
        bytes(32),
        ed.encodepoint(
            ed.scalarmult(
                ed.B,
                ed.decodeint(getPrivateKey(mnemonic, password, 0)[:32]) %
                ed.l)))
    #Checks via the initial Data.
    if bytes.fromhex(
            rpc.call("transactions", "getTransaction",
                     {"hash": data})["inputs"][0]["hash"]) != initial.hash:
        raise TestError(
            "Meros used the wrong key to create the Data Transactions.")