def TwoHundredFifteenTest(rpc: RPC) -> None:
    vectors: Dict[str, Any]
    with open("e2e/Vectors/Transactions/ClaimedMint.json", "r") as file:
        vectors = json.loads(file.read())

    merit: Merit = Merit.fromJSON(vectors["blockchain"])
    sendFilter: SpamFilter = SpamFilter(3)
    dataFilter: SpamFilter = SpamFilter(5)

    privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
    pubKey: ed25519.VerifyingKey = privKey.get_verifying_key()

    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)

    Liver(rpc, vectors["blockchain"], callbacks={7: syncUnknown}).live()
示例#2
0
def createSend(rpc: RPC, claim: Claim, to: bytes) -> bytes:
    send: Send = Send([(claim.hash, 0)], [(to, claim.amount)])
    send.sign(Ristretto.SigningKey(b'\0' * 32))
    send.beat(SpamFilter(3))
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back a Send.")
    return send.hash
示例#3
0
def NodeThresholdTest(rpc: RPC) -> None:
    edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)

    dataFilter: SpamFilter = SpamFilter(5)

    datas: List[Data] = [Data(bytes(32), edPrivKey.get_verifying_key())]
    datas[-1].sign(edPrivKey)
    datas[-1].beat(dataFilter)

    def verifyThreshold(b: int) -> None:
        rpc.meros.liveTransaction(datas[-1])
        datas.append(Data(datas[-1].hash, b"a"))
        datas[-1].sign(edPrivKey)
        datas[-1].beat(dataFilter)

        #Swallow the new Data(s).
        if b == 1:
            rpc.meros.live.recv()
        rpc.meros.live.recv()

        #Check the threshold.
        threshold: int = rpc.call("consensus", "getStatus",
                                  {"hash": datas[-2].hash.hex()})["threshold"]
        if b < 9:
            if threshold != ((max(b + 6, 5) // 5 * 4) + 1):
                raise TestError(
                    "Meros didn't calculate the right node threshold. That said, this isn't defined by the protocol."
                )
        elif threshold != 5:
            raise TestError("Meros didn't lower the node threshold.")

    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        Liver(rpc, json.loads(file.read())[:9],
              everyBlock=verifyThreshold).live()
示例#4
0
def createSend(rpc: RPC, last: Union[Claim, Send], toAddress: str) -> Send:
    funded: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    if isinstance(last, Claim):
        send: Send = Send([(last.hash, 0)],
                          [(decodeAddress(toAddress), 1),
                           (funded.get_verifying_key(), last.amount - 1)])
    else:
        send: Send = Send(
            [(last.hash, 1)],
            [(decodeAddress(toAddress), 1),
             (funded.get_verifying_key(), last.outputs[1][1] - 1)])

    send.sign(funded)
    send.beat(SpamFilter(3))
    sleep(65)
    rpc.meros.liveConnect(Blockchain().blocks[0].header.hash)
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Send.")

    sv: SignedVerification = SignedVerification(send.hash)
    sv.sign(0, PrivateKey(0))
    if rpc.meros.signedElement(sv) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Verification.")

    return send
示例#5
0
def createSend(
    rpc: RPC,
    inputs: List[Union[Claim, Send]],
    to: bytes,
    key: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)) -> Send:
    pub: bytes = key.get_verifying_key().to_bytes()
    actualInputs: List[Tuple[bytes, int]] = []
    outputs: List[Tuple[bytes, int]] = [(to, 1)]
    toSpend: int = 0
    for txInput in inputs:
        if isinstance(txInput, Claim):
            actualInputs.append((txInput.hash, 0))
            toSpend += txInput.amount
        else:
            for n in range(len(txInput.outputs)):
                if txInput.outputs[n][0] == key.get_verifying_key().to_bytes():
                    actualInputs.append((txInput.hash, n))
                    toSpend += txInput.outputs[n][1]
    if toSpend > 1:
        outputs.append((pub, toSpend - 1))

    send: Send = Send(actualInputs, outputs)
    send.sign(key)
    send.beat(SpamFilter(3))
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Send.")
    return send
示例#6
0
def VUnknownSignedTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
    chain: Blockchain = Blockchain.fromJSON(json.loads(file.read()))
    file.close()

    #Send a single block so we have a miner.
    rpc.meros.liveConnect(chain.blocks[0].header.hash)
    rpc.meros.syncConnect(chain.blocks[0].header.hash)
    header: bytes = rpc.meros.liveBlockHeader(chain.blocks[1].header)
    if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest:
        raise TestError("Meros didn't ask for the body.")
    rpc.meros.blockBody(chain.blocks[1])
    if rpc.meros.live.recv() != header:
        raise TestError("Meros didn't broadcast the header.")

    #Create a valid Data.
    #Uneccessary at this time, but good preparation for the future.
    privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
    data: Data = Data(bytes(32), privKey.get_verifying_key().to_bytes())
    data.sign(privKey)
    data.beat(SpamFilter(5))

    #Sign the Data.
    verif: SignedVerification = SignedVerification(data.hash)
    verif.sign(0, PrivateKey(0))

    #Run twice. The first shouldn't send the Transaction. The second should.
    for i in range(2):
        rpc.meros.signedElement(verif)
        if MessageType(
                rpc.meros.sync.recv()[0]) != MessageType.TransactionRequest:
            raise TestError("Meros didn't request the transaction.")

        if i == 0:
            #When we send DataMissing, we should be disconnected within a few seconds.
            rpc.meros.dataMissing()
            start: int = int(time())
            try:
                rpc.meros.sync.recv()
            except Exception:
                #More than a few seconds is allowed as Meros's own SyncRequest must timeout.
                if int(time()) - start > 10:
                    raise TestError(
                        "Meros didn't disconnect us for sending a Verification of a non-existent Transaction."
                    )
            #Clear our invalid connections.
            rpc.meros.live.connection.close()
            rpc.meros.sync.connection.close()
            sleep(65)
            #Init new ones.
            rpc.meros.liveConnect(chain.blocks[0].header.hash)
            rpc.meros.syncConnect(chain.blocks[0].header.hash)

        else:
            rpc.meros.syncTransaction(data)
            sleep(2)
            if not rpc.call("consensus", "getStatus",
                            [data.hash.hex()])["verifiers"]:
                raise TestError("Meros didn't add the Verification.")
示例#7
0
    def __init__(self,
                 txInput: bytes,
                 data: bytes,
                 signature: bytes = bytes(64),
                 proof: int = 0) -> None:
        self.txInput: bytes = txInput
        self.data: bytes = data
        self.hash: bytes = blake2b(b"\3" + txInput + data,
                                   digest_size=32).digest()
        self.signature: bytes = signature

        self.proof: int = proof
        self.argon: bytes = SpamFilter.run(self.hash, self.proof)
示例#8
0
文件: DataTest.py 项目: Vyryn/Meros
def DataTest(rpc: RPC) -> None:
    privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    pubKey: bytes = privKey.get_verifying_key()

    genesis: bytes = Blockchain().blocks[0].header.hash
    spamFilter: SpamFilter = SpamFilter(5)

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

    rpc.meros.liveConnect(genesis)
    rpc.meros.liveTransaction(data)
    verifyTransaction(rpc, data)
示例#9
0
def HundredFortySevenTest(rpc: RPC) -> None:
    file: IO[Any] = open("e2e/Vectors/Transactions/ClaimedMint.json", "r")
    vectors: Dict[str, Any] = json.loads(file.read())
    file.close()

    #Merit.
    merit: Merit = Merit.fromJSON(vectors["blockchain"])
    #Transactions.
    transactions: Transactions = Transactions.fromJSON(vectors["transactions"])

    #Ed25519 keys.
    privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
    pubKey: ed25519.VerifyingKey = privKey.get_verifying_key()

    #Grab the Claim hash,
    claim: bytes = merit.blockchain.blocks[-1].body.packets[0].hash

    #Create a Send which underflows.
    send: Send = Send(
        [(claim, 0)],
        [(pubKey.to_bytes(), 18446744073709551231),
         (pubKey.to_bytes(),
          385 + Claim.fromTransaction(transactions.txs[claim]).amount)])
    send.sign(privKey)
    send.beat(SpamFilter(3))

    #Custom function to send the last Block and verify it errors at the right place.
    def checkFail() -> None:
        #Send the Send.
        rpc.meros.liveTransaction(send)

        #Handle sync requests.
        while True:
            #Try receiving from the Live socket, where Meros sends keep-alives.
            try:
                if len(rpc.meros.live.recv()) != 0:
                    raise Exception()
            except TestError:
                raise SuccessError(
                    "Node disconnected us after we sent an invalid Transaction."
                )
            except Exception:
                raise TestError("Meros sent a keep-alive.")

    #Create and execute a Liver.
    Liver(rpc, vectors["blockchain"], transactions, callbacks={
        12: checkFail
    }).live()
示例#10
0
def createSend(rpc: RPC, last: Union[Claim, Send], toAddress: str) -> Send:
    funded: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    if isinstance(last, Claim):
        send: Send = Send([(last.hash, 0)],
                          [(decodeAddress(toAddress), 1),
                           (funded.get_verifying_key(), last.amount - 1)])
    else:
        send: Send = Send(
            [(last.hash, 1)],
            [(decodeAddress(toAddress), 1),
             (funded.get_verifying_key(), last.outputs[1][1] - 1)])
    send.sign(funded)
    send.beat(SpamFilter(3))
    if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
        raise TestError("Meros didn't broadcast back a Send.")
    return send
示例#11
0
    def __init__(self,
                 inputs: List[Tuple[bytes, int]],
                 outputs: List[Tuple[bytes, int]],
                 signature: bytes = bytes(64),
                 proof: int = 0) -> None:
        self.inputs = inputs
        self.outputs = outputs
        self.hash: bytes = blake2b(
            (b"\2" + len(self.inputs).to_bytes(1, "little") +
             self.serializeInputs() + len(self.outputs).to_bytes(1, "little") +
             self.serializeOutputs()),
            digest_size=32).digest()
        self.signature: bytes = signature

        self.proof: int = proof
        self.argon: bytes = SpamFilter.run(self.hash, self.proof)
示例#12
0
def HundredSixSignedElementsTest(
  rpc: RPC
) -> None:
  #Solely used to get the genesis Block hash.
  blockchain: Blockchain = Blockchain()

  edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
  blsPrivKey: PrivateKey = PrivateKey(0)
  sig: Signature = blsPrivKey.sign(bytes())

  #Create a Data for the Verification.
  data: Data = Data(bytes(32), edPrivKey.get_verifying_key())
  data.sign(edPrivKey)
  data.beat(SpamFilter(5))

  #Create a signed Verification, SendDifficulty, and DataDifficulty.
  elements: List[SignedElement] = [
    SignedVerification(data.hash, 1, sig),
    SignedSendDifficulty(0, 0, 1, sig),
    SignedDataDifficulty(0, 0, 1, sig)
  ]

  dataSent: bool = False
  for elem in elements:
    #Handshake with the node.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)

    #Send the Data if we have yet to.
    if not dataSent:
      if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Data wasn't rebroadcasted.")
      dataSent = True

    #Send the Element.
    rpc.meros.signedElement(elem)

    #Sleep for thirty seconds to make sure Meros realizes our connection is dead.
    sleep(30)

    #Verify the node didn't crash.
    try:
      if rpc.call("merit", "getHeight") != 1:
        raise Exception()
    except Exception:
      raise TestError("Node crashed after being sent a malformed Element.")
示例#13
0
def DataTest(rpc: RPC) -> None:
    #Get the genesis hash.
    genesis: bytes = Blockchain().blocks[0].header.hash

    #Create the Spam Filter.
    spamFilter: SpamFilter = SpamFilter(5)

    #Create the Data.
    data: Data = Data(bytes(32), pubKey.to_bytes())
    data.sign(privKey)
    data.beat(spamFilter)

    #Handshake with the node.
    rpc.meros.liveConnect(genesis)

    #Send the Data.
    rpc.meros.liveTransaction(data)

    #Sleep for 100 milliseconds.
    sleep(0.1)

    #Verify the Data.
    verifyTransaction(rpc, data)
示例#14
0
def HundredFiftyFiveTest(rpc: RPC) -> None:
    edPrivKeys: List[ed25519.SigningKey] = [
        ed25519.SigningKey(b'\0' * 32),
        ed25519.SigningKey(b'\1' * 32)
    ]
    edPubKeys: List[ed25519.VerifyingKey] = [
        edPrivKeys[0].get_verifying_key(), edPrivKeys[1].get_verifying_key()
    ]

    blsPrivKey: PrivateKey = PrivateKey(
        bytes.fromhex(rpc.call("personal", "getMiner")))
    blsPubKey: bytes = blsPrivKey.toPublicKey().serialize()

    blockchain: Blockchain = Blockchain()
    dataFilter: SpamFilter = SpamFilter(5)

    #Handshake with the node.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)

    #Call getBlockTemplate just to get an ID.
    #Skips the need to write a sync loop for the BlockBody.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        [blsPubKey.hex()])

    #Mine a Block.
    block = Block(
        BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1,
                    bytes(4), bytes(32), blsPubKey,
                    blockchain.blocks[0].header.time + 1200, 0), BlockBody())
    block.mine(blsPrivKey, blockchain.difficulty())
    blockchain.add(block)

    rpc.call("merit", "publishBlock",
             [template["id"], block.serialize().hex()])

    if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
        raise TestError("Meros didn't broadcast the Block we just published.")
    #Ignore the Verification for the Block's Data.
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError(
            "Meros didn't send the SignedVerification for the Block's Data.")

    datas: List[Data] = [
        Data(bytes(32), edPubKeys[0].to_bytes()),
        Data(bytes(32), edPubKeys[1].to_bytes())
    ]

    for d in range(len(datas)):
        datas[d].sign(edPrivKeys[d])
        datas[d].beat(dataFilter)

        #Send the Data and verify Meros sends it back.
        if rpc.meros.liveTransaction(datas[d]) != rpc.meros.live.recv():
            raise TestError("Meros didn't send back the Data.")

        #Verify Meros sends back a Verification.
        res: bytes = rpc.meros.live.recv()
        if MessageType(res[0]) != MessageType.SignedVerification:
            raise TestError("Meros didn't send a SignedVerification.")

        verif: SignedVerification = SignedVerification(datas[d].hash)
        verif.sign(0, blsPrivKey)
        if res[1:] != verif.signedSerialize():
            raise TestError(
                "Meros didn't send the correct SignedVerification.")
