Giter VIP home page Giter VIP logo

block_keys's Introduction

BlockKeys

Build Status

BlockKeys is an Elixir implementation of BIP44 Multi-Account Hierarchy for Deterministic Wallets. Currently it supports Bitcoin, Ethereum and Solana, but will be extended to support a large number of coin types of coins in the future.

For low level details check the Wiki.

Installation

Add the dependency to your mix.exs:

def deps do
  [
    {:block_keys, "~> 1.0.2"}
  ]
end

What is this good for ?

The purpose of HD wallets is to increase anonymity by generating different addresses each time you transact. For Bitcoin this means generating new addresses when you receive funds but also generating unique change addresses (this would prevent someone from knowing how much bitcoin you sent in a transactions because the to address and change address will both not be tied to your sending address). This is pseudo-anonymity because there are other ways of clustering transactions in order to de-anonymize your transactions.

The second use case for this library is to allow online stores or exchanges to receive crypto currency payments without using a hot wallet. You only need a Master Public Key deployed on your server in order to generate addresses. In case of a security breach the only danger is the attacker now has the ability to check the balances for all your accounts but not steal any coins (really just a privacy issue here). Using the Master Public Key you can also setup a watch-only wallet in order to reconcile the payments you receive.

How to use this

Mnemonic Lenghts

This library supports a wide range of mnemonic sizes: from 3 words to 24 words. Please note that anything less than 12 word mnemonics are not recommended for generating blockchain wallets.

Import Ledger Nano mnemonic

Disclaimer: The mnemonic phrase for you hardware wallet is the key to all your crypto currency stored on that device. Before you go ahead and input that in this library make sure you audit the code to make sure everything looks legit.

root_key = BlockKeys.from_mnemonic("nurse grid sister metal flock choice system control about mountain sister rapid hundred render shed chicken print cover tape sister zero bronze tattoo stairs")
"xprv9s21ZrQH143K35qGjQ6GG1wGHFZP7uCZA1WBdUJA8vBZqESQXQGA4A9d4eve5JqWB5m8YTMcNe8cc7c3FVzDGNcmiabi9WQycbFeEvvJF2D"

Ethereum Addresses

Now that you have the root private key you can start deriving your account extended keys along with ethereum addresses. Ledger will use the same receive address unless you recycle it.

BlockKeys.CKD.derive(root_key, "M/44'/60'/0'/0/0") |> BlockKeys.Ethereum.Address.from_xpub
"0x73bb50c828fd325c011d740fde78d02528826156"

Note that you can generate a master public key by deriving the Account path:

master_public_key = BlockKeys.CKD.derive(root_key, "M/44'/60'/0'")
"xpub6C821eJHTSrPMS1sGZ4o5QDDAfWyViQek3fVwUA53FkgndTwjxL7PS1pFP9EdqKpejTZeaQkmxoergebKCpVpPuTdE67Kzn2jZn9AL9TzxD"

You can now use this key to generate addresses on a live server that will be in sync with your Ledger

BlockKeys.Derivation.CKD.derive(master_public_key, "M/0/0") |> BlockKeys.Ethereum.Address.from_xpub
"0x73bb50c828fd325c011d740fde78d02528826156"

We have to use a non hardened path here because we're feeding a public key (hardened paths require a private key for concatenation in the child key derivation function). Note how the address at M/0/0 is the same as our initial M/44'/60'/0'/0/0.

Create a master node

Generate the mnemonic phrase and the master private key

%{mnemonic: mnemonic, root_key: root_key} = BlockKeys.generate()

Generate Master Public Key

Generate a master public key that you can use to generate addresses

Bitcoin

path = "M/44'/0'/0'"
xpub = BlockKeys.CKD.derive(root_key, path)

Ethereum

path = "M/60'/0'/0'"
xpub = BlockKeys.CKD.derive(root_key, path)

Solana

path = "M/501'/0'/0'"
xpub = BlockKeys.CKD.derive(root_key, path, curve: :ed25519)

Generating addresses from Master Public Key

Generally you would export the master public key and keep it on your live server so that you can generate addresses for payments or deposits.

This is just an example of how you would generate some sample addresses

Bitcoin

