Beispiel #1
0
    def string(self):
        """
        A base-58 encoding of the pubkey hash.

        Returns: 
            str: The encoded address.
        """
        b = ByteArray(self.netID)
        b += self.pkHash
        b += checksum(b.b)
        x = b.int()

        answer = ""

        while x > 0:
            m = x % RADIX
            x = x // RADIX
            answer += ALPHABET[m]

        while len(answer) < len(b) * 136 // 100:
            answer += ALPHABET[0]

        # reverse
        return answer[::-1]
Beispiel #2
0
class ExtendedKey:
    """
    ExtendedKey houses all the information needed to support a BIP0044 
    hierarchical deterministic extended key.
    """
    def __init__(self, privVer, pubVer, key, pubKey, chainCode, parentFP,
                 depth, childNum, isPrivate):
        """
        Args:
            privVer (byte-like): Network version bytes for extended priv keys.
            pubVer (byte-like): Network version bytes for extended pub keys
            key (byte-like): The key. 
            pubKey (byte-like): Will be the same as `key` for public key. Will
                be generated from key if zero is provided. 
            chainCode (byte-like): Chain code for key derivation.
            parentFP (ByteArray): parent key fingerprint
            depth (int): Key depth. 
            childNum (int): Child number.
            isPrivate (bool): Whether the key is a private or public key. 
        """
        assert len(privVer) == 4 and len(
            pubVer) == 4, "Network version bytes of incorrect length"
        self.privVer = ByteArray(privVer)
        self.pubVer = ByteArray(pubVer)
        self.key = ByteArray(key)
        self.pubKey = ByteArray(pubKey)
        if self.pubKey.iszero():
            if isPrivate:
                self.pubKey = Curve.publicKey(
                    self.key.int()).serializeCompressed()
            else:
                self.pubKey = self.key
        self.chainCode = ByteArray(chainCode)
        self.parentFP = ByteArray(parentFP)
        self.depth = depth
        self.childNum = childNum
        self.isPrivate = isPrivate

    # def __tojson__(self):
    #     return {
    #         "privVer": self.privVer,
    #         "pubVer": self.pubVer,
    #         "key": self.key,
    #         "pubKey": self.pubKey,
    #         "chainCode": self.chainCode,
    #         "parentFP": self.parentFP,
    #         "depth": self.depth,
    #         "childNum": self.childNum,
    #         "isPrivate": self.isPrivate,
    #     }
    # @staticmethod
    # def __fromjson__(obj):
    #     return ExtendedKey(
    #         privVer = obj["privVer"],
    #         pubVer = obj["pubVer"],
    #         key = obj["key"],
    #         pubKey = obj["pubKey"],
    #         chainCode = obj["chainCode"],
    #         parentFP = obj["parentFP"],
    #         depth = obj["depth"],
    #         childNum = obj["childNum"],
    #         isPrivate = obj["isPrivate"],
    #     )
    def deriveCoinTypeKey(self, coinType):
        """
        First two hardened child derivations in accordance with BIP0044. 

        Args:
            coinType (int): The BIP0044 coin type. For a full list, see
                https://github.com/satoshilabs/slips/blob/master/slip-0044.md

        Returns:
            ExtendedKey: The coin-type key.
        """
        if coinType > MAX_COIN_TYPE:
            raise ParameterRangeError("coinType too high. %i > %i" %
                                      (coinType, MAX_COIN_TYPE))

        purpose = self.child(44 + HARDENED_KEY_START)

        # Derive the purpose key as a child of the master node.
        return purpose.child(coinType + HARDENED_KEY_START)

    def child(self, i):
        """
        Child returns a derived child extended key at the given index.  When this
        extended key is a private extended key (as determined by the IsPrivate
        function), a private extended key will be derived.  Otherwise, the derived
        extended key will be also be a public extended key.
                
        When the index is greater to or equal than the HardenedKeyStart constant, the
        derived extended key will be a hardened extended key.  It is only possible to
        derive a hardended extended key from a private extended key.  Consequently,
        this function will return ErrDeriveHardFromPublic if a hardened child
        extended key is requested from a public extended key.
                
        A hardened extended key is useful since, as previously mentioned, it requires
        a parent private extended key to derive.  In other words, normal child
        extended public keys can be derived from a parent public extended key (no
        knowledge of the parent private key) whereas hardened extended keys may not
        be.
                
        NOTE: There is an extremely small chance (< 1 in 2^127) the specific child
        index does not derive to a usable child.  The ErrInvalidChild error will be
        returned if this should occur, and the caller is expected to ignore the
        invalid child and simply increment to the next index.

        There are four scenarios that could happen here:
        1) Private extended key -> Hardened child private extended key
        2) Private extended key -> Non-hardened child private extended key
        3) Public extended key -> Non-hardened child public extended key
        4) Public extended key -> Hardened child public extended key (INVALID!)

        Args:
            i (int): Child number.

        Returns:
            ExtendedKey: The child key.
        """

        # Case #4 is invalid, so error out early.
        # A hardened child extended key may not be created from a public
        # extended key.
        isChildHardened = i >= HARDENED_KEY_START
        if not self.isPrivate and isChildHardened:
            raise ParameterRangeError(
                "cannot generate hardened child from public extended key")

        # The data used to derive the child key depends on whether or not the
        # child is hardened per [BIP32].
        #
        # For hardened children:
        #   0x00 || ser256(parentKey) || ser32(i)
        #
        # For normal children:
        #   serP(parentPubKey) || ser32(i)
        keyLen = 33
        data = ByteArray(bytearray(keyLen + 4))

        if isChildHardened:
            # Case #1.
            # When the child is a hardened child, the key is known to be a
            # private key due to the above early return.  Pad it with a
            # leading zero as required by [BIP32] for deriving the child.
            data[1] = self.key
        else:
            # Case #2 or #3.
            # This is either a public or private extended key, but in
            # either case, the data which is used to derive the child key
            # starts with the secp256k1 compressed public key bytes.
            data[0] = ByteArray(self.pubKey)
        data[keyLen] = ByteArray(i, length=len(data) - keyLen)

        data |= i  # ByteArray will handle the type conversion

        # Take the HMAC-SHA512 of the current key's chain code and the derived
        # data:
        #   I = HMAC-SHA512(Key = chainCode, Data = data)
        ilr = ByteArray(hmacDigest(self.chainCode.b, data.b, hashlib.sha512))

        # Split "I" into two 32-byte sequences Il and Ir where:
        #   Il = intermediate key used to derive the child
        #   Ir = child chain code
        il = ilr[:len(ilr) // 2]
        childChainCode = ilr[len(ilr) // 2:]

        # Both derived public or private keys rely on treating the left 32-byte
        # sequence calculated above (Il) as a 256-bit integer that must be
        # within the valid range for a secp256k1 private key.  There is a small
        # chance (< 1 in 2^127) this condition will not hold, and in that case,
        # a child extended key can't be created for this index and the caller
        # should simply increment to the next index.
        if il.int() >= Curve.N or il.iszero():
            raise ParameterRangeError(
                "ExtendedKey.child: generated Il outside valid range")

        # The algorithm used to derive the child key depends on whether or not
        # a private or public child is being derived.
        #
        # For private children:
        #   childKey = parse256(Il) + parentKey
        #
        # For public children:
        #   childKey = serP(point(parse256(Il)) + parentKey)
        isPrivate = False
        if self.isPrivate:
            # Case #1 or #2.
            # Add the parent private key to the intermediate private key to
            # derive the final child key.
            #
            # childKey = parse256(Il) + parenKey
            childKey = ByteArray((self.key.int() + il.int()) % Curve.N)
            isPrivate = True
        else:
            # Case #3.
            # Calculate the corresponding intermediate public key for
            # intermediate private key.

            x, y = Curve.scalarBaseMult(il.int())  # Curve.G as ECPointJacobian
            if x == 0 or y == 0:
                raise ParameterRangeError(
                    "ExtendedKey.child: generated pt outside valid range")
            # Convert the serialized compressed parent public key into X
            # and Y coordinates so it can be added to the intermediate
            # public key.
            pubKey = Curve.parsePubKey(self.key)
            # Add the intermediate public key to the parent public key to
            # derive the final child key.
            #
            # childKey = serP(point(parse256(Il)) + parentKey)
            # childX, childY := curve.Add(pt.x, pt.y, pubKey.X, pubKey.Y)
            childX, childY = Curve.add(x, y, pubKey.x, pubKey.y)
            # pk := secp256k1.PublicKey{Curve: secp256k1.S256(), X: childX, Y: childY}
            # childKey = pk.SerializeCompressed()
            childKey = PublicKey(Curve, childX, childY).serializeCompressed()

        # The fingerprint of the parent for the derived child is the first 4
        # bytes of the RIPEMD160(BLAKE256(parentPubKey)).
        parentFP = hash160(self.pubKey.b)[:4]

        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=childKey,
            pubKey="",
            chainCode=childChainCode,
            parentFP=parentFP,
            depth=self.depth + 1,
            childNum=i,
            isPrivate=isPrivate,
        )

    def deriveAccountKey(self, account):
        """
        deriveAccountKey derives the extended key for an account according to the
        hierarchy described by BIP0044 given the master node.
                
        In particular this is the hierarchical deterministic extended key path:
          m/44'/<coin type>'/<account>'

        Args:
            account (int): Account number.

        Returns: 
            ExtendedKey: Account key.
        """
        # Enforce maximum account number.
        if account > MAX_ACCOUNT_NUM:
            raise ParameterRangeError(
                "deriveAccountKey: account number greater than MAX_ACCOUNT_NUM"
            )

        # Derive the account key as a child of the coin type key.
        return self.child(account + HARDENED_KEY_START)

    def neuter(self):
        """
        neuter returns a new extended public key from this extended private key.  The
        same extended key will be returned unaltered if it is already an extended
        public key.

        As the name implies, an extended public key does not have access to the
        private key, so it is not capable of signing transactions or deriving
        child extended private keys.  However, it is capable of deriving further
        child extended public keys.

        Returns:
            ExtendedKey: The public extended key.
        """
        # Already an extended public key.
        if not self.isPrivate:
            return self

        # Convert it to an extended public key.  The key for the new extended
        # key will simply be the pubkey of the current extended private key.
        #
        # This is the function N((k,c)) -> (K, c) from [BIP32].
        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=self.pubKey,
            pubKey=self.pubKey,
            chainCode=self.chainCode,
            parentFP=self.parentFP,
            depth=self.depth,
            childNum=self.childNum,
            isPrivate=False,
        )

    def string(self):
        """
        string returns the extended key as a base58-encoded string. See
        `decodeExtendedKey` for decoding.

        Returns:
            str: The encoded extended key.
        """
        if self.key.iszero():
            raise ZeroBytesError("unexpected zero key")

        childNumBytes = ByteArray(self.childNum, length=4)
        depthByte = ByteArray(self.depth % 256, length=1)

        # The serialized format is:
        #   version (4) || depth (1) || parent fingerprint (4)) ||
        #   child num (4) || chain code (32) || key data (33) || checksum (4)
        serializedBytes = ByteArray(
            bytearray(0))  # length serializedKeyLen + 4 after appending
        if self.isPrivate:
            serializedBytes += self.privVer
        else:
            serializedBytes += self.pubVer

        serializedBytes += depthByte
        serializedBytes += self.parentFP
        serializedBytes += childNumBytes
        serializedBytes += self.chainCode
        if self.isPrivate:
            serializedBytes += bytearray(1)
            serializedBytes += self.key
        else:
            serializedBytes += self.pubKey

        checkSum = checksum(serializedBytes.b)[:4]
        serializedBytes += checkSum
        return b58encode(serializedBytes.bytes()).decode()

    def deriveChildAddress(self, i, net):
        """
        The base-58 encoded address for the i'th child.

        Args:
            i (int): Child number.
            net (obj): Network parameters.
        """
        child = self.child(i)
        return newAddressPubKeyHash(
            hash160(child.publicKey().serializeCompressed().b), net,
            STEcdsaSecp256k1).string()

    def privateKey(self):
        """
        A PrivateKey structure that can be used for signatures.

        Returns:
            PrivateKey: The private key structure.
        """
        return privKeyFromBytes(self.key)

    def publicKey(self):
        """
        A PublicKey structure of the pubKey.

        Returns:
            PublicKey: The public key structure.
        """
        return Curve.parsePubKey(self.pubKey)