示例#15
0
from e2e.Classes.Transactions.Transactions import Claim, Send, Transactions

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

from e2e.Classes.Merit.Merit import Block, Merit

from e2e.Vectors.Generation.PrototypeChain import PrototypeBlock, PrototypeChain

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

transactions: Transactions = Transactions()
sendFilter: SpamFilter = SpamFilter(3)

proto: PrototypeChain = PrototypeChain(40, keepUnlocked=True)
proto.add(1)
merit: Merit = Merit.fromJSON(proto.toJSON())

#Create a Claim.
claim: Claim = Claim([(merit.mints[-1], 0)], edPubKey)
claim.sign(PrivateKey(0))
transactions.add(claim)

merit.add(
    PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
                   packets=[VerificationPacket(claim.hash, list(range(2)))
                            ]).finish(0, merit))
示例#16
0
from typing import IO, Any
import json

import ed25519

from e2e.Classes.Transactions.Data import Data
from e2e.Classes.Transactions.Transactions import Transactions

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

from e2e.Vectors.Generation.PrototypeChain import PrototypeChain

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

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

proto: PrototypeChain = PrototypeChain(5)
for _ in range(80):
    proto.add(1)

for _ in range(14):
    proto.add(2)

data: Data = Data(bytes(32), edPubKey.to_bytes())
data.sign(edPrivKey)
data.beat(spamFilter)
transactions.add(data)
示例#17
0
文件: Data.py 项目: vporton/Meros
 def beat(self, spamFilter: SpamFilter) -> None:
     result: Tuple[bytes,
                   int] = spamFilter.beat(self.hash,
                                          (101 + len(self.data)) // 102)
     self.argon = result[0]
     self.proof = result[1]
示例#18
0
 def beat(self, spamFilter: SpamFilter) -> None:
     result: Tuple[bytes, int] = spamFilter.beat(
         self.hash,
         (70 + (33 * len(self.inputs)) + (40 * len(self.outputs))) // 143)
     self.argon = result[0]
     self.proof = result[1]
示例#19
0
def EightyEightTest(
  rpc: RPC
) -> None:
  edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32)
  edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

  blsPrivKey: PrivateKey = PrivateKey(0)
  blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex()

  file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
  blocks: List[Dict[str, Any]] = json.loads(file.read())
  file.close()

  merit: Merit = Merit()
  dataFilter: SpamFilter = SpamFilter(5)

  #Handshake with the node.
  rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
  rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

  #Send the first Block.
  block: Block = Block.fromJSON(blocks[0])
  merit.blockchain.add(block)
  rpc.meros.liveBlockHeader(block.header)

  #Handle sync requests.
  reqHash: bytes = bytes()
  while True:
    msg: bytes = rpc.meros.sync.recv()
    if MessageType(msg[0]) == MessageType.BlockBodyRequest:
      reqHash = msg[1 : 33]
      if reqHash != block.header.hash:
        raise TestError("Meros asked for a Block Body that didn't belong to the Block we just sent it.")

      #Send the BlockBody.
      rpc.meros.blockBody(block)
      break

    else:
      raise TestError("Unexpected message sent: " + msg.hex().upper())

  if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
    raise TestError("Meros didn't broadcast the Block Header it just added.")

  #Create two Datas.
  datas: List[Data] = [Data(bytes(32), edPubKey.to_bytes())]
  datas.append(Data(datas[0].hash, b"Hello there! General Kenobi."))

  for data in datas:
    #Sign them and have them beat the spam filter.
    data.sign(edPrivKey)
    data.beat(dataFilter)

    #Transmit them.
    rpc.meros.liveTransaction(data)

  #Verify both.
  verifs: List[SignedVerification] = [
    SignedVerification(datas[0].hash),
    SignedVerification(datas[1].hash)
  ]
  for verif in verifs:
    verif.sign(0, blsPrivKey)

  #Only transmit the second.
  rpc.meros.signedElement(verifs[1])
  sleep(0.5)

  #Verify the block template has no verifications.
  if bytes.fromhex(
    rpc.call("merit", "getBlockTemplate", [blsPubKey])["header"]
  )[36 : 68] != bytes(32):
    raise TestError("Block template has Verification Packets.")

  #Transmit the first signed verification.
  rpc.meros.signedElement(verifs[0])
  sleep(0.5)

  #Verify the block template has both verifications.
  template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey])
  template["header"] = bytes.fromhex(template["header"])
  packets: List[VerificationPacket] = [VerificationPacket(datas[0].hash, [0]), VerificationPacket(datas[1].hash, [0])]
  if template["header"][36 : 68] != BlockHeader.createContents(packets):
    raise TestError("Block template doesn't have both Verification Packets.")

  #Mine the Block.
  block = Block(
    BlockHeader(
      0,
      block.header.hash,
      BlockHeader.createContents(packets),
      1,
      template["header"][-43 : -39],
      BlockHeader.createSketchCheck(template["header"][-43 : -39], packets),
      0,
      int.from_bytes(template["header"][-4:], byteorder="little")
    ),
    BlockBody(
      packets,
      [],
      Signature.aggregate([verifs[0].signature, verifs[1].signature])
    )
  )
  if block.header.serializeHash()[:-4] != template["header"]:
    raise TestError("Failed to recreate the header.")
  if block.body.serialize(
    block.header.sketchSalt,
    len(packets)
  ) != bytes.fromhex(template["body"]):
    raise TestError("Failed to recreate the body.")

  block.mine(blsPrivKey, merit.blockchain.difficulty())
  merit.blockchain.add(block)

  rpc.call(
    "merit",
    "publishBlock",
    [
      template["id"],
      (
        template["header"] +
        block.header.proof.to_bytes(4, byteorder="little") +
        block.header.signature +
        block.body.serialize(block.header.sketchSalt, len(packets))
      ).hex()
    ]
  )

  verifyBlockchain(rpc, merit.blockchain)
