## Substrate library

The Substrate library allows to [derive keys](https://wiki.polkadot.network/docs/learn-accounts#derivation-paths) for coins of the Polkadot ecosystem, since they don't follow BIP44.\
The module generates the same keys and addresses of Polkadot-JS and uses sr25519 curve for keys derivation.\
With respect to BIP-0032, Substrate paths can be also strings (in addition to numbers) and they are identified by a prefix:
- `/` for not-hardened (soft) derivation (e.g. "/soft")
- `//` for hardened derivation (e.g. "//hard")

### Coin types

Supported coins enumerative:

|Coin|Enum|
|---|---|
|Acala|`SubstrateCoins.ACALA`|
|Bifrost|`SubstrateCoins.BIFROST`|
|Chainx|`SubstrateCoins.CHAINX`|
|Edgeware|`SubstrateCoins.EDGEWARE`|
|Generic|`SubstrateCoins.GENERIC`|
|Karura|`SubstrateCoins.KARURA`|
|Kusama|`SubstrateCoins.KUSAMA`|
|Moonbeam|`SubstrateCoins.MOONBEAM`|
|Moonriver|`SubstrateCoins.MOONRIVER`|
|Phala Network|`SubstrateCoins.PHALA`|
|Plasm Network|`SubstrateCoins.PLASM`|
|Polkadot|`SubstrateCoins.POLKADOT`|
|Sora|`SubstrateCoins.SORA`|
|Stafi|`SubstrateCoins.STAFI`|

The code is structured so that it can be easily extended with other coins if needed.

### Construction from seed

The class can be constructed from a seed, like `Bip32`. The seed can be specified manually or generated by `SubstrateBip39SeedGenerator`.

**NOTE**: If `Bip39SeedGenerator` is used instead, the wrong addresses will be generated

**Code example**

    import binascii
    from bip_utils import SubstrateBip39SeedGenerator, SubstrateCoins, Substrate

    # Generate from mnemonic
    mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
    seed_bytes = SubstrateBip39SeedGenerator(mnemonic).Generate()
    # Specify seed manually. The seed is required to be 32-byte long. If longer, only the first 32-byte will be considered.
    seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc1")

    # Construction from seed
    substrate_ctx = Substrate.FromSeed(seed_bytes, SubstrateCoins.POLKADOT)
    # Construction from seed by specifying the path
    substrate_ctx = Substrate.FromSeedAndPath(seed_bytes, "//hard/soft", SubstrateCoins.POLKADOT)

### Construction from private/public key

The class can be constructed from a private or a public key.

**Code example**

    import binascii
    from bip_utils import SubstrateCoins, Substrate, Sr25519PublicKey, Sr25519PrivateKey

    # Construction from private key bytes
    priv_key_bytes = binascii.unhexlify(b"2ec306fc1c5bc2f0e3a2c7a6ec6014ca4a0823a7d7d42ad5e9d7f376a1c36c0d14a2ddb1ef1df4adba49f3a4d8c0f6205117907265f09a53ccf07a4e8616dfd8")
    substrate_ctx = Substrate.FromPrivateKey(priv_key_bytes, SubstrateCoins.POLKADOT)
    # Or key object directly
    substrate_ctx = Substrate.FromPrivateKey(Sr25519PrivateKey.FromBytes(priv_key_bytes), SubstrateCoins.POLKADOT)
    # Return false
    print(substrate_ctx.IsPublicOnly())

    # Construction from public key bytes
    # The object will be public-only and support only public derivation
    pub_key_bytes = binascii.unhexlify(b"66933bd1f37070ef87bd1198af3dacceb095237f803f3d32b173e6b425ed7972")
    substrate_ctx = Substrate.FromPublicKey(pub_key_bytes, SubstrateCoins.POLKADOT)
    # Or key object directly
    substrate_ctx = Substrate.FromPublicKey(Sr25519PublicKey.FromBytes(pub_key_bytes), SubstrateCoins.POLKADOT)
    # Return true
    print(substrate_ctx.IsPublicOnly())

### Keys derivation

Like [`Bip32`](https://github.com/ebellocchia/bip_utils/tree/master/readme/bip32.md), each time a key is derived a new instance of the Substrate class is returned.\
The usage is similar to `Bip32`/`Bip44` module.

**Code example**

    import binascii
    from bip_utils import SubstrateCoins, Substrate

    # Seed bytes
    seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc1")
    # Construction from seed
    substrate_ctx = Substrate.FromSeed(seed_bytes, SubstrateCoins.POLKADOT)
    # Print master keys and address
    print(substrate_ctx.PrivateKey().Raw().ToBytes())
    print(bytes(substrate_ctx.PrivateKey().Raw()))
    print(substrate_ctx.PrivateKey().Raw().ToHex())
    print(substrate_ctx.PublicKey().RawCompressed().ToBytes())
    print(bytes(substrate_ctx.PublicKey().RawCompressed()))
    print(substrate_ctx.PublicKey().RawCompressed().ToHex())
    print(substrate_ctx.PublicKey().ToAddress())

    # Derive a child key
    substrate_ctx = substrate_ctx.ChildKey("//hard")
    # Print derived keys and address
    print(substrate_ctx.PrivateKey().Raw().ToHex())
    print(substrate_ctx.PublicKey().RawCompressed().ToHex())
    print(substrate_ctx.PublicKey().ToAddress())
    # Print path
    print(substrate_ctx.Path().ToStr())

    # Derive a path
    substrate_ctx = substrate_ctx.DerivePath("//hard/soft") # Path: //hard/soft
    substrate_ctx = substrate_ctx.DerivePath("//0/1")       # Path: //hard/soft//0/1
    # Print derived keys and address
    print(substrate_ctx.PrivateKey().Raw().ToHex())
    print(substrate_ctx.PublicKey().RawCompressed().ToHex())
    print(substrate_ctx.PublicKey().ToAddress())
    # Print path
    print(substrate_ctx.Path().ToStr())

It's also possible to use public derivation (i.e. "watch-only" addresses) by:
- converting a private object to a public-only using `ConvertToPublic` method
- constructing a public-only object from a public key

In case of a public-only object, only public derivation will be supported (only "soft" path elements), otherwise a SubstrateKeyError exception will be raised.

**Code example**

    import binascii
    from bip_utils import SubstrateKeyError, SubstrateCoins, Substrate

    # Construction from public key
    pub_key_bytes = b"66933bd1f37070ef87bd1198af3dacceb095237f803f3d32b173e6b425ed7972"
    substrate_ctx = Substrate.FromPublicKey(binascii.unhexlify(pub_key_bytes), SubstrateCoins.POLKADOT)
    # Return true
    print(substrate_ctx.IsPublicOnly())
    # Print key and address
    print(substrate_ctx.PublicKey().RawCompressed().ToHex())
    print(substrate_ctx.PublicKey().ToAddress())

    # Public derivation is used to derive a child key
    substrate_ctx = substrate_ctx.ChildKey("/soft")
    # Print key and address
    print(substrate_ctx.PublicKey().RawCompressed().ToHex())
    print(substrate_ctx.PublicKey().ToAddress())
    # Print path
    print(substrate_ctx.Path().ToStr())
    # Public derivation is used to derive a path
    substrate_ctx = substrate_ctx.DerivePath("/0/1")

    # Getting the private key will raise a SubstrateKeyError
    try:
        print(substrate_ctx.PrivateKey().Raw().ToHex())
    except SubstrateKeyError as ex:
        print(ex)

    # Deriving a hard path will raise a SubstrateKeyError
    try:
        substrate_ctx = substrate_ctx.ChildKey("//hard")
        substrate_ctx = substrate_ctx.DerivePath("//0/1")
    except SubstrateKeyError as ex:
        print(ex)

    # Construction from private key
    priv_key_bytes = b"2ec306fc1c5bc2f0e3a2c7a6ec6014ca4a0823a7d7d42ad5e9d7f376a1c36c0d14a2ddb1ef1df4adba49f3a4d8c0f6205117907265f09a53ccf07a4e8616dfd8"
    substrate_ctx = Substrate.FromPrivateKey(binascii.unhexlify(priv_key_bytes), SubstrateCoins.POLKADOT)
    # Convert to public object
    substrate_ctx.ConvertToPublic()
    # Same as before...

### Parse path

The Substrate module allows also to parse derivation paths.\
Please note that, if a path contains only numbers (e.g. "//123"), it'll be considered as an integer and not as a string of ASCII characters.

**Code example**

    from bip_utils import SubstratePath, SubstratePathParser

    # Parse path, SubstratePathError is raised in case of errors
    path = SubstratePathParser.Parse("//hard/soft")
    # Or construct directly from a list of indexes
    path = SubstratePath(["//hard", "/soft"])

    # Get length
    print(path.Length())
    # Get as string
    print(path.ToStr())
    print(str(path))
    # Print elements info and value
    for elem in path:
        print(elem.IsHard())
        print(elem.IsSoft())
        print(elem.ToStr())
        print(str(elem))
        print(elem.ChainCode())
    # Get as list of strings
    path_list = path.ToList()
    for elem in path_list:
        print(elem)
