示例#1
0
文件: Lib.py 项目: Vyryn/Meros
def getIndex(
  mnemonic: str,
  password: str,
  skip: int
) -> int:
  seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest()

  c: int = -1
  failures: int = 0
  while skip != -1:
    c += 1
    try:
      BIP32.derive(
        seed,
        [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0, c]
      )

      #Since we derived a valid address, decrement skip.
      skip -= 1
      failures = 0
    except Exception:
      #Safety check to prevent infinite execution.
      failures += 1
      if failures == 100:
        raise Exception("Invalid mnemonic passed to getPrivateKey.")
      continue

  return c
示例#2
0
文件: Lib.py 项目: Vyryn/Meros
def getMnemonic(
  password: str = ""
) -> str:
  while True:
    res: str = Bip39MnemonicGenerator.FromWordsNumber(Bip39WordsNum.WORDS_NUM_24)
    seed: bytes = sha256(Bip39SeedGenerator(res).Generate(password)).digest()
    try:
      BIP32.derive(seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0])
      BIP32.derive(seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 1])
    except Exception:
      continue
    return res
示例#3
0
文件: Lib.py 项目: Vyryn/Meros
def getChangePublicKey(
  mnemonic: str,
  password: str,
  skip: int
) -> bytes:
  seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest()
  extendedKey: bytes = bytes()

  #Above's getIndex, yet utilizing the return value of derive
  c: int = -1
  failures: int = 0
  while skip != -1:
    c += 1
    try:
      extendedKey = BIP32.derive(
        seed,
        [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 1, c]
      )

      #Since we derived a valid address, decrement skip.
      skip -= 1
      failures = 0
    except Exception:
      #Safety check to prevent infinite execution.
      failures += 1
      if failures == 100:
        raise Exception("Invalid mnemonic passed to getPrivateKey.")
      continue

  return RistrettoScalar(extendedKey[:32]).toPoint().serialize()
示例#4
0
文件: Lib.py 项目: Vyryn/Meros
def getPrivateKey(
  mnemonic: str,
  password: str,
  skip: int
) -> bytes:
  seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest()
  return BIP32.derive(
    seed,
    [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0, getIndex(mnemonic, password, skip)]
  )
示例#5
0
def DerivationTest(rpc: RPC) -> None:
    #Start by testing BIP 32, 39, and 44 functionality in general.
    for _ in range(10):
        rpc.call("personal", "setWallet")
        verifyMnemonicAndAccount(rpc)

    #Set specific Mnemonics and ensure they're handled properly.
    for _ in range(10):
        mnemonic: str = getMnemonic()
        rpc.call("personal", "setWallet", {"mnemonic": mnemonic})
        verifyMnemonicAndAccount(rpc, mnemonic)

    #Create Mnemonics with passwords and ensure they're handled properly.
    for _ in range(10):
        password: str = os.urandom(32).hex()
        rpc.call("personal", "setWallet", {"password": password})
        verifyMnemonicAndAccount(rpc, password=password)

    #Set specific Mnemonics with passwords and ensure they're handled properly.
    for i in range(10):
        password: str = os.urandom(32).hex()
        #Non-hex string.
        if i == 0:
            password = "******"
        mnemonic: str = getMnemonic(password)
        rpc.call("personal", "setWallet", {
            "mnemonic": mnemonic,
            "password": password
        })
        verifyMnemonicAndAccount(rpc, mnemonic, password)

    #setWallet, getMnemonic, getMeritHolderKey, getMeritHolderNick's non-existent case, and getAccount have now been tested.
    #This leaves getAddress with specific indexes.

    #Clear the Wallet.
    rpc.call("personal", "setWallet")

    #Start by testing specific derivation.
    password: str = "password since it shouldn't be relevant"
    for _ in range(10):
        mnemonic: str = getMnemonic(password)
        index: int = 100
        key: bytes
        while True:
            try:
                key = BIP32.derive(
                    sha256(Bip39SeedGenerator(mnemonic).Generate(
                        password)).digest(), [
                            44 + (1 << 31), 5132 + (1 << 31), 0 +
                            (1 << 31), 0, index
                        ])
                break
            except Exception:
                index += 1

        rpc.call("personal", "setWallet", {
            "mnemonic": mnemonic,
            "password": password
        })
        addr: str = bech32_encode(
            "mr",
            convertbits(
                bytes([0]) + RistrettoScalar(key[:32]).toPoint().serialize(),
                8, 5))
        if rpc.call("personal", "getAddress", {"index": index}) != addr:
            raise TestError("Didn't get the correct address for this index.")

    #Test if a specific address is requested, it won't come up naturally.
    #This isn't explicitly required by the RPC spec, which has been worded carefully to leave this open ended.
    #The only requirement is the address was never funded and the index is sequential (no moving backwards).
    #The node offers this feature to try to make mixing implicit/explicit addresses safer, along with some internal benefits.
    #That said, said internal benefits are minimal or questionable, hence why the RPC docs are open ended.
    #This way we can decide differently in the future.
    rpc.call("personal", "setWallet")
    firstAddr: str = rpc.call("personal", "getAddress")
    #Explicitly get the first address.
    for i in range(256):
        try:
            rpc.call("personal", "getAddress", {"index": i})
            break
        except TestError:
            if i == 255:
                raise Exception(
                    "The first 256 address were invalid; this should be practically impossible."
                )
    if firstAddr == rpc.call("personal", "getAddress"):
        raise TestError("Explicitly grabbed address was naturally returned.")

    #Test error cases.

    #Mnemonic with an improper amount of entropy.
    #Runs multiple times in case the below error pops up for the sole reason the Mnemonic didn't have viable keys.
    #This should error earlier than that though.
    for _ in range(16):
        try:
            rpc.call(
                "personal", "setWallet", {
                    "mnemonic":
                    Bip39MnemonicGenerator.FromWordsNumber(
                        Bip39WordsNum.WORDS_NUM_12)
                })
            raise Exception()
        except Exception as e:
            if str(e) != "-3 Invalid mnemonic or password.":
                raise TestError(
                    "Could set a Mnemonic with too little entropy.")

    #Mnemonic with additional spaces.
    rpc.call("personal", "setWallet")
    mnemonic: str = rpc.call("personal", "getMnemonic")
    rpc.call("personal", "setWallet",
             {"mnemonic": "   " + (" " * 2).join(mnemonic.split(" ")) + " "})
    if rpc.call("personal", "getMnemonic") != mnemonic:
        raise TestError(
            "Meros didn't handle a mnemonic with extra whitespace.")

    #Negative index to getAddress.
    try:
        rpc.call("personal", "getAddress", {"index": -1})
        raise Exception()
    except Exception as e:
        if str(e) != "-32602 Invalid params.":
            raise TestError("Could call getAddress with a negative index.")
示例#6
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.")
示例#7
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.")