示例#20
0
    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()
示例#21
0
def TwoHundredThirtyFiveTest(rpc: RPC) -> None:
    blockchain: Blockchain = Blockchain()
    dataFilter: SpamFilter = SpamFilter(5)

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

    #Mine one Block to the node.
    blsPrivKey: PrivateKey = PrivateKey(
        bytes.fromhex(rpc.call("personal", "getMiner")))
    blsPubKey: bytes = blsPrivKey.toPublicKey().serialize()

    #Call getBlockTemplate just to get an ID.
    #Skips the need to write a sync loop for the BlockBody.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        [blsPubKey.hex()])

    #Mine a Block.
    block = Block(
        BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1,
                    bytes(4), bytes(32), blsPubKey,
                    blockchain.blocks[0].header.time + 1200, 0), BlockBody())
    block.mine(blsPrivKey, blockchain.difficulty())
    blockchain.add(block)

    rpc.call("merit", "publishBlock",
             [template["id"], block.serialize().hex()])

    #Send Meros a Data and receive its Verification to make sure it's verifying Transactions in the first place.
    data: Data = Data(bytes(32), edPubKey.to_bytes())
    data.sign(edPrivKey)
    data.beat(dataFilter)

    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back the Data.")
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't send us its SignedVerification.")

    #Close our connection and mine 8 Blocks so its Merit is locked.
    rpc.meros.live.connection.close()
    for _ in range(8):
        block = Block(
            BlockHeader(0, blockchain.blocks[-1].header.hash, bytes(32), 1,
                        bytes(4), bytes(32), 0,
                        blockchain.blocks[-1].header.time + 1200, 0),
            BlockBody())
        #Reusing its key is fine as mining doesn't count as participation.
        block.mine(blsPrivKey, blockchain.difficulty())
        blockchain.add(block)

    #Sleep 30 seconds to make sure Meros noted we disconnected, and then reconnect.
    sleep(30)
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)

    #Sync the Blocks.
    for b in range(8):
        header: bytes = rpc.meros.liveBlockHeader(blockchain.blocks[b +
                                                                    2].header)
        if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() +
                                     blockchain.blocks[b + 2].header.hash):
            raise TestError("Meros didn't request the BlockBody.")
        rpc.meros.blockBody(blockchain.blocks[b + 2])
        if rpc.meros.live.recv() != header:
            raise TestError("Meros didn't send back the header.")
        if MessageType(
                rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
            raise TestError("Meros didn't verify this Block's data.")

    #Verify its Merit is locked.
    #Theoretically, all code after this check is unecessary.
    #Meros verifies a Block's Data after updating its State.
    #Therefore, if the above last Block had its Data verified, this issue should be closed.
    #That said, the timing is a bit too tight for comfort.
    #Better safe than sorry. Hence why the code after this check exists.
    if rpc.call("merit", "getMerit", [0])["status"] != "Locked":
        raise TestError("Merit wasn't locked when it was supposed to be.")

    #Send it a Transaction and make sure Meros verifies it, despite having its Merit locked.
    data = Data(data.hash, edPubKey.to_bytes())
    data.sign(edPrivKey)
    data.beat(dataFilter)

    if rpc.meros.liveTransaction(data) != rpc.meros.live.recv():
        raise TestError("Meros didn't send back the Data.")
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification:
        raise TestError("Meros didn't send us its SignedVerification.")
示例#22
0
edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key()

#Create the SendDifficulty MR.
sendDiff: SignedSendDifficulty = SignedSendDifficulty(4, 0)
sendDiff.sign(0, blsPrivKey)
sendDiffMR: SignedMeritRemoval = SignedMeritRemoval(sendDiff, sendDiff)

#Create the DataDifficulty MR.
dataDiff: SignedDataDifficulty = SignedDataDifficulty(4, 0)
dataDiff.sign(0, blsPrivKey)
dataDiffMR: SignedMeritRemoval = SignedMeritRemoval(dataDiff, dataDiff)

#Create the Verification MR.
data: Data = Data(bytes(32), edPubKey.to_bytes())
data.sign(edPrivKey)
data.beat(SpamFilter(5))
verif: SignedVerification = SignedVerification(data.hash)
verif.sign(0, blsPrivKey)

#Transform the Verification to a SignedMeritRemovalVerificationPacket.
packet: SignedMeritRemovalVerificationPacket = SignedMeritRemovalVerificationPacket(
  SignedVerificationPacket(data.hash),
  [blsPubKey.serialize()],
  verif.signature
)
verifMR: SignedMeritRemoval = SignedMeritRemoval(verif, packet)

result: Dict[str, Any] = {
  "blockchain": proto.toJSON(),
  "removals": [
    sendDiffMR.toSignedJSON(),
示例#23
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()
示例#24
0
def getBlockTemplateTest(rpc: RPC) -> None:
    edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    edPubKey: bytes = edPrivKey.get_verifying_key()
    blockchain: Blockchain = Blockchain()

    #Get multiple templates to verify they share an ID if they're requested within the same second.
    templates: List[Dict[str, Any]] = []
    startTime: float = nextSecond()
    for k in range(5):
        templates.append(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(k)},
                     False))
    if int(startTime) != int(time.time()):
        #Testing https://github.com/MerosCrypto/Meros/issues/278 has a much more forgiving timer of < 1 second each.
        #That said, this test was written on the fair assumption of all the above taking place in a single second.
        raise Exception(
            "getBlockTemplate is incredibly slow, to the point an empty Block template takes > 0.2 seconds to grab, invalidating this test."
        )

    for k, template in zip(range(5), templates):
        if template["id"] != int(startTime):
            raise TestError("Template ID isn't the time.")

        #Also check general accuracy.
        if bytes.fromhex(template["key"]) != blockchain.genesis:
            raise TestError("Template has the wrong RandomX key.")

        bytesHeader: bytes = bytes.fromhex(template["header"])
        serializedHeader: bytes = BlockHeader(
            0, blockchain.blocks[0].header.hash, bytes(32), 0, bytes(4),
            bytes(32),
            PrivateKey(k).toPublicKey().serialize(),
            int(startTime)).serialize()[:-52]
        #Skip over the randomized sketch salt.
        if (bytesHeader[:72] + bytesHeader[76:]) != (serializedHeader[:72] +
                                                     serializedHeader[76:]):
            raise TestError("Template has an invalid header.")
        #Difficulty modified as this is a new miner.
        if template["difficulty"] != (blockchain.difficulty() * 11 // 10):
            raise TestError("Template's difficulty is wrong.")

    currTime: int = int(nextSecond())
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": getMiner(0)}, False)
    if template["id"] != currTime:
        raise TestError("Template ID wasn't advanced with the time.")

    #Override the ID to enable easy comparison against a historical template.
    template["id"] = int(startTime)

    if int.from_bytes(bytes.fromhex(template["header"])[-4:],
                      "little") != currTime:
        raise TestError("The header has the wrong time.")
    template["header"] = (
        bytes.fromhex(template["header"])[:72] +
        #Use the header we'll compare to's salt.
        bytes.fromhex(templates[0]["header"])[72:76] +
        bytes.fromhex(template["header"])[76:-4] +
        #Also use its time.
        int(startTime).to_bytes(4, "little")).hex().upper()

    if template != templates[0]:
        raise TestError(
            "Template, minus the time difference, doesn't match the originally provided template."
        )

    #Test that the templates are deleted whenever a new Block appears.
    #This is done by checking the error given when we use an old template.
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        block: Block = Block.fromJSON(json.loads(file.read())[0])
        blockchain.add(block)
        rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
        rpc.meros.syncConnect(blockchain.blocks[0].header.hash)
        rpc.meros.liveBlockHeader(block.header)
        rpc.meros.rawBlockBody(block, 0)
        time.sleep(1)
    #Sanity check.
    if rpc.call("merit", "getHeight", auth=False) != 2:
        raise Exception("Didn't successfully send Meros the Block.")

    #Get a new template so Meros realizes the template situation has changed.
    rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)}, False)

    try:
        rpc.call("merit", "publishBlock", {
            "id": int(startTime),
            "header": ""
        }, False)
        raise Exception("")
    except Exception as e:
        if str(e) != "-2 Invalid ID.":
            raise TestError("Meros didn't delete old template IDs.")

    #Test VerificationPacket inclusion.
    data: Data = Data(bytes(32), edPubKey)
    data.sign(edPrivKey)
    data.beat(SpamFilter(5))
    verif: SignedVerification = SignedVerification(data.hash)
    verif.sign(0, PrivateKey(0))
    packet = VerificationPacket(data.hash, [0])

    rpc.meros.liveTransaction(data)
    rpc.meros.signedElement(verif)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet]):
        raise TestError(
            "Meros didn't include the Verification in its new template.")

    #Test Element inclusion.
    sendDiff: SignedSendDifficulty = SignedSendDifficulty(0, 0)
    sendDiff.sign(0, PrivateKey(0))
    rpc.meros.signedElement(sendDiff)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet], [sendDiff]):
        raise TestError(
            "Meros didn't include the Element in its new template.")

    #The 88 test checks for the non-inclusion of Verifications with unmentioned predecessors.
    #Test for non-inclusion of Elements with unmentioned predecessors.
    sendDiffChild: SignedSendDifficulty = SignedSendDifficulty(0, 2)
    sendDiffChild.sign(0, PrivateKey(0))
    rpc.meros.signedElement(sendDiffChild)
    time.sleep(1)
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [packet], [sendDiff]):
        raise TestError(
            "Meros did include an Element with an unmentioned parent in its new template."
        )

    #If we send a block with a time in the future, yet within FTL (of course), make sure Meros can still generate a template.
    #Naively using the current time will create a negative clock, which isn't allowed.
    #Start by closing the sockets to give us time to work.
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()
    #Sleep to reset the connection state.
    time.sleep(35)

    #Create and mine the Block.
    header: BlockHeader = BlockHeader(
        0,
        blockchain.blocks[-1].header.hash,
        bytes(32),
        0,
        bytes(4),
        bytes(32),
        PrivateKey(0).toPublicKey().serialize(),
        0,
    )
    miningStart: int = 0
    #If this block takes longer than 10 seconds to mine, try another.
    #Low future time (20 seconds) is chosen due to feasibility + supporting lowering the FTL in the future.
    while time.time() > miningStart + 10:
        miningStart = int(time.time())
        header = BlockHeader(
            0,
            blockchain.blocks[-1].header.hash,
            bytes(32),
            0,
            bytes(4),
            bytes(32),
            #Mod by something is due to a 2-byte limit (IIRC -- Kayaba).
            #100 is just clean. +11 ensures an offset from the above, which really shouldn't be necessary.
            #If we did need one, +1 should work, as we only ever work with PrivateKey(0) on the blockchain.
            PrivateKey((miningStart % 100) + 10).toPublicKey().serialize(),
            int(time.time()) + 20,
        )
        header.mine(PrivateKey((miningStart % 100) + 10),
                    blockchain.difficulty() * 11 // 10)
    blockchain.add(Block(header, BlockBody()))

    #Send it and verify it.
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(blockchain.blocks[0].header.hash)
    rpc.meros.liveBlockHeader(header)
    rpc.meros.rawBlockBody(Block(header, BlockBody()), 0)
    rpc.meros.live.connection.close()
    rpc.meros.sync.connection.close()
    time.sleep(1)

    #Ensure a stable template ID.
    currTime = int(nextSecond())
    template = rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                        False)
    if template["id"] != currTime:
        raise TestError(
            "Template ID isn't the time when the previous Block is in the future."
        )
    if int.from_bytes(bytes.fromhex(template["header"])[-4:],
                      "little") != (header.time + 1):
        raise TestError(
            "Meros didn't handle generating a template off a Block in the future properly."
        )

    #Verify a Block with three Elements from a holder, where two form a Merit Removal.
    #Only the two which cause a MeritRemoval should be included.
    #Mine a Block to a new miner and clear the current template with it (might as well).
    #Also allows us to test template clearing.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": getMiner(1)}, False)
    #Mine the Block.
    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 = PrivateKey(1).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()
        })
    time.sleep(1)

    #Verify the template was cleared.
    currTime = int(nextSecond())
    bytesHeader: bytes = bytes.fromhex(
        rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                 False)["header"])
    serializedHeader: bytes = BlockHeader(
        0,
        tempHash,
        bytes(32),
        0,
        bytes(4),
        bytes(32),
        0,
        #Ensures that the previous time manipulation doesn't come back to haunt us.
        max(currTime, blockchain.blocks[-1].header.time + 1)).serialize()[:-52]
    #Skip over the randomized sketch salt and time (which we don't currently have easy access to).
    if (bytesHeader[:72] + bytesHeader[76:-4]) != (serializedHeader[:72] +
                                                   serializedHeader[76:-4]):
        raise TestError("Template wasn't cleared.")

    #Sleep so we can reconnect.
    time.sleep(35)
    rpc.meros.liveConnect(blockchain.blocks[0].header.hash)

    #Finally create the Elements.
    dataDiff: SignedDataDifficulty = SignedDataDifficulty(1, 0)
    dataDiff.sign(2, PrivateKey(1))
    rpc.meros.signedElement(dataDiff)
    sendDiffs: List[SignedSendDifficulty] = [
        SignedSendDifficulty(1, 1),
        SignedSendDifficulty(2, 1)
    ]
    for sd in sendDiffs:
        sd.sign(2, PrivateKey(1))
        rpc.meros.signedElement(sd)
    time.sleep(1)

    #`elem for elem` is used below due to Pyright not handling inheritance properly when nested.
    #pylint: disable=unnecessary-comprehension
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate", {"miner": getMiner(0)},
                     False)["header"])[36:68] != BlockHeader.createContents(
                         [], [elem for elem in sendDiffs[::-1]]):
        raise TestError(
            "Meros didn't include just the malicious Elements in its new template."
        )