path = "M/0/0"
address = BlockKeys.Bitcoin.address(xpub, path)

Ethereum

path = "M/0/0"
address = BlockKeys.Ethereum.address(xpub, path)

Solana

path = "M/0/0"
address = BlockKeys.Solana.address(xpub, path)

Path and derivations

You will notice that we used different paths for generating the master private key vs addresses. This is because our initial derivation path includes some hardened paths in order to prevent any downstream generation of addresses for specific paths. For example, the coin code path is hardened in order to prevent anyone from generating a tree of addresses for a different coin given our master public key.

Essentially out master public key will only be able to generate trees of addresses from un-hardened paths.

Our address path

M/0/0

Is equivalent to

M/60'/0'/0'/0/0

Contributing

  1. Fork it!
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

ExKeccak is released under the Apache-2.0 License.

block_keys's People

Contributors

dingpl716 avatar fpesti avatar lishawnl avatar oltarasenko avatar tzumby avatar vikger avatar viktmv avatar vtymofeev-tc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

block_keys's Issues

Unclear how to import the generated addresses on wallet

Hey!
I've been using this library and its great, we are testing it with testnet bitcoin, I cant seem to import the master private key on a wallet and match the generated addresses from the library, so I can’t spent.
I've tried P2PKH and derivation paths m/44'/1'/0', m/44'/0'/0', m/0'/0' and m.
Thanks in advance!

Not working on Elixir 1.10.3 / OTP 23

I cloned this repository, then ran mix deps.get and iex -S mix:

Erlang/OTP 23 [erts-11.0.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1]

Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> root_key = BlockKeys.from_mnemonic("nurse grid sister metal flock choice system control about mountain sister rapid hundred render shed chicken print cover tape sister zero bronze tattoo stairs")
"xprv9s21ZrQH143K35qGjQ6GG1wGHFZP7uCZA1WBdUJA8vBZqESQXQGA4A9d4eve5JqWB5m8YTMcNe8cc7c3FVzDGNcmiabi9WQycbFeEvvJF2D"
iex(2)> BlockKeys.CKD.derive(root_key, "M/44'/60'/0'/0/0") |> BlockKeys.Ethereum.Address.from_xpub

21:58:12.955 [warn]  The on_load function for module libsecp256k1 returned:
{:error, {:load_failed, 'Failed to load NIF library: \'/home/felipe/local-repositories/block_keys/_build/dev/lib/libsecp256k1/priv/libsecp256k1_nif.so: cannot open shared object file: No such file or directory\''}}

** (UndefinedFunctionError) function :libsecp256k1.ec_pubkey_create/2 is undefined (module :libsecp256k1 is not available)
    (libsecp256k1 0.1.10) :libsecp256k1.ec_pubkey_create(<<52, 229, 119, 241, 177, 34, 46, 53, 218, 61, 119, 101, 81, 208, 34, 9, 178, 233, 180, 154, 141, 245, 203, 159, 179, 170, 124, 150, 195, 100, 32, 80>>, :uncompressed)
    (block_keys 0.1.10) lib/block_keys/crypto.ex:26: BlockKeys.Crypto.public_key/1
    (block_keys 0.1.10) lib/block_keys/ckd.ex:202: BlockKeys.CKD.put_uncompressed_parent_pub/2
    (block_keys 0.1.10) lib/block_keys/ckd.ex:82: BlockKeys.CKD.child_key_private/2
    (block_keys 0.1.10) lib/block_keys/ckd.ex:55: BlockKeys.CKD._derive/2
    (block_keys 0.1.10) lib/block_keys/ckd.ex:28: BlockKeys.CKD.derive/2
iex(2)> 

Can't compile on OTP23