示例#25
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()
示例#26
0
def EightyEightTest(rpc: RPC) -> None:
    edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
    edPubKey: bytes = edPrivKey.get_verifying_key()

    blsPrivKey: PrivateKey = PrivateKey(0)
    blsPubKey: str = blsPrivKey.toPublicKey().serialize().hex()

    merit: Merit = Merit()
    dataFilter: SpamFilter = SpamFilter(5)

    #Handshake with the node.
    rpc.meros.liveConnect(merit.blockchain.blocks[0].header.hash)
    rpc.meros.syncConnect(merit.blockchain.blocks[0].header.hash)

    #Send the first Block.
    block: Block
    with open("e2e/Vectors/Merit/BlankBlocks.json", "r") as file:
        block = Block.fromJSON(json.loads(file.read())[0])
    merit.blockchain.add(block)
    rpc.meros.liveBlockHeader(block.header)
    rpc.meros.handleBlockBody(block)
    if MessageType(rpc.meros.live.recv()[0]) != MessageType.BlockHeader:
        raise TestError(
            "Meros didn't broadcast the Block Header it just added.")

    #Create two Datas.
    datas: List[Data] = [Data(bytes(32), edPubKey)]
    datas.append(Data(datas[0].hash, b"Hello there! General Kenobi."))

    for data in datas:
        #Sign them and have them beat the spam filter.
        data.sign(edPrivKey)
        data.beat(dataFilter)

        #Transmit them.
        rpc.meros.liveTransaction(data)

    #Verify both.
    verifs: List[SignedVerification] = [
        SignedVerification(datas[0].hash),
        SignedVerification(datas[1].hash)
    ]
    for verif in verifs:
        verif.sign(0, blsPrivKey)

    #Only transmit the second.
    rpc.meros.signedElement(verifs[1])
    sleep(1.5)

    #Verify the block template has no verifications.
    if bytes.fromhex(
            rpc.call("merit", "getBlockTemplate",
                     {"miner": blsPubKey})["header"])[36:68] != bytes(32):
        raise TestError("Block template has Verification Packets.")

    #Transmit the first signed verification.
    rpc.meros.signedElement(verifs[0])
    sleep(1.5)

    #Verify the block template has both verifications.
    template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate",
                                        {"miner": blsPubKey})
    template["header"] = bytes.fromhex(template["header"])
    packets: List[VerificationPacket] = [
        VerificationPacket(datas[0].hash, [0]),
        VerificationPacket(datas[1].hash, [0])
    ]
    if template["header"][36:68] != BlockHeader.createContents(packets):
        raise TestError(
            "Block template doesn't have both Verification Packets.")

    #Mine the Block.
    block = Block(
        BlockHeader(
            0, block.header.hash, BlockHeader.createContents(packets),
            len(packets), template["header"][-43:-39],
            BlockHeader.createSketchCheck(template["header"][-43:-39],
                                          packets), 0,
            int.from_bytes(template["header"][-4:], byteorder="little")),
        BlockBody(
            packets, [],
            Signature.aggregate([verifs[0].signature, verifs[1].signature])))
    if block.header.serializeHash()[:-4] != template["header"]:
        raise TestError("Failed to recreate the header.")

    block.mine(blsPrivKey, merit.blockchain.difficulty())
    merit.blockchain.add(block)

    rpc.call(
        "merit", "publishBlock", {
            "id":
            template["id"],
            "header": (template["header"] +
                       block.header.proof.to_bytes(4, byteorder="little") +
                       block.header.signature).hex()
        })

    verifyBlockchain(rpc, merit.blockchain)
示例#27
0
    def test() -> None:
        #Send to the first address from outside the Wallet. First address is now funded.
        sendHash: bytes = createSend(
            rpc, claims[0], decodeAddress(rpc.call("personal", "getAddress")))

        #Send to the second address with all of the funds. Second address is now funded.
        #Tests send's minimal case (single input, no change).
        nextAddr: str = rpc.call("personal", "getAddress")
        sends: List[str] = [
            rpc.call(
                "personal", "send", {
                    "outputs": [{
                        "address": nextAddr,
                        "amount": str(claims[0].amount)
                    }]
                })
        ]
        checkSend(
            rpc, sends[-1], {
                "inputs": [{
                    "hash": sendHash.hex().upper(),
                    "nonce": 0
                }],
                "outputs": [{
                    "key": decodeAddress(nextAddr).hex().upper(),
                    "amount": str(claims[0].amount)
                }]
            })
        verify(rpc, bytes.fromhex(sends[-1]))

        #Send to the third address with some of the funds. Third and change addresses are now funded.
        #Tests send's capability to create a change output.
        mnemonic: str = rpc.call("personal", "getMnemonic")
        nextAddr = rpc.call("personal", "getAddress")
        sends.append(
            rpc.call(
                "personal", "send", {
                    "outputs": [{
                        "address": nextAddr,
                        "amount": str(claims[0].amount - 1)
                    }]
                }))
        checkSend(
            rpc, sends[-1], {
                "inputs": [{
                    "hash": sends[-2],
                    "nonce": 0
                }],
                "outputs":
                [{
                    "key": decodeAddress(nextAddr).hex().upper(),
                    "amount": str(claims[0].amount - 1)
                }, {
                    "key": getChangePublicKey(mnemonic, "", 0).hex().upper(),
                    "amount": "1"
                }]
            })
        verify(rpc, bytes.fromhex(sends[-1]))

        #Send all funds out of Wallet.
        #Tests MuSig signing and change UTXO detection.
        privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32)
        pubKey: bytes = privKey.get_verifying_key()
        sends.append(
            rpc.call(
                "personal", "send", {
                    "outputs": [{
                        "address":
                        bech32_encode("mr",
                                      convertbits(bytes([0]) + pubKey, 8, 5)),
                        "amount":
                        str(claims[0].amount)
                    }]
                }))
        checkSend(
            rpc, sends[-1], {
                "inputs": [{
                    "hash": sends[-2],
                    "nonce": 0
                }, {
                    "hash": sends[-2],
                    "nonce": 1
                }],
                "outputs": [{
                    "key": pubKey.hex().upper(),
                    "amount": str(claims[0].amount)
                }]
            })
        verify(rpc, bytes.fromhex(sends[-1]))

        #Clear Wallet. Set a password this time around to make sure the password is properly carried.
        #Send two instances of funds to the first address.
        rpc.call("personal", "setWallet", {"password": "******"})
        mnemonic = rpc.call("personal", "getMnemonic")
        nodeKey: bytes = decodeAddress(rpc.call("personal", "getAddress"))
        send: Send = Send([(bytes.fromhex(sends[-1]), 0)],
                          [(nodeKey, claims[0].amount // 2),
                           (nodeKey, claims[0].amount // 2)])
        send.sign(Ristretto.SigningKey(b'\0' * 32))
        send.beat(SpamFilter(3))
        if rpc.meros.liveTransaction(send) != rpc.meros.live.recv():
            raise TestError("Meros didn't send back a Send.")
        verify(rpc, send.hash)
        sends = [send.hash.hex().upper()]

        #Send to self.
        #Tests send's capability to handle multiple UTXOs per key/lack of aggregation when all keys are the same/multiple output Sends.
        nextAddr = rpc.call("personal", "getAddress")
        changeKey: bytes = getChangePublicKey(mnemonic, "test", 0)
        sends.append(
            rpc.call(
                "personal", "send", {
                    "outputs": [{
                        "address": nextAddr,
                        "amount": str(claims[0].amount - 1)
                    }],
                    "password":
                    "******"
                }))
        checkSend(
            rpc, sends[-1], {
                "inputs": [{
                    "hash": sends[-2],
                    "nonce": 0
                }, {
                    "hash": sends[-2],
                    "nonce": 1
                }],
                "outputs": [{
                    "key": decodeAddress(nextAddr).hex().upper(),
                    "amount": str(claims[0].amount - 1)
                }, {
                    "key": changeKey.hex().upper(),
                    "amount": "1"
                }]
            })
        verify(rpc, bytes.fromhex(sends[-1]))

        #Externally send to the second/change address.
        #Enables entering multiple instances of each key into MuSig, which is significant as we originally only used the unique keys.
        sends.append(
            createSend(rpc, claims[1], decodeAddress(nextAddr)).hex().upper())
        sends.append(createSend(rpc, claims[2], changeKey).hex().upper())

        #Check personal_getUTXOs.
        utxos: List[Dict[str, Any]] = [{
            "hash": sends[-3],
            "nonce": 0,
            "address": nextAddr
        }, {
            "hash":
            sends[-3],
            "nonce":
            1,
            "address":
            bech32_encode("mr", convertbits(bytes([0]) + changeKey, 8, 5))
        }, {
            "hash": sends[-2],
            "nonce": 0,
            "address": nextAddr
        }, {
            "hash":
            sends[-1],
            "nonce":
            0,
            "address":
            bech32_encode("mr", convertbits(bytes([0]) + changeKey, 8, 5))
        }]
        if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos):
            raise TestError("personal_getUTXOs was incorrect.")
        for utxo in utxos:
            del utxo["address"]

        #Send to any address with all funds minus one.
        #Test MuSig signing, multiple inputs per key on account chains, change output creation to the next change key...
        sends.append(
            rpc.call(
                "personal", "send", {
                    "outputs": [{
                        "address":
                        nextAddr,
                        "amount":
                        str(claims[0].amount + claims[1].amount +
                            claims[2].amount - 1)
                    }],
                    "password":
                    "******"
                }))
        checkSend(
            rpc, sends[-1], {
                "inputs":
                utxos,
                "outputs": [{
                    "key":
                    decodeAddress(nextAddr).hex().upper(),
                    "amount":
                    str(claims[0].amount + claims[1].amount +
                        claims[2].amount - 1)
                }, {
                    "key":
                    getChangePublicKey(mnemonic, "test", 1).hex().upper(),
                    "amount":
                    "1"
                }]
            })
        verify(rpc, bytes.fromhex(sends[-1]))

        #Mine a Block so we can reboot the node without losing data.
        blsPrivKey: PrivateKey = PrivateKey(
            bytes.fromhex(rpc.call("personal", "getMeritHolderKey")))
        for _ in range(6):
            template: Dict[str, Any] = rpc.call(
                "merit", "getBlockTemplate",
                {"miner": blsPrivKey.toPublicKey().serialize().hex()})
            proof: int = -1
            tempHash: bytes = bytes()
            tempSignature: bytes = bytes()
            while ((proof == -1) or (
                (int.from_bytes(tempHash, "little") * template["difficulty"]) >
                    int.from_bytes(bytes.fromhex("FF" * 32), "little"))):
                proof += 1
                tempHash = RandomX(
                    bytes.fromhex(template["header"]) +
                    proof.to_bytes(4, "little"))
                tempSignature = blsPrivKey.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()
                })

        #Reboot the node and verify it still tracks the same change address.
        #Also reload the Wallet and verify it still tracks the same change address.
        #Really should be part of address discovery; we just have the opportunity right here.
        #Due to the timing of how the codebase was developed, and a personal frustration for how long this has taken...
        rpc.quit()
        sleep(3)
        rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc)
        if rpc.call("personal", "getTransactionTemplate",
                    {"outputs": [{
                        "address": nextAddr,
                        "amount": "1"
                    }]})["outputs"][1]["key"] != getChangePublicKey(
                        mnemonic, "test", 2).hex().upper():
            raise TestError(
                "Rebooting the node caused the WalletDB to stop tracking the next change address."
            )
        rpc.call("personal", "setAccount", rpc.call("personal", "getAccount"))
        if rpc.call("personal", "getTransactionTemplate",
                    {"outputs": [{
                        "address": nextAddr,
                        "amount": "1"
                    }]})["outputs"][1]["key"] != getChangePublicKey(
                        mnemonic, "test", 2).hex().upper():
            raise TestError(
                "Reloading the Wallet caused the WalletDB to stop tracking the next change address."
            )

        raise SuccessError()
示例#28
0
proto.add(3)
proto.add(4)
proto.add(elements=[SendDifficulty(1, 0, 2), SendDifficulty(1, 0, 4)])
merit: Merit = Merit.fromJSON(proto.toJSON())
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,
示例#29
0
#JSON standard lib.
import json

#Blank Blocks.
bbFile: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r")
blankBlocks: List[Dict[str, Any]] = json.loads(bbFile.read())
bbFile.close()

#Transactions.
transactions: Transactions = Transactions()
#Merit.
merit: Merit = Merit()

#SpamFilter.
dataFilter: SpamFilter = SpamFilter(5)

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

#BLS keys.
blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest())
blsPubKey: PublicKey = blsPrivKey.toPublicKey()

#Add 1 Blank Block.
for i in range(1):
  merit.add(Block.fromJSON(blankBlocks[i]))

#Create the Data and a successor.
first: Data = Data(bytes(32), edPubKey.to_bytes())
示例#30
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()