Looks like we've got the next problem. One of the dependencies (keccakf1600) can't be compiled on otp23, as it has a major change in erl_interface. (http://blog.erlang.org/OTP-23-Highlights/)

On the high level it looks like this:

$ mix compile
===> Compiling keccakf1600
 LD     keccakf1600.so
ld: library not found for -lerl_interface
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [/Users/name/proj/deps/keccakf1600/c_src/../priv/keccakf1600.so] Error 1
===> Hook for compile failed!
** (Mix) Could not compile dependency :keccakf1600, "/Users/name/.asdf/installs/elixir/1.10.3-otp-23/.mix/rebar3 bare compile --paths="/Users/name/proj/_build/dev/lib/*/ebin"" command failed. You can recompile this dependency with "mix deps.compile keccakf1600", update it with "mix deps.update keccakf1600" or clean it with "mix deps.clean keccakf1600"

The quick investigation shows that the problematic line is: https://github.com/hayesgm/erlang-keccakf1600/blob/master/c_src/Makefile#L92 and the following change fixes it:
LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -le

However, I have two questions:

  1. Is there a way for us to get rid of this keccakf1600 as it seems to be unrelated to generating mnemonic based keys at all and is used only in some bitcoin specific thing we don't need atm?
  2. If the option 1 is not possible is what is the best way to patch the keccakf1600 so we can get it mergeble to BlockKeys?

Remove dependency on libsecp256k1 library

Currently we rely on libsecp256k1 for the tweak add and decompress operations:

  • :libsecp256k1.ec_pubkey_tweak_add/2
  • :libsecp256k1.ec_pubkey_decompress/1

It would help to eliminate that dependency and implement those operations in pure Elixir. Although this would be a performance hit, it would help when trying to use block_keys in an embedded environment like Nerves.

One good compromise would be to have the ecc library configurable.

Can't compile on alpine

Hey,

We have started making a docker version of the code and got the following:

===> Compiling keccakf1600
make: Entering directory '/app/deps/keccakf1600/c_src'
 C      decaf-utils.c
In file included from /app/deps/keccakf1600/c_src/decaf-common.h:42,
                 from /app/deps/keccakf1600/c_src/decaf-utils.c:38:
/usr/lib/gcc/x86_64-alpine-linux-musl/9.3.0/include/stdint.h:9:26: error: no include path in which to search for stdint.h
    9 | # include_next <stdint.h>
      |                          ^
In file included from /app/deps/keccakf1600/c_src/decaf-utils.c:38:
/app/deps/keccakf1600/c_src/decaf-common.h:43:10: fatal error: sys/types.h: No such file or directory
   43 | #include <sys/types.h>
      |          ^~~~~~~~~~~~~
compilation terminated.
make: *** [Makefile:146: /app/deps/keccakf1600/c_src/decaf-utils.o] Error 1
make: Leaving directory '/app/deps/keccakf1600/c_src'
===> Hook for compile failed!```

Alpine linux problems with libsecp256k1

Hey,

Noticed another problem with the docker build. It turns out I can't generate the keys :(.

I am getting the following error when starting a console from the docker container:

09:37:06.739 [warn]  The on_load function for module libsecp256k1 returned:
{:error, {:load_failed, 'Failed to load NIF library: \'Error loading shared library /app/_build/prod/lib/libsecp256k1/priv/libsecp256k1_nif.so: No such file or directory\''}}

Also, the following error appeared during the compilation:

make[1]: Leaving directory '/app/deps/libsecp256k1/c_src/secp256k1'
cc -I/usr/local/lib/erlang/erts-10.7.1/include -I c_src/secp256k1 -I c_src/secp256k1/src -I c_src/secp256k1/include -I../libsecp256k1/src -fPIC -shared -o priv/libsecp256k1_nif.so c_src/libsecp256k1_nif.c c_src/secp256k1/.libs/libsecp256k1.a -lgmp
c_src/libsecp256k1_nif.c: In function 'ecdsa_sign':
c_src/libsecp256k1_nif.c:494:51: warning: passing argument 2 of 'secp256k1_ecdsa_signature_serialize_der' from incompatible pointer type [-Wincompatible-pointer-types]
  494 |  if (secp256k1_ecdsa_signature_serialize_der(ctx, &intermediatesig, &siglen, &signature) != 1) {
      |                                                   ^~~~~~~~~~~~~~~~
      |                                                   |
      |                                                   unsigned char (*)[74]
In file included from c_src/libsecp256k1_nif.c:9:
c_src/secp256k1/src/secp256k1.c:261:90: note: expected 'unsigned char *' but argument is of type 'unsigned char (*)[74]'
  261 | int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) {
      |                                                                           ~~~~~~~~~~~~~~~^~~~~~
In file included from c_src/secp256k1/src/secp256k1.c:9,
                 from c_src/libsecp256k1_nif.c:9:
c_src/libsecp256k1_nif.c:497:64: warning: passing argument 3 of 'secp256k1_ecdsa_signature_parse_der' from incompatible pointer type [-Wincompatible-pointer-types]
  497 |     CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature, &intermediatesig, siglen) == 1);
      |                                                                ^~~~~~~~~~~~~~~~
      |                                                                |
      |                                                                unsigned char (*)[74]
c_src/secp256k1/src/util.h:40:39: note: in definition of macro 'EXPECT'
   40 | #define EXPECT(x,c) __builtin_expect((x),(c))
      |                                       ^
c_src/libsecp256k1_nif.c:497:5: note: in expansion of macro 'CHECK'
  497 |     CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature, &intermediatesig, siglen) == 1);
      |     ^~~~~
In file included from c_src/libsecp256k1_nif.c:9:
c_src/secp256k1/src/secp256k1.c:224:124: note: expected 'const unsigned char *' but argument is of type 'unsigned char (*)[74]'
  224 | int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
      |                                                                                                       ~~~~~~~~~~~~~~~~~~~~~^~~~~
/usr/lib/gcc/x86_64-alpine-linux-musl/9.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lgmp
collect2: error: ld returned 1 exit status
make: *** [Makefile:31: priv/libsecp256k1_nif.so] Error 1

How to validate phrases?

One of the tasks I need to build is being able to generate keys from the phrases sent by clients from outside.

What I have noticed that my phrases generated by BlockKeys.Mnemonic.generate_phrase the function would allow me to generate seeds used for keys generation.

However I am trying to understand how to validate phrases from external systems, so they can be used by us. For example:

iex(27)> phrase = BlockKeys.Mnemonic.generate_phrase
"shuffle clay veteran vicious achieve regular mammal regular pluck income crack client oxygen unaware plate client expire eager glimpse limit wonder dentist access naive"
iex(28)> BlockKeys.Mnemonic.generate_seed(phrase)
"a1425bc1452ae8a9ef9a1743daa8164657bf60003170fc32faa9b1cf34f90c52e44c6bb3da66b38b2af333d71219b1eab41c147c0b2b272f5612949e9affadb7"

Works

However when I have changed something in the phrase (e.g. I am assuming that the user have sent us something different)

# See I have replaced the first word to clay here...
iex(30)> phrase = "clay clay veteran vicious achieve regular mammal regular pluck income crack client oxygen unaware plate client expire eager glimpse limit wonder dentist access naive"
"clay clay veteran vicious achieve regular mammal regular pluck income crack client oxygen unaware plate client expire eager glimpse limit wonder dentist access naive"
iex(31)> BlockKeys.Mnemonic.generate_seed(phrase)                                                     
{:error, "Checksum is not valid"}

# See I have removed two first words here
iex(32)> phrase = "veteran vicious achieve regular mammal regular pluck income crack client oxygen unaware plate client expire eager glimpse limit wonder dentist access naive"
"veteran vicious achieve regular mammal regular pluck income crack client oxygen unaware plate client expire eager glimpse limit wonder dentist access naive"
iex(33)> BlockKeys.Mnemonic.generate_seed(phrase)                                                      
** (FunctionClauseError) no function clause matching in BlockKeys.Mnemonic.verify_checksum/1

    The following arguments were given to BlockKeys.Mnemonic.verify_checksum/1:

        # 1
        <<243, 62, 112, 7, 90, 104, 107, 105, 169, 171, 148, 49, 197, 90, 121, 118, 58,
          98, 85, 148, 26, 39, 99, 48, 63, 244, 29, 64, 21, 37>>

    Attempted function clauses (showing 1 out of 1):

        defp verify_checksum(<<entropy::binary()-size(32), checksum::binary()-size(1)>>)

    (block_keys 0.1.5) lib/block_keys/mnemonic.ex:151: BlockKeys.Mnemonic.verify_checksum/1
    (block_keys 0.1.5) lib/block_keys/mnemonic.ex:66: BlockKeys.Mnemonic.generate_seed/2
iex(33)>

Could you explain how do I understand which phrase is correct and which is not?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.