├── src ├── bip44.h ├── store.h ├── command.h ├── bip85.h ├── command.c ├── bip39.h ├── bip44.c ├── meson.build ├── bip32.h ├── utils.h ├── bip39.c ├── bip85.c ├── utils.c ├── btct.c ├── bip44_command.c ├── store.c ├── bip39_command.c ├── sss_command.c ├── bip85_command.c ├── store_command.c ├── bip32.c ├── bip32_command.c ├── bip39_english.h └── bip44_coins.h ├── .gitmodules ├── meson.build ├── external └── meson.build └── test ├── meson.build ├── bip39_spec.c ├── utils_spec.c ├── test_vectors.h ├── bip85_spec.c ├── bip32_spec.c └── bdd-for-c.h /src/bip44.h: -------------------------------------------------------------------------------- 1 | #include "bip44_coins.h" 2 | #include "bip32.h" 3 | 4 | bip44_coin_t *bip44_coin_by_symbol(const char *symbol); 5 | 6 | int bip44_create_account(const bip32_key_t *masterkey, const bip44_coin_t *coin, uint32_t account, bip32_key_t *accountkey); 7 | -------------------------------------------------------------------------------- /src/store.h: -------------------------------------------------------------------------------- 1 | #ifndef __store_h__ 2 | #define __store_h__ 3 | 4 | int store_write_mnemonics(const char *filename, const char *password, const uint8_t *mnemonics); 5 | int store_read_mnemonics(const char *filename, const char *password, 6 | uint8_t *data, size_t *size); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/libbase58"] 2 | path = external/libbase58 3 | url = git@github.com:bitcoin/libbase58.git 4 | [submodule "external/sss"] 5 | path = external/sss 6 | url = git@github.com:dsprenkels/sss.git 7 | [submodule "external/secp256k1"] 8 | path = external/secp256k1 9 | url = git@github.com:bitcoin-core/secp256k1.git 10 | -------------------------------------------------------------------------------- /src/command.h: -------------------------------------------------------------------------------- 1 | #ifndef __command_h 2 | #define __command_h 3 | 4 | #include 5 | #include 6 | 7 | typedef struct command_t { 8 | const char *name; 9 | int (*command)(int argc, char **argv); 10 | } command_t; 11 | 12 | int command_dispatch(command_t *commands, const char *command, bool partial_match, int argc, char **argv); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('bitcoin', 'c', 2 | version : '0.1', 3 | default_options : ['warning_level=3']) 4 | 5 | endianess_template = '@0@_ENDIAN' 6 | endian = endianess_template.format(target_machine.endian()).to_upper() 7 | add_project_arguments('-D' + endian, language: 'c') 8 | 9 | version_template = 'v@0@' 10 | version = version_template.format(meson.project_version()) 11 | add_project_arguments('-DVERSION=' + version , language: 'c') 12 | 13 | subdir('external') 14 | subdir('src') 15 | subdir('test') 16 | -------------------------------------------------------------------------------- /src/bip85.h: -------------------------------------------------------------------------------- 1 | #ifndef __bip85_h__ 2 | #define __bip85_h__ 3 | 4 | #include 5 | #include "bip32.h" 6 | 7 | int bip85_entropy_from_key(const bip32_key_t *key, const char *subpath, uint8_t *entropy); 8 | int bip85_application_bip39(const bip32_key_t *key, uint32_t language, uint32_t word_cnt, uint32_t index, 9 | char ***result, size_t *result_cnt); 10 | int bip85_application_pwd_base85(const bip32_key_t *key, uint32_t length, uint32_t index, char *result); 11 | int bip85_application_hd_seed_wif(const bip32_key_t *key, uint32_t index, char *result, size_t *size); 12 | #endif 13 | -------------------------------------------------------------------------------- /src/command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "command.h" 3 | #include 4 | int 5 | command_dispatch(command_t *commands, const char *command, bool partial_match, int argc, char **argv) 6 | { 7 | command_t *cmd = commands; 8 | while (cmd && cmd->name != NULL) 9 | { 10 | if ((!partial_match && strcmp(command, cmd->name) !=0) 11 | || (partial_match && strncmp(command, cmd->name, strlen(cmd->name)) != 0)) 12 | { 13 | cmd++; 14 | continue; 15 | } 16 | return cmd->command(argc, argv); 17 | } 18 | 19 | return -1; 20 | } 21 | -------------------------------------------------------------------------------- /src/bip39.h: -------------------------------------------------------------------------------- 1 | #ifndef __bip39_h 2 | #define __bip39_h 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | typedef struct bip39_t { 11 | struct sha256_ctx sha256; 12 | } bip39_t; 13 | 14 | 15 | int bip39_init(bip39_t *ctx); 16 | 17 | int bip39_to_mnemonics(bip39_t *ctx, uint8_t *seed, size_t bits, 18 | char ***mnemonics, size_t *count); 19 | 20 | int bip39_to_seed(bip39_t *ctx, const uint8_t *menomics, size_t mnemonice_size, 21 | int iterations,const uint8_t *passphrase, 22 | uint8_t *seed); 23 | #endif 24 | -------------------------------------------------------------------------------- /external/meson.build: -------------------------------------------------------------------------------- 1 | 2 | libbase58_static = static_library('libbase58', 'libbase58/base58.c') 3 | 4 | sss_incdir = include_directories(['sss/']) 5 | sss_static = static_library('sss', ['sss/sss.c', 'sss/randombytes.c', 'sss/tweetnacl.c', 'sss/hazmat.c'], 6 | include_directories: sss_incdir) 7 | 8 | secp256k1_incdir = include_directories(['secp256k1/include']) 9 | secp256k1_static = static_library('secp256k1', [ 10 | 'secp256k1/src/secp256k1.c', 11 | 'secp256k1/src/precomputed_ecmult.c', 12 | 'secp256k1/src/precomputed_ecmult_gen.c', 13 | ], 14 | c_args: ['-DSECP256K1_BUILD', '-DSECP256K1_STATIC'], 15 | include_directories: secp256k1_incdir) 16 | -------------------------------------------------------------------------------- /test/meson.build: -------------------------------------------------------------------------------- 1 | ncurses = dependency('ncurses', version: '>=6.0', 2 | method: 'pkg-config', 3 | required: true) 4 | utils_spec = executable('utils_spec', 'utils_spec.c', dependencies: [ ncurses, nettle ], link_with: [libbtct_static]) 5 | bip32_spec = executable('bip32_spec', 'bip32_spec.c', dependencies: [ ncurses, nettle ], link_with: [libbtct_static]) 6 | bip39_spec = executable('bip39_spec', 'bip39_spec.c', dependencies: [ ncurses, nettle ], link_with: [libbtct_static]) 7 | bip85_spec = executable('bip85_spec', 'bip85_spec.c', dependencies: [ ncurses, nettle ], link_with: [libbtct_static]) 8 | 9 | test('utils_spec', utils_spec) 10 | test('bip32_spec', bip32_spec) 11 | test('bip39_spec', bip39_spec) 12 | test('bip85_spec', bip85_spec) 13 | -------------------------------------------------------------------------------- /src/bip44.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bip44.h" 5 | 6 | bip44_coin_t * 7 | bip44_coin_by_symbol(const char *symbol) { 8 | char *psym, *pbuf, buf[64]={0}; 9 | pbuf = buf; 10 | psym = (char*)symbol; 11 | 12 | while (*psym != '\0') { 13 | *pbuf = toupper(*psym); 14 | psym++; 15 | pbuf++; 16 | } 17 | 18 | if (strlen(buf) == 0) 19 | return NULL; 20 | 21 | for (size_t i=0; i < sizeof(bip44_coins) / sizeof(bip44_coin_t); i++) { 22 | if (strcmp(bip44_coins[i].symbol, buf) == 0) 23 | return &bip44_coins[i]; 24 | } 25 | 26 | return NULL; 27 | } 28 | 29 | int 30 | bip44_create_account(const bip32_key_t *masterkey, const bip44_coin_t *coin, uint32_t account, bip32_key_t *accountkey) 31 | { 32 | char path[1024]; 33 | snprintf(path, sizeof(path), "m/44'/%d'/%d'", coin->type, account); 34 | if (bip32_key_derive_child_by_path(masterkey, path, accountkey) != 0) 35 | return -1; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | nettle = dependency('nettle', version: '>=3.4', 2 | method: 'pkg-config', 3 | required: true) 4 | 5 | gmp = dependency('gmp', version: '>=6.2.0', 6 | method: 'pkg-config', 7 | required: true) 8 | 9 | library_sources = [ 10 | 'utils.c', 11 | 'bip32.c', 12 | 'bip39.c', 13 | 'bip44.c', 14 | 'bip85.c', 15 | ] 16 | 17 | libbtct_static = static_library('btct', library_sources, 18 | dependencies: [ gmp ], 19 | link_with: [libbase58_static, secp256k1_static], 20 | include_directories: [sss_incdir]) 21 | 22 | clitool_sources = [ 23 | 'command.c', 24 | 'store.c', 25 | 'store_command.c', 26 | 'bip32_command.c', 27 | 'bip39_command.c', 28 | 'bip44_command.c', 29 | 'bip85_command.c', 30 | 'sss_command.c', 31 | 'btct.c' 32 | ] 33 | executable('btct', clitool_sources, 34 | dependencies: [ gmp, nettle ], 35 | link_with: [libbtct_static, sss_static], 36 | include_directories: [sss_incdir], 37 | install: true) 38 | -------------------------------------------------------------------------------- /src/bip32.h: -------------------------------------------------------------------------------- 1 | #ifndef __bip32_h 2 | #define __bip32_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct bip32_key_t { 10 | bool public; 11 | 12 | union { 13 | uint8_t private[32]; 14 | uint8_t public[64]; 15 | } key; 16 | 17 | uint8_t chain[32]; 18 | uint8_t parent_fingerprint[4]; 19 | uint8_t depth; 20 | uint32_t index; 21 | } bip32_key_t; 22 | 23 | int bip32_key_init_private(bip32_key_t *ctx); 24 | int bip32_key_init_from_entropy(bip32_key_t *bip32_key_ctx, uint8_t *entropy, size_t size); 25 | int bip32_key_init_public_from_private_key(bip32_key_t *ctx, const bip32_key_t *private); 26 | int bip32_key_p2pkh_address_from_key(const bip32_key_t *ctx, uint8_t *address, size_t *size); 27 | int bip32_key_derive_child_key(const bip32_key_t *parent, uint32_t index, bip32_key_t *child); 28 | int bip32_key_derive_child_by_path(const bip32_key_t *ctx, const char *path, bip32_key_t *child); 29 | int bip32_key_secp256k1_serialize_public_key(const bip32_key_t *ctx, bool compressed, uint8_t *result); 30 | int bip32_key_serialize(bip32_key_t *ctx, bool encoded, 31 | uint8_t *result, size_t *size); 32 | int bip32_key_deserialize(bip32_key_t *ctx, const char *encoded_key); 33 | int bip32_key_to_wif(bip32_key_t *ctx, uint8_t *result, size_t *size); 34 | 35 | typedef uint8_t bip32_key_identifier_t[RIPEMD160_DIGEST_SIZE]; 36 | int bip32_key_identifier_init_from_key(bip32_key_identifier_t ident, const bip32_key_t *key); 37 | int bip32_key_identifier_fingerprint(const bip32_key_identifier_t ident, uint8_t *fingerprint); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __utils_h 2 | #define __utils_h 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef LITTLE_ENDIAN 8 | #define utils_out_u16_be(p, v) \ 9 | { \ 10 | *(p + 1) = (v) & 0xff; \ 11 | *(p + 0) = ((v) >> 8) & 0xff; \ 12 | } 13 | #define utils_out_u32_be(p, v) \ 14 | { \ 15 | utils_out_u16_be(p + 2, (v) & 0xffff); \ 16 | utils_out_u16_be(p + 0, ((v) >> 16) & 0xffff); \ 17 | } 18 | #define utils_in_u16_be(p) ((uint16_t) *(p + 0) << 8 | *(p + 1)) 19 | #define utils_in_u32_be(p) ((uint32_t) utils_in_u16_be(p + 0) << 16 | utils_in_u16_be(p + 2)) 20 | 21 | #else /* big endian */ 22 | #define utils_out_u16_be(p, v) \ 23 | { \ 24 | *(p + 0) = (v) & 0xff; \ 25 | *(p + 1) = ((v) >> 8) & 0xff; \ 26 | } 27 | #define utils_out_u32_be(p, v) \ 28 | { \ 29 | utils_out_u16_be(p + 0, (v) & 0xffff); \ 30 | utils_out_u16_be(p + 2, ((v) >> 16) & 0xffff); \ 31 | } 32 | #define utils_in_u16_be(p) ((uint16_t) *(p + 0) << 8 | *(p + 1)) 33 | #define utils_in_u32_be(p) ((uint32_t) utils_in_u16_be(p) << 16 | utils_in_u16_be(p + 2)) 34 | #endif 35 | 36 | void utils_hexdump(uint8_t *data, size_t size, FILE *out); 37 | 38 | int utils_fill_random(uint8_t *out, size_t size); 39 | 40 | int utils_base85_encode(const uint8_t *data, size_t size, char *result); 41 | int utils_to_hex_string(const uint8_t *data, size_t size, char *result); 42 | 43 | /** SHA256(SHA256(x))[0:3] */ 44 | int utils_sha256_sha256_checksum(const uint8_t *data, size_t size, uint8_t *checksum); 45 | /** RIPEMD160(SHA256(x)) */ 46 | int utils_hash160(const uint8_t *data, size_t size, uint8_t *out); 47 | #endif 48 | -------------------------------------------------------------------------------- /src/bip39.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bip39.h" 5 | #include "bip39_english.h" 6 | 7 | int bip39_init(bip39_t *ctx) 8 | { 9 | memset(ctx, 0, sizeof(bip39_t)); 10 | return 0; 11 | } 12 | 13 | static inline uint16_t 14 | _read_11bit_value_at_bit_index(uint8_t *seed, size_t bit_index) { 15 | uint32_t value = 0; 16 | uint8_t byte_index = bit_index / 8; 17 | uint16_t bit_offset = bit_index - (byte_index * 8); 18 | uint8_t *pseed = seed + byte_index; 19 | value = pseed[0] << 24 | pseed[1] << 16 | pseed[2] << 8 | pseed[1]; 20 | value = value >> (32 - bit_offset - 11); 21 | value = value & 0x7ff; 22 | return value; 23 | } 24 | 25 | int bip39_to_mnemonics(bip39_t *ctx, uint8_t *entropy, size_t bits, 26 | char ***mnemonics, size_t *mnemonic_count) 27 | { 28 | uint8_t bytes = bits / 8; 29 | uint8_t checksum_size = bits / 32; 30 | 31 | if (!(128 <= bits && bits <= 256)) 32 | return 1; 33 | 34 | uint8_t digest[4] = {0}; 35 | sha256_init(&ctx->sha256); 36 | sha256_update(&ctx->sha256, bytes, entropy); 37 | sha256_digest(&ctx->sha256, 4, digest); 38 | 39 | uint8_t *seed=malloc(bytes+2); 40 | memcpy(seed, entropy, bytes); 41 | seed[bytes] = digest[0]; 42 | seed[bytes + 1] = digest[1]; 43 | 44 | *mnemonic_count = (bits + checksum_size) / 11; 45 | *mnemonics = malloc(sizeof(char *) * *mnemonic_count); 46 | uint16_t bit_index = 0; 47 | for (size_t word = 0; word < *mnemonic_count; word++) { 48 | (*mnemonics)[word] = (char *)bip39_english[_read_11bit_value_at_bit_index(seed, bit_index)]; 49 | bit_index += 11; 50 | } 51 | 52 | free(seed); 53 | return 0; 54 | } 55 | 56 | int bip39_to_seed(bip39_t *ctx, const uint8_t *mnemonic, size_t mnemonic_size, 57 | int iterations, const uint8_t *passphrase, 58 | uint8_t *seed) 59 | { 60 | uint8_t salt[4096] = "mnemonic"; 61 | 62 | if (passphrase != NULL) 63 | strncat((char*)salt, (char*)passphrase, strlen((char*)passphrase)); 64 | 65 | pbkdf2_hmac_sha512(mnemonic_size, mnemonic, iterations, strlen((char*)salt), salt, 64, seed); 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /test/bip39_spec.c: -------------------------------------------------------------------------------- 1 | #include "./bdd-for-c.h" 2 | #include "./test_vectors.h" 3 | #include "../src/bip39.h" 4 | 5 | #define check_str(got, expected) check(strcmp(got, expected) == 0, "expected string '%s' got '%s'", expected, got) 6 | #define check_number(got, expected) check(got == expected, "expected '%d' got '%d'", expected, got) 7 | 8 | spec("bip39") { 9 | static bip39_t ctx; 10 | 11 | context("given generating mnemonic seed phrase") { 12 | static char **words; 13 | static size_t word_cnt; 14 | before_each() { 15 | bip39_init(&ctx); 16 | } 17 | 18 | describe("when using 32bit entropy") { 19 | static uint8_t seed[] = {0,0,0,0}; 20 | it("then it should fail") 21 | check(bip39_to_mnemonics(&ctx, seed, sizeof(seed)*8, &words, &word_cnt) != 0); 22 | } 23 | 24 | 25 | describe("when using a known 128bit entropy") { 26 | it("then it should succeed") 27 | check(bip39_to_mnemonics(&ctx, vectors[0].entropy, 16*8, &words, &word_cnt) == 0); 28 | it("then it should generate correct 12 mnemonics") 29 | check_number(word_cnt, 12); 30 | it("then it should generate correct mnemonic phrase") { 31 | for (size_t i=0; i < word_cnt; i++) { 32 | check_str(words[i], vectors[0].mnemonics[i]); 33 | } 34 | } 35 | } 36 | 37 | describe("when using a known 192bit entropy") { 38 | it("then it should succeed") 39 | check(bip39_to_mnemonics(&ctx, vectors[1].entropy, 24*8, &words, &word_cnt) == 0); 40 | it("then it should generate correct 18 mnemonics") 41 | check_number(word_cnt, 18); 42 | it("then it should generate correct mnemonic phrase") { 43 | for (size_t i=0; i < word_cnt; i++) { 44 | check_str(words[i], vectors[1].mnemonics[i]); 45 | } 46 | } 47 | } 48 | 49 | describe("when using a known 256bit entropy") { 50 | it("then it should succeed") 51 | check(bip39_to_mnemonics(&ctx, vectors[2].entropy, 32*8, &words, &word_cnt) == 0); 52 | it("then it should generate correct 24 mnemonics") 53 | check_number(word_cnt, 24); 54 | it("then it should generate correct mnemonic phrase") { 55 | for (size_t i=0; i < word_cnt; i++) { 56 | check_str(words[i], vectors[2].mnemonics[i]); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/utils_spec.c: -------------------------------------------------------------------------------- 1 | #include "./bdd-for-c.h" 2 | #include "../src/utils.h" 3 | 4 | #define check_str(got, expected) check(strcmp(got, expected) == 0, "expected string '%s' got '%s'", expected, got) 5 | #define check_number(got, expected) check(got == expected, "expected '%u' got '%u'", expected, got) 6 | #define check_number_hex(got, expected) check(got == expected, "expected '0x%x' got '0x%x'", expected, got) 7 | 8 | spec("bip32") { 9 | uint16_t u16 = 0xff01; 10 | uint32_t u32 = 0xfffe0102; 11 | 12 | context("in/out of big endian values") { 13 | 14 | describe("when writing u16 big endian") { 15 | uint8_t buf[16]; 16 | uint8_t expected[] = { 0xff, 0x01 }; 17 | utils_out_u16_be(buf, u16); 18 | it("should write expected value") 19 | check(memcmp(buf, expected, 2) == 0); 20 | } 21 | 22 | describe("when reading u16 big endian") { 23 | uint8_t buf[] = { 0xff, 0x01 }; 24 | uint16_t value = utils_in_u16_be(buf); 25 | it("should write expected value") 26 | check_number_hex(value, u16); 27 | } 28 | 29 | describe("when writing u32 big endian") { 30 | uint8_t buf[16]; 31 | uint8_t expected[] = { 0xff, 0xfe, 0x01, 0x02 }; 32 | utils_out_u32_be(buf, u32); 33 | it("should write expected value") 34 | check(memcmp(buf, expected, 2) == 0); 35 | } 36 | 37 | describe("when reading u32 big endian") { 38 | uint8_t buf[] = { 0xff, 0xfe, 0x01, 0x02 }; 39 | uint32_t value = utils_in_u32_be(buf); 40 | it("should write expected value") 41 | check_number_hex(value, u32); 42 | } 43 | } 44 | 45 | context("base85 encoding") { 46 | describe("when encoding 0x000000") { 47 | static uint8_t data[] = {0x00, 0x00, 0x00, 0x00}; 48 | static char result[512] = {0}; 49 | before() { 50 | utils_base85_encode(data, 4, result); 51 | } 52 | it("should return 'z'") 53 | check_str(result, "z"); 54 | } 55 | 56 | describe("when encoding 'helloyou'") { 57 | static uint8_t data[] = {'h', 'e', 'l', 'l', 'o', 'y', 'o', 'u'}; 58 | static char result[512] = {0}; 59 | before() { 60 | utils_base85_encode(data, 8, result); 61 | } 62 | it("should return 'Xk~0{Z+UNZ'") 63 | check_str(result, "Xk~0{Z+UNZ"); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/bip85.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../external/libbase58/libbase58.h" 5 | 6 | #include "bip39.h" 7 | #include "bip85.h" 8 | 9 | int 10 | bip85_entropy_from_key(const bip32_key_t *master_key, const char *subpath, uint8_t *entropy) 11 | { 12 | bip32_key_t child; 13 | char bip85_path[256]; 14 | struct hmac_sha512_ctx hmac_sha512; 15 | const char *key = "bip-entropy-from-k"; 16 | 17 | if (master_key->public == true) 18 | return -1; 19 | 20 | snprintf(bip85_path, sizeof(bip85_path), "m/83696968'/%s", subpath); 21 | if (bip32_key_derive_child_by_path(master_key, bip85_path, &child) != 0) 22 | return -2; 23 | 24 | hmac_sha512_set_key(&hmac_sha512, strlen(key), (uint8_t*)key); 25 | hmac_sha512_update(&hmac_sha512, sizeof(child.key.private), child.key.private); 26 | hmac_sha512_digest(&hmac_sha512, 64, entropy); 27 | 28 | return 0; 29 | } 30 | 31 | int 32 | bip85_application_bip39(const bip32_key_t *key, uint32_t language, uint32_t word_cnt, uint32_t index, 33 | char ***result, size_t *result_cnt) 34 | { 35 | bip39_t bip39; 36 | char buf[1024] = {0}; 37 | uint8_t entropy[512] = {0}; 38 | size_t entropy_bits; 39 | 40 | if (word_cnt == 12) entropy_bits = 128; 41 | else if (word_cnt == 15) entropy_bits = 160; 42 | else if (word_cnt == 18) entropy_bits = 192; 43 | else if (word_cnt == 21) entropy_bits = 224; 44 | else if (word_cnt == 24) entropy_bits = 256; 45 | else return -1; 46 | 47 | // only suport for english bip39 48 | if (language != 0) 49 | return -2; 50 | 51 | // derive entropy for bip39 seed phrase 52 | snprintf(buf, sizeof(buf), "39'/%d'/%d'/%d'", language, word_cnt, index); 53 | if (bip85_entropy_from_key(key, buf, &entropy) != 0) 54 | return -3; 55 | 56 | bip39_init(&bip39); 57 | if (bip39_to_mnemonics(&bip39, entropy, entropy_bits, result, result_cnt) != 0) 58 | return -4; 59 | 60 | return 0; 61 | } 62 | 63 | int 64 | bip85_application_pwd_base85(const bip32_key_t *key, uint32_t length, uint32_t index, char *result) 65 | { 66 | char buf[1024] = {0}; 67 | size_t buf_size = sizeof(buf); 68 | uint8_t entropy[512] = {0}; 69 | 70 | snprintf(buf, buf_size, "707785'/%d'/%d'", length, index); 71 | if (bip85_entropy_from_key(key, buf, &entropy) != 0) 72 | return -1; 73 | 74 | if (utils_base85_encode(entropy, 64, buf) != 0) 75 | return -2; 76 | 77 | strncat(result, buf, length); 78 | return 0; 79 | } 80 | 81 | int 82 | bip85_application_hd_seed_wif(const bip32_key_t *key, uint32_t index, char *result, size_t *size) 83 | { 84 | char buf[1024] = {0}; 85 | size_t buf_size = sizeof(buf); 86 | uint8_t entropy[512] = {0}; 87 | bip32_key_t private_key; 88 | 89 | snprintf(buf, buf_size, "2'/%d'", index); 90 | if (bip85_entropy_from_key(key, buf, &entropy) != 0) 91 | return -1; 92 | 93 | private_key.public = false; 94 | memcpy(private_key.key.private, entropy, 32); 95 | 96 | if (bip32_key_to_wif(&private_key, result, &size) != 0) 97 | return -3; 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "utils.h" 7 | 8 | void 9 | utils_hexdump(uint8_t *data, size_t size, FILE *out) { 10 | size_t rows = 1 + size / 16; 11 | size_t byte_offset = 0; 12 | 13 | fprintf(out, "Hexdump of %ld bytes of data:\n", size); 14 | 15 | for (size_t row = 0; row < rows; row++) { 16 | byte_offset = row * 16; 17 | fprintf(out, "%8.8lx ", byte_offset); 18 | 19 | uint8_t byte; 20 | char str[17] = { 0 }; 21 | for (byte = 0; byte < 16; byte++) { 22 | if ((byte_offset + byte) == size) 23 | break; 24 | 25 | str[byte] = isprint(data[byte_offset + byte]) != 0 ? data[byte_offset + byte] : '.'; 26 | fprintf(out, " %.2x", data[byte_offset + byte]); 27 | } 28 | fprintf(out, " |%s|\n", str); 29 | if (byte_offset + byte == size) 30 | break; 31 | } 32 | fprintf(out, "%8.8lx\n", byte_offset + 16); 33 | } 34 | 35 | int 36 | utils_to_hex_string(const uint8_t *data, size_t size, char *result) 37 | { 38 | char buf[16]; 39 | result[0] = '\0'; 40 | for (size_t i = 0; i < size; i++) { 41 | sprintf(buf, "%.2x", data[i]); 42 | strcat(result, buf); 43 | } 44 | return 0; 45 | } 46 | 47 | int 48 | utils_base85_encode(const uint8_t *data, size_t size, char *result) 49 | { 50 | // Using rfc1925 variant 51 | static char *base85="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; 52 | 53 | uint32_t value; 54 | uint8_t *pdata = data; 55 | uint8_t *presult = result; 56 | 57 | while (1) { 58 | if (pdata >= data + size) 59 | break; 60 | 61 | value = utils_in_u32_be(pdata); 62 | 63 | if (value == 0) { 64 | *presult = 'z'; 65 | pdata += 4; 66 | continue; 67 | } 68 | 69 | uint32_t b1 = value % 85; 70 | value = (value - b1) / 85; 71 | uint32_t b2 = value % 85; 72 | value = (value - b2) / 85; 73 | uint32_t b3 = value % 85; 74 | value = (value - b3) / 85; 75 | uint32_t b4 = value % 85; 76 | value = (value - b4) / 85; 77 | uint32_t b5 = value % 85; 78 | 79 | presult[0] = base85[b5]; 80 | presult[1] = base85[b4]; 81 | presult[2] = base85[b3]; 82 | presult[3] = base85[b2]; 83 | presult[4] = base85[b1]; 84 | presult += 5; 85 | 86 | pdata += 4; 87 | } 88 | 89 | return 0; 90 | } 91 | 92 | int 93 | utils_fill_random(uint8_t *out, size_t size) 94 | { 95 | int h = open("/dev/urandom", O_RDONLY); 96 | if (read(h, out, size) < size) 97 | return -1; 98 | close(h); 99 | return 0; 100 | } 101 | 102 | int 103 | utils_sha256_checksum(const uint8_t *data, size_t size, uint8_t *checksum) 104 | { 105 | struct sha256_ctx sha256; 106 | uint8_t hashed[SHA256_DIGEST_SIZE] = {0}; 107 | sha256_init(&sha256); 108 | sha256_update(&sha256, size, data); 109 | sha256_digest(&sha256, SHA256_DIGEST_SIZE, hashed); 110 | sha256_update(&sha256, SHA256_DIGEST_SIZE, hashed); 111 | sha256_digest(&sha256, SHA256_DIGEST_SIZE, hashed); 112 | 113 | memcpy(checksum, hashed, 4); 114 | return 0; 115 | } 116 | 117 | int 118 | utils_hash160(const uint8_t *data, size_t size, uint8_t *out) 119 | { 120 | struct sha256_ctx sha256; 121 | struct ripemd160_ctx ripemd160; 122 | uint8_t hashed[SHA256_DIGEST_SIZE] = {0}; 123 | 124 | sha256_init(&sha256); 125 | sha256_update(&sha256, size, data); 126 | sha256_digest(&sha256, SHA256_DIGEST_SIZE, hashed); 127 | 128 | ripemd160_init(&ripemd160); 129 | ripemd160_update(&ripemd160, sizeof(hashed), hashed); 130 | ripemd160_digest(&ripemd160, RIPEMD160_DIGEST_SIZE, out); 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /src/btct.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | 8 | extern int (store_command)(int,char**); 9 | extern int (bip32_command)(int,char**); 10 | extern int (bip39_command)(int,char**); 11 | extern int (bip44_command)(int,char**); 12 | extern int (bip85_command)(int,char**); 13 | extern int (sss_command)(int,char**); 14 | 15 | static command_t commands[] = { 16 | { "store", store_command }, 17 | { "bip32", bip32_command }, 18 | { "bip39", bip39_command }, 19 | { "bip44", bip44_command }, 20 | { "bip85", bip85_command }, 21 | { "sss", sss_command }, 22 | { NULL, NULL, } 23 | }; 24 | 25 | static void usage(void) 26 | { 27 | fputs("usage: btct [-v | --version] [-h | --help]\n", stderr); 28 | fputs(" [.] []\n", stderr); 29 | fputs("\n", stderr); 30 | fputs("These are the BiTCoin Tools (btct) modules used for various operation:\n", stderr); 31 | fputs("\n", stderr); 32 | fputs(" store Store of a password protected mnemonics seed phrase\n", stderr); 33 | fputs("\n", stderr); 34 | fputs(" bip32 Hierarchical Deterministic Wallets\n", stderr); 35 | fputs("\n", stderr); 36 | fputs(" bip39 Mnemonic code for generating deterministic keys\n", stderr); 37 | fputs("\n", stderr); 38 | fputs(" bip44 Multi-Account Hierarchy for Deterministic Wallets\n", stderr); 39 | fputs("\n", stderr); 40 | fputs(" bip85 Deterministic Entropy From BIP32 Keychains\n", stderr); 41 | fputs("\n", stderr); 42 | fputs(" sss Shamir Secret Sharing is used to secure a secret in a distributed form,\n" 43 | " most often to secure encryption keys. The secret is split into multiple\n" 44 | " shares, which individually do not give any information about the secret.", stderr); 45 | fputs("\n\n", stderr); 46 | fputs("To get more information of each module, name the module and add the --help argument to the btct\n" 47 | "commandline, for example if you want to know more about bip32 module run like:\n", stderr); 48 | fputs("\n", stderr); 49 | fputs(" btct bip32 --help\n", stderr); 50 | fputs("\n", stderr); 51 | } 52 | 53 | static void version(void) 54 | { 55 | fputs("bct v1.0.0\n", stderr); 56 | } 57 | 58 | int 59 | main(int argc, char **argv) 60 | { 61 | 62 | int i, c; 63 | char *command; 64 | int argc_command = argc; 65 | 66 | for (i = 1; i < argc; i++) { 67 | if (argv[i][0] == '-') 68 | continue; 69 | 70 | command = argv[i]; 71 | argv[i] = NULL; 72 | argc_command = i; 73 | break; 74 | } 75 | 76 | while (1) 77 | { 78 | int option_index = 0; 79 | static struct option long_options[] = { 80 | {"help", no_argument, 0, 'h' }, 81 | {"version", no_argument, 0, 'v'}, 82 | {0, 0, 0, 0} 83 | }; 84 | 85 | c = getopt_long(argc_command, argv, "hv", long_options, &option_index); 86 | if (c == -1) 87 | break; 88 | 89 | switch (c) { 90 | case 'h': 91 | usage(); 92 | return EXIT_FAILURE; 93 | case 'v': 94 | version(); 95 | return EXIT_FAILURE; 96 | } 97 | } 98 | 99 | if (optind == argc) 100 | { 101 | usage(); 102 | exit(EXIT_FAILURE); 103 | } 104 | 105 | argc = argc - argc_command; 106 | argv += argc_command; 107 | *argv = command; 108 | 109 | int res = command_dispatch(commands, command, true, argc, argv); 110 | if (res == -1) 111 | usage(); 112 | 113 | exit(res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 114 | } 115 | -------------------------------------------------------------------------------- /test/test_vectors.h: -------------------------------------------------------------------------------- 1 | #include 2 | typedef struct test_vector_t { 3 | uint8_t entropy[32]; 4 | uint8_t entropy_bytes; 5 | char *mnemonics[24]; 6 | uint8_t seed[64]; 7 | uint8_t privkey[64]; 8 | char *masterkey; 9 | char *publickey; 10 | char *p2pkh_legacy_address; 11 | } test_vector_t; 12 | 13 | static test_vector_t vectors[] = { 14 | { 15 | .entropy = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }, 16 | .entropy_bytes = 16, 17 | .mnemonics = { "legal", "winner", "thank", "year", "wave", "sausage", "worth", "useful", "legal", "winner", "thank", "yellow" }, 18 | .seed = { 19 | 0x87, 0x83, 0x86, 0xef, 0xb7, 0x88, 0x45, 0xb3, 0x35, 0x5b, 0xd1, 0x5e, 0xa4, 0xd3, 0x9e, 0xf9, 20 | 0x7d, 0x17, 0x9c, 0xb7, 0x12, 0xb7, 0x7d, 0x5c, 0x12, 0xb6, 0xbe, 0x41, 0x5f, 0xff, 0xef, 0xfe, 21 | 0x5f, 0x37, 0x7b, 0xa0, 0x2b, 0xf3, 0xf8, 0x54, 0x4a, 0xb8, 0x00, 0xb9, 0x55, 0xe5, 0x1f, 0xbf, 22 | 0xf0, 0x98, 0x28, 0xf6, 0x82, 0x05, 0x2a, 0x20, 0xfa, 0xa6, 0xad, 0xdb, 0xbd, 0xdf, 0xb0, 0x96 23 | }, 24 | .privkey = { 25 | 0xc5, 0x52, 0x57, 0xc3, 0x60, 0xc0, 0x7c, 0x72, 0x02, 0x9a, 0xeb, 0xc1, 0xb5, 0x3c, 0x05, 0xed, 26 | 0x03, 0x62, 0xad, 0xa3, 0x8e, 0xad, 0x3e, 0x3e, 0x9e, 0xfa, 0x37, 0x08, 0xe5, 0x34, 0x95, 0x53, 27 | 0x1f, 0x09, 0xa6, 0x98, 0x75, 0x99, 0xd1, 0x82, 0x64, 0xc1, 0xe1, 0xc9, 0x2f, 0x2c, 0xf1, 0x41, 28 | 0x63, 0x0c, 0x7a, 0x3c, 0x4a, 0xb7, 0xc8, 0x1b, 0x2f, 0x00, 0x16, 0x98, 0xe7, 0x46, 0x3b, 0x04 29 | }, 30 | .masterkey = "xprv9s21ZrQH143K2x4gnzRB1eZDq92Uuvy9CXbvgQGdvykXZ9mkkot6LBjzDpgaAfvzkuxJe9JKJXQ38VoPutxvACA5MsyoBs5UyQ4HZKGshGs", 31 | .publickey = "xpub661MyMwAqRbcFS99u1xBNnVxPAryKPgzZkXXUngFVKHWRx6uJMCLsz4U56FN7PxTSeVqL8tPJpiCrs1KZh1dV2Bh6QyAbmNmjFRPnkrZP52", 32 | .p2pkh_legacy_address ="1Hp4asPpCYUURPBRHjHFeYmSpicwzcx6NB", 33 | }, 34 | 35 | { 36 | .entropy = { 37 | 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 38 | 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 39 | }, 40 | .entropy_bytes = 24, 41 | .mnemonics = { 42 | "legal", "winner", "thank", "year", "wave", "sausage", "worth", "useful", "legal", "winner", "thank", 43 | "year", "wave", "sausage", "worth", "useful", "legal", "will" 44 | }, 45 | .privkey = { 46 | 0xf2, 0xb9, 0x45, 0x08, 0x73, 0x2b, 0xcb, 0xac, 0xbc, 0xc0, 0x20, 0xfa, 0xef, 0xec, 0xfc, 0x89, 47 | 0xfe, 0xaf, 0xa6, 0x64, 0x9a, 0x54, 0x91, 0xb8, 0xc9, 0x52, 0xce, 0xde, 0x49, 0x6c, 0x21, 0x4a, 48 | 0x0c, 0x7b, 0x3c, 0x39, 0x2d, 0x16, 0x87, 0x48, 0xf2, 0xd4, 0xa6, 0x12, 0xba, 0xda, 0x07, 0x53, 49 | 0xb5, 0x2a, 0x1c, 0x7a, 0xc5, 0x3c, 0x1e, 0x93, 0xab, 0xd5, 0xc6, 0x32, 0x0b, 0x9e, 0x95, 0xdd, 50 | }, 51 | .masterkey = "xprv9s21ZrQH143K3Lv9MZLj16np5GzLe7tDKQfVusBni7toqJGcnKRtHSxUwbKUyUWiwpK55g1DUSsw76TF1T93VT4gz4wt5RM23pkaQLnvBh7", 52 | }, 53 | 54 | { 55 | .entropy = { 56 | 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 57 | 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 58 | }, 59 | .entropy_bytes = 32, 60 | .mnemonics = { 61 | "legal", "winner", "thank", "year", "wave", "sausage", "worth", "useful", "legal", "winner", "thank", "year", 62 | "wave", "sausage", "worth", "useful", "legal", "winner", "thank", "year", "wave", "sausage", "worth", "title" 63 | }, 64 | .privkey = { 65 | 66 | 0xbc, 0x09, 0xfc, 0xa1, 0x80, 0x4f, 0x7e, 0x69, 0xda, 0x93, 0xc2, 0xf2, 0x02, 0x8e, 0xb2, 0x38, 67 | 0xc2, 0x27, 0xf2, 0xe9, 0xdd, 0xa3, 0x0c, 0xd6, 0x36, 0x99, 0x23, 0x25, 0x78, 0x48, 0x0a, 0x40, 68 | 0x21, 0xb1, 0x46, 0xad, 0x71, 0x7f, 0xbb, 0x7e, 0x45, 0x1c, 0xe9, 0xeb, 0x83, 0x5f, 0x43, 0x62, 69 | 0x0b, 0xf5, 0xc5, 0x14, 0xdb, 0x0f, 0x8a, 0xdd, 0x49, 0xf5, 0xd1, 0x21, 0x44, 0x9d, 0x3e, 0x87, 70 | }, 71 | .masterkey = "xprv9s21ZrQH143K3Y1sd2XVu9wtqxJRvybCfAetjUrMMco6r3v9qZTBeXiBZkS8JxWbcGJZyio8TrZtm6pkbzG8SYt1sxwNLh3Wx7to5pgiVFU" 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/bip44_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | #include "bip44.h" 8 | 9 | static int _bip44_account(uint32_t account_nr, char *coin_symbol) 10 | { 11 | bip32_key_t key, account_key; 12 | uint8_t buf[4096]={0}; 13 | size_t bytes = 0; 14 | bip44_coin_t *coin; 15 | 16 | coin = bip44_coin_by_symbol(coin_symbol); 17 | if (coin == NULL) 18 | { 19 | fprintf(stderr, "bip44.account: symbol '%s' not found in coin table\n", coin_symbol); 20 | return EXIT_FAILURE; 21 | } 22 | 23 | freopen(NULL, "rb", stdin); 24 | while (fread(buf + bytes, 1, 1, stdin)) 25 | bytes++; 26 | 27 | if (buf[bytes-1] == '\n') { 28 | buf[bytes-1] = '\0'; 29 | bytes--; 30 | } 31 | 32 | if (bip32_key_deserialize(&key, buf) != 0) 33 | { 34 | fputs("bip44.account: failed to deserialize key from stdin\n", stderr); 35 | return EXIT_FAILURE; 36 | } 37 | 38 | if (key.public == true) 39 | { 40 | fputs("bip44.account: failed, read private key is a public key\n", stderr); 41 | return EXIT_FAILURE; 42 | } 43 | 44 | if (bip44_create_account(&key, coin, account_nr, &account_key) != 0) 45 | { 46 | fputs("bip44.account: failed, read create account private key\n", stderr); 47 | return EXIT_FAILURE; 48 | } 49 | 50 | bytes = sizeof(buf); 51 | bip32_key_serialize(&account_key, true, buf, &bytes); 52 | fprintf(stdout, "%s\n", buf); 53 | return EXIT_SUCCESS; 54 | } 55 | 56 | static void 57 | _bip44_account_command_usage(void) 58 | { 59 | fputs("usage: btct bip44.account \n", stderr); 60 | fputs("\n", stderr); 61 | fputs(" -a, --account Specify which account number to derive a key for, default\n", stderr); 62 | fputs(" account is #0.\n", stderr); 63 | fputs(" -c, --coin Specify coin symbol for specific coin type, default is 'BTC'\n", stderr); 64 | fputs("\n", stderr); 65 | fputs(" Generate account #2 for BTC\n", stderr); 66 | fputs("\n", stderr); 67 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n",stderr); 68 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 69 | fputs(" | btct bip32.masterkey\\\n", stderr); 70 | fputs(" | btct bip44.account --account=2 --coin=BTC\n", stderr); 71 | fputs("\n", stderr); 72 | } 73 | 74 | static int 75 | _bip44_account_command(int argc, char **argv) 76 | { 77 | int c; 78 | int account_nr = 0; 79 | char *coin_symbol = "BTC"; 80 | 81 | while (1) 82 | { 83 | int option_index = 0; 84 | static struct option long_options[] = { 85 | {"help", no_argument, 0, 'h' }, 86 | {"account", required_argument, 0, 'a' }, 87 | {"coin", required_argument, 0, 'c' }, 88 | {0, 0, 0, 0} 89 | }; 90 | 91 | c = getopt_long(argc, argv, "ha:c:", long_options, &option_index); 92 | if (c == -1) 93 | break; 94 | 95 | switch (c) { 96 | case 'h': 97 | _bip44_account_command_usage(); 98 | return EXIT_FAILURE; 99 | 100 | case 'a': 101 | account_nr = atoi(optarg); 102 | break; 103 | 104 | case 'c': 105 | coin_symbol = optarg; 106 | break; 107 | } 108 | } 109 | 110 | return _bip44_account(account_nr, coin_symbol); 111 | } 112 | 113 | static void _bip44_command_usage(void) 114 | { 115 | fputs("usage: btct bip44. \n", stderr); 116 | fputs("\n", stderr); 117 | fputs(" account Generate a bip44 account for specified coin from encoded masterkey\n", stderr); 118 | fputs(" read on stdin.\n", stderr); 119 | fputs("\n",stderr); 120 | fputs("examples:\n", stderr); 121 | fputs("\n",stderr); 122 | fputs(" Generate bip44 account #1 for XRP coin:\n",stderr); 123 | fputs("\n",stderr); 124 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n", stderr); 125 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 126 | fputs(" | btct bip32.masterkey \\\n", stderr); 127 | fputs(" | btct bip44.account -a 0 -c XRP\n", stderr); 128 | fputs("\n", stderr); 129 | } 130 | 131 | int bip44_command(int argc, char **argv) 132 | { 133 | int res; 134 | 135 | struct command_t commands[] = { 136 | { "bip44.account", _bip44_account_command }, 137 | { NULL, NULL, } 138 | }; 139 | 140 | res = command_dispatch(commands, argv[0], false, argc, argv); 141 | if (res == -1) 142 | _bip44_command_usage(); 143 | 144 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 145 | } 146 | -------------------------------------------------------------------------------- /src/store.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bip39_english.h" 11 | 12 | #include "store.h" 13 | #include "utils.h" 14 | 15 | #define KEY_SIZE 32 16 | 17 | static int 18 | _derive_key(const char *password, uint8_t *key) { 19 | static size_t iterations = 4096; 20 | static char *salt = "btct_store_password"; 21 | pbkdf2_hmac_sha512(strlen(password), password, iterations, strlen(salt), salt, KEY_SIZE, key); 22 | 23 | return 0; 24 | } 25 | 26 | static uint16_t 27 | _lookup_mnemonic_index(const char *mnemonic) 28 | { 29 | char *pend = mnemonic; 30 | while (*pend != ' ' && *pend != '\0' && *pend != '\n') 31 | pend++; 32 | 33 | for (size_t i = 0; i < 2047; i++) { 34 | if (strncmp(bip39_english[i], mnemonic, pend - mnemonic) == 0) 35 | return i; 36 | } 37 | return 0xffff; 38 | } 39 | 40 | static int 41 | _mnemonics_to_data(const char *mnemonics, uint8_t *out) 42 | { 43 | uint16_t cnt = 0; 44 | uint8_t *pout = out; 45 | char *ps = mnemonics; 46 | 47 | while (1) { 48 | uint16_t idx = _lookup_mnemonic_index(ps); 49 | utils_out_u16_be(pout, idx); 50 | if ( pout[0] == 0xff && pout[1] == 0xff) 51 | return -1; 52 | 53 | cnt++; 54 | 55 | // advance to start of next word 56 | while(*ps != ' ' && *ps != '\0') 57 | ps++; 58 | 59 | if (*ps == '\0') 60 | break; 61 | 62 | ps++; 63 | pout+=2; 64 | } 65 | 66 | return cnt; 67 | } 68 | 69 | int 70 | _write_store_file(const char *filename, const uint8_t *data, size_t size) 71 | { 72 | char file[2048] = {0}; 73 | char *home_dir = getenv("HOME"); 74 | if (filename == NULL) 75 | snprintf(file, sizeof(file), "%s/.btct.dat", home_dir != NULL ? home_dir : "./" ); 76 | else 77 | realpath(file, filename); 78 | 79 | int out = open(file, O_CREAT | O_RDWR | O_TRUNC, S_IRWXU); 80 | if (out == -1) { 81 | perror("open failed with reason"); 82 | return -1; 83 | } 84 | 85 | write(out, data, size); 86 | close(out); 87 | 88 | return 0; 89 | } 90 | 91 | int 92 | _read_store_file(const char *filename, uint8_t *data, size_t size) { 93 | char file[2048] = {0}; 94 | char *home_dir = getenv("HOME"); 95 | if (filename == NULL) 96 | snprintf(file, sizeof(file), "%s/.btct.dat", home_dir != NULL ? home_dir : "./" ); 97 | else 98 | realpath(file, filename); 99 | 100 | int out = open(file, O_RDONLY); 101 | if (out == -1) { 102 | perror("open failed with reason"); 103 | return -1; 104 | } 105 | 106 | read(out, data, size); 107 | close(out); 108 | 109 | return 0; 110 | } 111 | 112 | int 113 | store_write_mnemonics(const char *filename, const char *password, const uint8_t *mnemonics) 114 | { 115 | uint8_t block[128] = {0}; 116 | uint8_t data[128] = {0}; 117 | int res; 118 | uint8_t key[KEY_SIZE]; 119 | struct aes_ctx aes; 120 | 121 | // derive key from password 122 | _derive_key(password, &key); 123 | 124 | // fill block with random data 125 | if (utils_fill_random(block, sizeof(block)) != 0) 126 | return -1; 127 | 128 | // write mnemonics indicies to block 129 | res = _mnemonics_to_data(mnemonics, block); 130 | if (res == -1) 131 | return -2; 132 | 133 | // add marker for end of mnemonics 134 | block[res*2] = 0xff; 135 | block[1 + res*2] = 0xff; 136 | 137 | // encrypt block 138 | aes_set_encrypt_key(&aes, KEY_SIZE, key); 139 | aes_encrypt(&aes, sizeof(block), data, (uint8_t*)block); 140 | 141 | // write to file 142 | _write_store_file(filename, data, sizeof(data)); 143 | return 0; 144 | } 145 | 146 | int store_read_mnemonics(const char *filename, const char *password, 147 | uint8_t *data, size_t *size) 148 | { 149 | uint8_t block[128]; 150 | uint8_t decrypted_block[128]; 151 | uint8_t key[KEY_SIZE]; 152 | struct aes_ctx aes; 153 | 154 | // derive key from password 155 | _derive_key(password, &key); 156 | 157 | // read file from store 158 | if (_read_store_file(filename, block, sizeof(block)) != 0) 159 | return -1; 160 | 161 | // decrypt using aes 162 | aes_set_decrypt_key(&aes, KEY_SIZE, key); 163 | aes_decrypt(&aes, sizeof(block), decrypted_block, block); 164 | 165 | // create mnemonics from data 166 | uint8_t *pdata = decrypted_block; 167 | uint8_t cnt = 1; 168 | 169 | while(cnt <= 24) { 170 | uint16_t idx = utils_in_u16_be(pdata); 171 | if (idx == 0xffff) 172 | break; 173 | else if(pdata != decrypted_block) 174 | fputs(" ", stdout); 175 | 176 | idx = idx & 0x07ff; 177 | 178 | fputs(bip39_english[idx], stdout); 179 | 180 | pdata += 2; 181 | cnt++; 182 | } 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /test/bip85_spec.c: -------------------------------------------------------------------------------- 1 | #include "./bdd-for-c.h" 2 | #include "./test_vectors.h" 3 | #include "../src/bip85.h" 4 | 5 | #define check_str(got, expected) check(strcmp(got, expected) == 0, "expected string '%s' got '%s'", expected, got) 6 | #define check_number(got, expected) check(got == expected, "expected '%d' got '%d'", expected, got) 7 | 8 | static void 9 | str_array_to_str(char **array, size_t size, char *delimiter, char *result) 10 | { 11 | for (size_t i = 0; i < size; i++) { 12 | strcat(result, array[i]); 13 | if (i != size - 1) 14 | strcat(result, delimiter); 15 | } 16 | } 17 | 18 | spec("bip85") { 19 | 20 | context("Given known masterkey") { 21 | static bip32_key_t key; 22 | before() { 23 | bip32_key_deserialize(&key, "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"); 24 | } 25 | 26 | describe("when deriving entropy using subpath 0'/0'") { 27 | static uint8_t entropy[64] = {0}; 28 | static uint8_t expected_entropy[] = { 29 | 0xef, 0xec, 0xfb, 0xcc, 0xff, 0xea, 0x31, 0x32, 0x14, 0x23, 0x2d, 0x29, 0xe7, 0x15, 0x63, 0xd9, 30 | 0x41, 0x22, 0x9a, 0xfb, 0x43, 0x38, 0xc2, 0x1f, 0x95, 0x17, 0xc4, 0x1a, 0xaa, 0x0d, 0x16, 0xf0, 31 | 0x0b, 0x83, 0xd2, 0xa0, 0x9e, 0xf7, 0x47, 0xe7, 0xa6, 0x4e, 0x8e, 0x2b, 0xd5, 0xa1, 0x48, 0x69, 32 | 0xe6, 0x93, 0xda, 0x66, 0xce, 0x94, 0xac, 0x2d, 0xa5, 0x70, 0xab, 0x7e, 0xe4, 0x86, 0x18, 0xf7 33 | }; 34 | static int result = -1; 35 | before() { 36 | result = bip85_entropy_from_key(&key, "0'/0'", entropy); 37 | } 38 | 39 | it("then should not return error") 40 | check_number(0, result); 41 | 42 | it("then should return expected entropy") 43 | check(memcmp(expected_entropy, entropy, 64) == 0); 44 | } 45 | 46 | describe("when deriving entropy using subpath 0'/1'") { 47 | static uint8_t entropy[64] = {0}; 48 | static uint8_t expected_entropy[] = { 49 | 0x70, 0xc6, 0xe3, 0xe8, 0xeb, 0xee, 0x8d, 0xc4, 0xc0, 0xdb, 0xba, 0x66, 0x07, 0x68, 0x19, 0xbb, 50 | 0x8c, 0x09, 0x67, 0x25, 0x27, 0xc4, 0x27, 0x7c, 0xa8, 0x72, 0x95, 0x32, 0xad, 0x71, 0x18, 0x72, 51 | 0x21, 0x8f, 0x82, 0x69, 0x19, 0xf6, 0xb6, 0x72, 0x18, 0xad, 0xde, 0x99, 0x01, 0x8a, 0x6d, 0xf9, 52 | 0x09, 0x5a, 0xb2, 0xb5, 0x8d, 0x80, 0x3b, 0x5b, 0x93, 0xec, 0x98, 0x02, 0x08, 0x5a, 0x69, 0x0e 53 | }; 54 | 55 | static int result = -1; 56 | before() { 57 | result = bip85_entropy_from_key(&key, "0'/1'", entropy); 58 | } 59 | 60 | it("then should not return error") 61 | check_number(0, result); 62 | 63 | it("then should return expected entropy") 64 | check(memcmp(expected_entropy, entropy, 64) == 0); 65 | } 66 | 67 | context("application bip39") { 68 | describe("when generating 12 english word index 0") { 69 | static char **words; 70 | static size_t word_cnt; 71 | static char *expected_words[] = { 72 | "girl", "mad", "pet", "galaxy", "egg", "matter", "matrix", "prison", 73 | "refuse", "sense", "ordinary", "nose"}; 74 | before() { 75 | bip85_application_bip39(&key, 0, 12, 0, &words, &word_cnt); 76 | } 77 | 78 | it("then it should generate correct 12 words") 79 | check_number(word_cnt, 12); 80 | 81 | it("then it should generate correct mnemonic phrase") { 82 | for (size_t i=0; i < word_cnt; i++) { 83 | check_str(words[i], expected_words[i]); 84 | } 85 | } 86 | } 87 | 88 | describe("when generating 18 english word index 0") { 89 | static char **words; 90 | static size_t word_cnt; 91 | static char *expected_words[] = { 92 | "near", "account", "window", "bike", "charge", "season", "chef", "number", 93 | "sketch", "tomorrow", "excuse", "sniff", "circle", "vital", "hockey", "outdoor", 94 | "supply", "token" 95 | }; 96 | before() { 97 | bip85_application_bip39(&key, 0, 18, 0, &words, &word_cnt); 98 | } 99 | 100 | it("then it should generate correct 18 words") 101 | check_number(word_cnt, 18); 102 | 103 | it("then it should generate correct mnemonic phrase") { 104 | for (size_t i=0; i < word_cnt; i++) { 105 | check_str(words[i], expected_words[i]); 106 | } 107 | } 108 | } 109 | 110 | describe("when generating 24 english word index 0") { 111 | static char **words; 112 | static size_t word_cnt; 113 | static char *expected_words[] = { 114 | "puppy", "ocean", "match", "cereal", "symbol", "another", "shed", "magic", 115 | "wrap", "hammer", "bulb", "intact", "gadget", "divorce", "twin", "tonight", 116 | "reason", "outdoor", "destroy", "simple", "truth", "cigar", "social", "volcano" 117 | }; 118 | before() { 119 | bip85_application_bip39(&key, 0, 24, 0, &words, &word_cnt); 120 | } 121 | 122 | it("then it should generate correct 18 words") 123 | check_number(word_cnt, 24); 124 | 125 | it("then it should generate correct mnemonic phrase") { 126 | for (size_t i=0; i < word_cnt; i++) { 127 | check_str(words[i], expected_words[i]); 128 | } 129 | } 130 | } 131 | } 132 | 133 | context("application pwd_base85") { 134 | describe("when generating password with length 12 using index 0") { 135 | static char password[64] = {0}; 136 | before () { 137 | bip85_application_pwd_base85(&key, 12, 0, &password); 138 | } 139 | 140 | it("then generated password should be 12 characters long") 141 | check_number(strlen(password), 12); 142 | 143 | it("then generated password should be expected string") 144 | check_str(password, "_s`{TW89)i4`"); 145 | } 146 | 147 | 148 | describe("when generating password with length 8 using index 1") { 149 | static char password[64] = {0}; 150 | before () { 151 | bip85_application_pwd_base85(&key, 8, 1, &password); 152 | } 153 | 154 | it("then generated password should be 8 characters long") 155 | check_number(strlen(password), 8); 156 | 157 | it("then generated password should be expected string") 158 | check_str(password, "W%v4tL`%"); 159 | } 160 | } 161 | 162 | context("application HD-Seed WIF") { 163 | describe("when generating password with length 12 using index 0") { 164 | static char wif[256] = {0}; 165 | static size_t size; 166 | static int result = -1; 167 | before () { 168 | size = sizeof(wif); 169 | result = bip85_application_hd_seed_wif(&key, 0, &wif, &size); 170 | } 171 | 172 | it("then should not return error") 173 | check_number(result, 0); 174 | 175 | it("then generate wallet WIF should be expected string") 176 | check_str(wif, "Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp"); 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/bip39_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | #include "bip39.h" 8 | 9 | 10 | typedef enum _mnemonics_format_e { 11 | SENTENCE, 12 | PRETTY 13 | } _mnemonics_format_t; 14 | 15 | static int _bip39_to_mnemonics(_mnemonics_format_t format) 16 | { 17 | uint8_t seed[128]={0}; 18 | size_t bytes = 0; 19 | 20 | // Read entrophy bits from stdin 21 | freopen(NULL, "rb", stdin); 22 | while (fread(seed + bytes, 1, 1, stdin)) 23 | bytes++; 24 | 25 | fprintf(stderr, "bip39.mnemonics: read %ld bits of entrophy seed from stdin\n", bytes*8); 26 | 27 | bip39_t ctx; 28 | if (bip39_init(&ctx) != 0) { 29 | fprintf(stderr, "bip39.mnemonics: failed to initialize bip39 context\n"); 30 | return EXIT_FAILURE; 31 | } 32 | 33 | char **words = NULL; 34 | size_t word_count = 0; 35 | if (bip39_to_mnemonics(&ctx, seed, bytes*8, &words, &word_count) != 0) { 36 | fprintf(stderr, "bip39.mnemonics: failed to generate mnemonics from seed\n"); 37 | return EXIT_FAILURE; 38 | } 39 | 40 | for (size_t w = 0; w < word_count; w++) 41 | { 42 | switch (format) 43 | { 44 | 45 | case SENTENCE: 46 | fprintf(stdout, "%s", words[w]); 47 | if (w < word_count - 1) 48 | fputs(" ", stdout); 49 | break; 50 | 51 | case PRETTY: 52 | default: 53 | fprintf(stdout, "%ld: %s\n", 1 + w, words[w]); 54 | break; 55 | } 56 | } 57 | 58 | fputs("\n", stdout); 59 | free(words); 60 | 61 | return EXIT_SUCCESS; 62 | } 63 | 64 | static void 65 | _bip39_mnemonics_command_usage(void) 66 | { 67 | fputs("usage: btct bip39.mnemonics \n", stderr); 68 | fputs("\n", stderr); 69 | fputs(" -s, --sentence Output mnemonics as a sentence on one line, use as input\n", stderr); 70 | fputs(" when creating a seed.\n", stderr); 71 | fputs("\n", stderr); 72 | fputs("examples:\n", stderr); 73 | fputs("\n", stderr); 74 | fputs(" Create a random seed of 16 bytes eg. 128 bits seed and generate mnemonics\n", stderr); 75 | fputs("\n", stderr); 76 | fputs(" head -c16 /dev/random | btct bip39 --mnemonics\n", stderr); 77 | fputs("\n", stderr); 78 | fputs(" Testing using test vector\n", stderr); 79 | fputs("\n", stderr); 80 | fputs(" echo '7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f' | xxd -r -p | btct bip39.mnemonics\n", stderr); 81 | fputs("\n", stderr); 82 | 83 | } 84 | 85 | static int 86 | _bip39_mnemonics_command(int argc, char **argv) 87 | { 88 | int c; 89 | _mnemonics_format_t format = PRETTY; 90 | 91 | while (1) 92 | { 93 | int option_index = 0; 94 | static struct option long_options[] = { 95 | {"help", no_argument, 0, 'h' }, 96 | {"sentence", no_argument, 0, 's' }, 97 | {0, 0, 0, 0} 98 | }; 99 | 100 | c = getopt_long(argc, argv, "hs", long_options, &option_index); 101 | if (c == -1) 102 | break; 103 | 104 | switch (c) { 105 | case 'h': 106 | _bip39_mnemonics_command_usage(); 107 | return EXIT_FAILURE; 108 | 109 | case 's': 110 | format = SENTENCE; 111 | break; 112 | } 113 | } 114 | 115 | return _bip39_to_mnemonics(format); 116 | } 117 | 118 | 119 | static int _bip39_to_seed(int iterations, const char *passphrase) 120 | { 121 | uint8_t seed[64]={0}; 122 | uint8_t mnemonics[4096]={0}; 123 | size_t bytes = 0; 124 | 125 | freopen(NULL, "rb", stdin); 126 | while (fread(mnemonics + bytes, 1, 1, stdin)) 127 | bytes++; 128 | 129 | // strip newline 130 | if (mnemonics[bytes-1] == '\n') { 131 | mnemonics[bytes-1] = '\0'; 132 | bytes--; 133 | } 134 | 135 | bip39_t ctx; 136 | if (bip39_init(&ctx) != 0) { 137 | fprintf(stderr, "bip39.seed: failed to initialize bip39 context\n"); 138 | return EXIT_FAILURE; 139 | } 140 | 141 | if (bip39_to_seed(&ctx, mnemonics, bytes, iterations, (uint8_t*)passphrase, seed) != 0) { 142 | fprintf(stderr, "bip39.seed: failed to generate seed from mnenomics\n"); 143 | return EXIT_FAILURE; 144 | } 145 | 146 | freopen(NULL, "wb", stdout); 147 | fwrite(seed, 1, 64, stdout); 148 | fflush(stdout); 149 | 150 | return EXIT_SUCCESS; 151 | } 152 | 153 | static void 154 | _bip39_seed_command_usage(void) 155 | { 156 | fputs("usage: btct bip39.seed \n", stderr); 157 | fputs("\n", stderr); 158 | fputs(" -i, --iterations Override the default iterations of 2048 for PBKDF2 routine\n", stderr); 159 | fputs(" -p, --passphrase Passphrase for generating 'hidden' wallet\n", stderr); 160 | fputs("\n", stderr); 161 | fputs(" Generate seed with passphrase\n", stderr); 162 | fputs("\n", stderr); 163 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n",stderr); 164 | fputs(" | btct bip39.seed --passphrase=TREZOR\n", stderr); 165 | fputs("\n", stderr); 166 | } 167 | 168 | static int 169 | _bip39_seed_command(int argc, char **argv) 170 | { 171 | int c; 172 | 173 | const char *passphrase=NULL; 174 | int iterations = 2048; 175 | 176 | while (1) 177 | { 178 | int option_index = 0; 179 | static struct option long_options[] = { 180 | {"help", no_argument, 0, 'h' }, 181 | {"passphrase", required_argument, 0, 'p' }, 182 | {"iterations", required_argument, 0, 'i' }, 183 | {0, 0, 0, 0} 184 | }; 185 | 186 | c = getopt_long(argc, argv, "hp:i:", long_options, &option_index); 187 | if (c == -1) 188 | break; 189 | 190 | switch (c) { 191 | case 'h': 192 | _bip39_seed_command_usage(); 193 | return EXIT_FAILURE; 194 | 195 | case 'p': 196 | passphrase = optarg; 197 | break; 198 | 199 | case 'i': 200 | iterations = atoi(optarg); 201 | break; 202 | } 203 | } 204 | 205 | return _bip39_to_seed(iterations, passphrase); 206 | } 207 | 208 | static void _bip39_command_usage(void) 209 | { 210 | fputs("usage: btct bip39. \n", stderr); 211 | fputs("\n", stderr); 212 | fputs(" mnemonics Generate mnemonics sentence from specified entrophy read from\n", stderr); 213 | fputs(" stdin, writing mnemonic words to stdout.\n", stderr); 214 | fputs(" seed Generates seed from sentence read from stdin\n", stderr); 215 | fputs("\n",stderr); 216 | fputs("examples:\n", stderr); 217 | fputs("\n",stderr); 218 | fputs(" Generate bip39 seed from hex string entropy using a passphrase:\n",stderr); 219 | fputs("\n",stderr); 220 | fputs(" echo '7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f' | \\\n", stderr); 221 | fputs(" xxd -r -p | btct bip39.mnemonics --sentence | \\\n", stderr); 222 | fputs(" btct bip39.seed --passphrase=TREZOR | hexdump -C\n", stderr); 223 | fputs("\n", stderr); 224 | } 225 | 226 | int bip39_command(int argc, char **argv) 227 | { 228 | int res; 229 | 230 | struct command_t commands[] = { 231 | { "bip39.seed", _bip39_seed_command }, 232 | { "bip39.mnemonics", _bip39_mnemonics_command }, 233 | { NULL, NULL, } 234 | }; 235 | 236 | res = command_dispatch(commands, argv[0], false, argc, argv); 237 | if (res == -1) 238 | _bip39_command_usage(); 239 | 240 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 241 | } 242 | -------------------------------------------------------------------------------- /src/sss_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | #include "bip32.h" 8 | #include "sss.h" 9 | 10 | #include "../external/libbase58/libbase58.h" 11 | 12 | static int 13 | _sss_create(uint8_t share_cnt, uint8_t threshold) 14 | { 15 | char buf[1024]; 16 | size_t size = 1024; 17 | uint8_t tmp; 18 | size_t bytes=0; 19 | uint8_t data[sss_MLEN]={0}; 20 | sss_Share shares[200]; 21 | 22 | fprintf(stderr, "sss.create: creating %d share from secret with a recovery threshold of %d\n", share_cnt, threshold); 23 | 24 | // Read secret from stdin 25 | freopen(NULL, "rb", stdin); 26 | bytes = fread(data, 1, sss_MLEN, stdin); 27 | if (bytes == 64 && fread(&tmp, 1, 1, stdin) == 1) { 28 | fprintf(stderr, "sss.create: the secret length is more than %ld bytes, aborting...\n", sss_MLEN); 29 | return EXIT_FAILURE; 30 | } 31 | 32 | fprintf(stderr, "sss.create: read %ld bytes (%ld bits) of secret from stdin\n", bytes, bytes*8); 33 | 34 | // create shares 35 | memset(shares, 0, sizeof(shares)); 36 | sss_create_shares(shares, data, 200, threshold); 37 | 38 | // dump shares to stdout 39 | fprintf(stderr, "sss.create: dumping %d shares to stdout in base58\n", share_cnt); 40 | 41 | for (size_t s=0; s < share_cnt; s++) { 42 | size = sizeof(buf); 43 | b58enc(buf, &size, shares[rand() % 200], sizeof(sss_Share)); 44 | fprintf(stdout, "%s\n", buf); 45 | } 46 | 47 | return EXIT_SUCCESS; 48 | } 49 | 50 | static void 51 | _sss_create_usage(void) 52 | { 53 | fputs("usage: btct sss.create \n", stderr); 54 | fputs("\n", stderr); 55 | fputs(" -s, --shares= Specify the amount of shares to splite the input into,\n" 56 | " default value is 3 shares.\n", stderr); 57 | fputs(" -t, --threshold= Specify the threshold of number of shares required for\n" 58 | " recover the secret, default value is 2 shares.\n",stderr); 59 | fputs("\n", stderr); 60 | 61 | fputs("examples:\n", stderr); 62 | fputs("\n", stderr); 63 | fputs(" Create HD Wallet and split the master key into 5 shares with a threshold of 3 keys for recover the key.\n", stderr); 64 | fputs("\n", stderr); 65 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 66 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 67 | fputs(" btct bip32.masterkey | btct sss.create --shares=5 --thresholds=3\n", stderr); 68 | fputs("\n", stderr); 69 | } 70 | 71 | static int 72 | _sss_recover(void) 73 | { 74 | uint8_t out[sss_MLEN]; 75 | char *buf[256] = {NULL}; 76 | size_t length; 77 | uint8_t share_cnt = 0; 78 | sss_Share *shares; 79 | 80 | // Read share from stdin 81 | while(getline(&buf[share_cnt], &length, stdin) != -1) { 82 | share_cnt++; 83 | if (share_cnt == 255) { 84 | fputs("To many shares read from stdin\n", stderr); 85 | return EXIT_FAILURE; 86 | } 87 | } 88 | fprintf(stderr, "sss.recover: %d shares read from stdin\n", share_cnt); 89 | 90 | // Decode base58 shares into binary shares to recover from 91 | shares = malloc(sizeof(sss_Share) * share_cnt); 92 | for (int s = 0; s < share_cnt; s++) { 93 | length = sizeof(sss_Share); 94 | if (b58tobin((sss_Share *)shares[s], &length, buf[s], strlen(buf[s])-1) == false) { 95 | fputs("sss.recover: Failed to decode base58 encoded share\n", stderr); 96 | return EXIT_FAILURE; 97 | } 98 | } 99 | 100 | // Recover secret from shares 101 | if (sss_combine_shares(out, (const sss_Share *)shares, share_cnt) != 0) { 102 | fputs("sss.recover: failed to recover secret from shares\n", stderr); 103 | return EXIT_FAILURE; 104 | } 105 | 106 | fwrite(out, sizeof(out), 1, stdout); 107 | return EXIT_SUCCESS; 108 | } 109 | 110 | static void 111 | _sss_recover_usage(void) 112 | { 113 | fputs("usage: btct sss.recover \n", stderr); 114 | fputs("\n", stderr); 115 | 116 | fputs("examples:\n", stderr); 117 | fputs("\n", stderr); 118 | fputs(" Create three shares of a secret menomic seed with a threshold of two: \n", stderr); 119 | fputs("\n", stderr); 120 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 121 | fputs(" btct sss.create --shares=3 --thresholds=2\n", stderr); 122 | fputs("\n", stderr); 123 | } 124 | 125 | 126 | static int 127 | _sss_create_command(int argc, char **argv) { 128 | int c; 129 | uint8_t shares = 3; 130 | uint8_t threshold = 2; 131 | 132 | while (1) 133 | { 134 | int option_index = 0; 135 | static struct option long_options[] = { 136 | {"help", no_argument, 0, 'h' }, 137 | {"shares", required_argument, 0, 's' }, 138 | {"thresholds", required_argument, 0, 't' }, 139 | {0, 0, 0, 0} 140 | }; 141 | 142 | c = getopt_long(argc, argv, "hst", long_options, &option_index); 143 | if (c == -1) 144 | break; 145 | 146 | switch (c) { 147 | case 'h': 148 | _sss_create_usage(); 149 | return EXIT_FAILURE; 150 | 151 | case 's': 152 | shares = atoi(optarg); 153 | break; 154 | 155 | case 't': 156 | threshold = atoi(optarg); 157 | break; 158 | } 159 | } 160 | 161 | return _sss_create(shares, threshold); 162 | } 163 | 164 | static int 165 | _sss_recover_command(int argc, char **argv) { 166 | int c; 167 | while (1) 168 | { 169 | int option_index = 0; 170 | static struct option long_options[] = { 171 | {"help", no_argument, 0, 'h' }, 172 | {0, 0, 0, 0} 173 | }; 174 | 175 | c = getopt_long(argc, argv, "h", long_options, &option_index); 176 | if (c == -1) 177 | break; 178 | 179 | switch (c) { 180 | case 'h': 181 | _sss_recover_usage(); 182 | return EXIT_FAILURE; 183 | } 184 | } 185 | 186 | return _sss_recover(); 187 | } 188 | 189 | static void _sss_command_usage(void) 190 | { 191 | fputs("usage: btct sss. \n", stderr); 192 | fputs("\n", stderr); 193 | fputs("Shamir Secret Sharing (SSS) is used to secure a secret in a distributed form, most often to secure " 194 | "encryption keys. The secret is split into multiple shares, which individually do not give any " 195 | "information about the secret. " 196 | "To reconstruct a secret secured by SSS, a number of shares is needed, called the threshold. No " 197 | "information about the secret can be gained from any number of shares below the threshold (a property " 198 | "called perfect secrecy).\n",stderr); 199 | fputs("\n\n", stderr); 200 | fputs("commands:\n", stderr); 201 | fputs("\n", stderr); 202 | fputs(" create Create shares from your secret\n", stderr); 203 | fputs(" recover Combine a number of shares to recover secret\n", stderr); 204 | fputs("\n\n",stderr); 205 | fputs("examples:\n", stderr); 206 | fputs("\n",stderr); 207 | fputs(" Generate bip32 serialized HD wallet master key and then split it into three shares with the ability to recover using only two of the three shares:\n",stderr); 208 | fputs("\n",stderr); 209 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 210 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 211 | fputs(" btct sss.create --shares=3 --threshold=2\n", stderr); 212 | fputs("\n", stderr); 213 | } 214 | 215 | int sss_command(int argc, char **argv) 216 | { 217 | int res; 218 | 219 | struct command_t commands[] = { 220 | { "sss.create", _sss_create_command }, 221 | { "sss.recover", _sss_recover_command }, 222 | { NULL, NULL, } 223 | }; 224 | 225 | res = command_dispatch(commands, argv[0], false, argc, argv); 226 | if (res == -1) 227 | _sss_command_usage(); 228 | 229 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 230 | } 231 | -------------------------------------------------------------------------------- /src/bip85_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | #include "bip85.h" 8 | 9 | static int 10 | _bip85_read_private_key_from_stdin(bip32_key_t *key) 11 | { 12 | uint8_t buf[1024]={0}; 13 | size_t bytes = 0; 14 | 15 | // read and deserialize private key from stdin 16 | freopen(NULL, "rb", stdin); 17 | while (fread(buf + bytes, 1, 1, stdin)) 18 | bytes++; 19 | 20 | if (buf[bytes-1] == '\n') { 21 | buf[bytes-1] = '\0'; 22 | bytes--; 23 | } 24 | 25 | if (bip32_key_deserialize(key, buf) != 0) 26 | { 27 | fputs("bip85.*: failed to deserialize key from stdin\n", stderr); 28 | return -1; 29 | } 30 | 31 | if (key->public == true) 32 | { 33 | fputs("bip85.*: failed, read private key is a public key\n", stderr); 34 | return -2; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | static int 41 | _bip85_bip39(uint32_t language, uint32_t words, uint32_t index) 42 | { 43 | bip32_key_t key; 44 | char **result = NULL; 45 | size_t result_count = 0; 46 | 47 | if (_bip85_read_private_key_from_stdin(&key) != 0) 48 | return EXIT_FAILURE; 49 | 50 | if (bip85_application_bip39(&key, language, words, index, &result, &result_count) != 0) 51 | return EXIT_FAILURE; 52 | 53 | for(size_t i = 0; i < result_count; i++) { 54 | fprintf(stdout, "%s", result[i]); 55 | if (i < result_count - 1) 56 | fputs(" ", stdout); 57 | } 58 | fputs("\n", stdout); 59 | 60 | return EXIT_SUCCESS; 61 | } 62 | 63 | static void 64 | _bip85_bip39_command_usage(void) 65 | { 66 | fputs("usage: btct bip85.bip39 \n", stderr); 67 | fputs("\n", stderr); 68 | fputs("Generates a new bip39 mnemonics from derived entropy. Use this to create new deterministic\n", stderr); 69 | fputs("wallets from one seed due to the works of bip85.\n", stderr); 70 | fputs("\n", stderr); 71 | fputs(" -l, --language Specify which language to use for mnemonics , default\n", stderr); 72 | fputs(" language is 0 (english).\n", stderr); 73 | fputs(" -w, --words Specify the amount of words to use, default is 12\n", stderr); 74 | fputs(" -i, --index Specify the index for the mnemonics, default is 0\n", stderr); 75 | fputs("\n", stderr); 76 | fputs(" Generate 24 words mnemonics for use with a hot wallet\n", stderr); 77 | fputs("\n", stderr); 78 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n",stderr); 79 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 80 | fputs(" | btct bip32.masterkey\\\n", stderr); 81 | fputs(" | btct bip85.bip39 --words=24 --index=100\n", stderr); 82 | fputs("\n", stderr); 83 | } 84 | 85 | static int 86 | _bip85_bip39_command(int argc, char **argv) 87 | { 88 | int c; 89 | uint32_t language = 0; 90 | uint32_t word_cnt = 12; 91 | uint32_t index = 0; 92 | 93 | while (1) 94 | { 95 | int option_index = 0; 96 | static struct option long_options[] = { 97 | {"help", no_argument, 0, 'h' }, 98 | {"language", required_argument, 0, 'l' }, 99 | {"words", required_argument, 0, 'w' }, 100 | {"index", required_argument, 0, 'i' }, 101 | {0, 0, 0, 0} 102 | }; 103 | 104 | c = getopt_long(argc, argv, "hl:w:i:", long_options, &option_index); 105 | if (c == -1) 106 | break; 107 | 108 | switch (c) { 109 | case 'h': 110 | _bip85_bip39_command_usage(); 111 | return EXIT_FAILURE; 112 | 113 | case 'l': 114 | language = atoi(optarg); 115 | break; 116 | 117 | case 'w': 118 | word_cnt = atoi(optarg); 119 | break; 120 | 121 | case 'i': 122 | index = atoi(optarg); 123 | break; 124 | } 125 | } 126 | 127 | return _bip85_bip39(language, word_cnt, index); 128 | } 129 | 130 | static void 131 | _bip85_pwd_base85_command_usage(void) 132 | { 133 | fputs("usage: btct bip85.pwd_base85 \n", stderr); 134 | fputs("\n", stderr); 135 | fputs("Generates a new password from deterministic entropy using base85 encoding.\n", stderr); 136 | fputs("\n", stderr); 137 | fputs(" -l, --length Specify the length of password, default is 12.\n", stderr); 138 | fputs(" -i, --index Specify the index for the password, default is 0\n", stderr); 139 | fputs("\n", stderr); 140 | fputs(" Generate a password with length 12 from index 1\n", stderr); 141 | fputs("\n", stderr); 142 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n",stderr); 143 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 144 | fputs(" | btct bip32.masterkey\\\n", stderr); 145 | fputs(" | btct bip85.pwd_base85 --length=12 --index=1\n", stderr); 146 | fputs("\n", stderr); 147 | } 148 | 149 | static int 150 | _bip85_pwd_base85(uint32_t length, uint32_t index) 151 | { 152 | bip32_key_t key; 153 | char buf[512]; 154 | 155 | if (_bip85_read_private_key_from_stdin(&key) != 0) 156 | return EXIT_FAILURE; 157 | 158 | if (bip85_application_pwd_base85(&key, length, index, buf) != 0) 159 | return EXIT_FAILURE; 160 | 161 | fprintf(stdout, "%s\n", buf); 162 | 163 | return EXIT_SUCCESS; 164 | } 165 | 166 | static int 167 | _bip85_pwd_base85_command(int argc, char **argv) 168 | { 169 | int c; 170 | uint32_t length = 12; 171 | uint32_t index = 0; 172 | 173 | while (1) 174 | { 175 | int option_index = 0; 176 | static struct option long_options[] = { 177 | {"help", no_argument, 0, 'h' }, 178 | {"length", required_argument, 0, 'l' }, 179 | {"index", required_argument, 0, 'i' }, 180 | {0, 0, 0, 0} 181 | }; 182 | 183 | c = getopt_long(argc, argv, "hl:i:", long_options, &option_index); 184 | if (c == -1) 185 | break; 186 | 187 | switch (c) { 188 | case 'h': 189 | _bip85_pwd_base85_command_usage(); 190 | return EXIT_FAILURE; 191 | 192 | case 'l': 193 | length = atoi(optarg); 194 | break; 195 | 196 | case 'i': 197 | index = atoi(optarg); 198 | break; 199 | } 200 | } 201 | 202 | return _bip85_pwd_base85(length, index); 203 | } 204 | 205 | static void 206 | _bip85_hd_seed_wif_command_usage(void) 207 | { 208 | fputs("usage: btct bip85.hd_seed_wif \n", stderr); 209 | fputs("\n", stderr); 210 | fputs("Generates a HD Seed WIF from deterministic entropy for Bitcoin Core wallets.\n", stderr); 211 | fputs("\n", stderr); 212 | fputs(" -i, --index Specify the index for the password, default is 0\n", stderr); 213 | fputs("\n", stderr); 214 | fputs(" Generate HD Seed WIF wallet using index 2\n", stderr); 215 | fputs("\n", stderr); 216 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n",stderr); 217 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 218 | fputs(" | btct bip32.masterkey\\\n", stderr); 219 | fputs(" | btct bip85.hd_seed_wif --index=2\n", stderr); 220 | fputs("\n", stderr); 221 | } 222 | 223 | static int 224 | _bip85_hd_seed_wif(uint32_t index) 225 | { 226 | bip32_key_t key; 227 | char buf[512]; 228 | size_t size; 229 | 230 | if (_bip85_read_private_key_from_stdin(&key) != 0) 231 | return EXIT_FAILURE; 232 | 233 | if (bip85_application_hd_seed_wif(&key, index, buf, &size) != 0) 234 | return EXIT_FAILURE; 235 | 236 | fprintf(stdout, "%s\n", buf); 237 | 238 | return EXIT_SUCCESS; 239 | } 240 | 241 | static int 242 | _bip85_hd_seed_wif_command(int argc, char **argv) 243 | { 244 | int c; 245 | uint32_t index = 0; 246 | 247 | while (1) 248 | { 249 | int option_index = 0; 250 | static struct option long_options[] = { 251 | {"help", no_argument, 0, 'h' }, 252 | {"index", required_argument, 0, 'i' }, 253 | {0, 0, 0, 0} 254 | }; 255 | 256 | c = getopt_long(argc, argv, "hi:", long_options, &option_index); 257 | if (c == -1) 258 | break; 259 | 260 | switch (c) { 261 | case 'h': 262 | _bip85_hd_seed_wif_command_usage(); 263 | return EXIT_FAILURE; 264 | 265 | case 'i': 266 | index = atoi(optarg); 267 | break; 268 | } 269 | } 270 | 271 | return _bip85_hd_seed_wif(index); 272 | } 273 | 274 | static void _bip85_command_usage(void) 275 | { 276 | fputs("usage: btct bip85. \n", stderr); 277 | fputs("\n", stderr); 278 | fputs(" bip39 Derive a deterministic mneonmic seed phrase.\n", stderr); 279 | fputs(" hd_seed_wif Derive a HD Seed for Bitcoin Core wallets.\n", stderr); 280 | fputs(" pwd_base85 Derive a deterministic password.\n", stderr); 281 | fputs("\n",stderr); 282 | fputs("examples:\n", stderr); 283 | fputs("\n",stderr); 284 | fputs(" Derive a deterministics mnenomic seed phrase, english, 12 words:\n",stderr); 285 | fputs("\n",stderr); 286 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' \\\n", stderr); 287 | fputs(" | btct bip39.seed --passphrase=TREZOR \\\n", stderr); 288 | fputs(" | btct bip32.masterkey \\\n", stderr); 289 | fputs(" | btct bip85.bip39 \n", stderr); 290 | fputs("\n", stderr); 291 | } 292 | 293 | int bip85_command(int argc, char **argv) 294 | { 295 | int res; 296 | 297 | struct command_t commands[] = { 298 | { "bip85.bip39", _bip85_bip39_command }, 299 | { "bip85.pwd_base85", _bip85_pwd_base85_command }, 300 | { "bip85.hd_seed_wif", _bip85_hd_seed_wif_command }, 301 | { NULL, NULL, } 302 | }; 303 | 304 | res = command_dispatch(commands, argv[0], false, argc, argv); 305 | if (res == -1) 306 | _bip85_command_usage(); 307 | 308 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 309 | } 310 | -------------------------------------------------------------------------------- /src/store_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "command.h" 11 | #include "bip32.h" 12 | #include "bip39.h" 13 | 14 | static 15 | int _input(const char *prompt, bool echo, char *result, size_t size) 16 | { 17 | char *res; 18 | struct termios term; 19 | 20 | if (!echo) { 21 | tcgetattr(fileno(stdin), &term); 22 | term.c_lflag &= ~ECHO; 23 | tcsetattr(fileno(stdin), 0, &term); 24 | } 25 | 26 | freopen(NULL, "rt", stdin); 27 | fprintf(stderr, "%s: ", prompt); 28 | res = fgets(result, size, stdin); 29 | 30 | if (!echo) { 31 | term.c_lflag |= ECHO; 32 | tcsetattr(fileno(stdin), 0, &term); 33 | fputs("\n", stderr); 34 | } 35 | 36 | if (res == NULL) 37 | return -1; 38 | 39 | return 0; 40 | } 41 | 42 | 43 | #define PROPOSE_CNT 5 44 | 45 | static int 46 | _store_init(const char *filename, uint32_t bits) 47 | { 48 | bip32_key_t key; 49 | bip39_t bip39; 50 | uint8_t seed[PROPOSE_CNT][32] = {0}; 51 | char buf[4096] = {0}; 52 | char password[512] = {0}; 53 | char **mnemonics; 54 | size_t mnemonics_cnt; 55 | 56 | if (bits != 128 && bits != 256) { 57 | fprintf(stderr, "store.init: Unsupported bit count %d, use 128 or 256bit\n", bits); 58 | return EXIT_FAILURE; 59 | } 60 | 61 | int h = open("/dev/urandom", O_RDONLY); 62 | for (size_t i = 0; i < PROPOSE_CNT; i++) 63 | read(h, seed[i], (bits/8)); 64 | close(h); 65 | 66 | bip39_init(&bip39); 67 | 68 | fputs("Here follows a list of 10 seeds from /dev/urandom, choose any of them by the number\n" 69 | "when prompt to be stored and used.\n" 70 | "\n" 71 | , stdout); 72 | 73 | // Present mnemonics for each seed generated to choose one of to use 74 | for (size_t i = 0; i < PROPOSE_CNT; i++) { 75 | // generate mnemonics to save to store 76 | if (bip39_to_mnemonics(&bip39, seed[i], bits, &mnemonics, &mnemonics_cnt) != 0) 77 | return EXIT_FAILURE; 78 | 79 | fprintf(stdout, " %2ld: " , 1+i); 80 | for (size_t i = 0; i < mnemonics_cnt; i++) { 81 | if (i != 0 && i % 8 == 0) fputs("\n ", stdout); 82 | 83 | fputs(mnemonics[i], stdout); 84 | 85 | if (i != (mnemonics_cnt - 1)) 86 | fputs(" ", stdout); 87 | } 88 | fputs("\n\n", stdout); 89 | } 90 | 91 | // prompt for choice 92 | redo_prompt: 93 | char *tmp; 94 | _input("Enter the number of seed to use", true, buf, sizeof(buf)); 95 | long choice = atoi(buf) - 1; 96 | if (choice < 0 || choice >= PROPOSE_CNT) 97 | goto redo_prompt; 98 | 99 | if (bip39_to_mnemonics(&bip39, seed[choice], bits, &mnemonics, &mnemonics_cnt) != 0) 100 | return EXIT_FAILURE; 101 | 102 | // prompt for password 103 | _input("Enter password for store", false, password, sizeof(password)); 104 | 105 | // generate mnemonics string 106 | memset(buf, 0, sizeof(buf)); 107 | for (size_t i = 0; i < mnemonics_cnt; i++) { 108 | strcat(buf, mnemonics[i]); 109 | if (i != mnemonics_cnt - 1) 110 | strcat(buf, " "); 111 | } 112 | 113 | if (store_write_mnemonics(filename, password, buf, strlen(buf)) != 0) 114 | { 115 | fprintf(stderr, "failed to store into file %s\n", filename); 116 | return EXIT_FAILURE; 117 | } 118 | 119 | return EXIT_SUCCESS; 120 | } 121 | 122 | static void 123 | _store_init_command_usage(void) 124 | { 125 | fputs("usage: btct store.init \n", stderr); 126 | fputs("\n", stderr); 127 | fputs(" -b, --bits Specify bits of entropy, 128bits = 12 words, 256bits = 24 words. Default\n", stderr); 128 | fputs(" is 128bits entropy, which creates a 12 word mnemoninc seed phrase.\n", stderr); 129 | fputs(" -f, --file Specify a file for the encrypted store with generated wallets.\n", stderr); 130 | fputs(" Default store file is ~/btct_store.dat.\n", stderr); 131 | fputs("\n", stderr); 132 | fputs("examples:\n", stderr); 133 | fputs("\n", stderr); 134 | fputs(" Create a random seed of 16 bytes eg. 256 bits seed, generate mnemonics and save to store\n", stderr); 135 | fputs("\n", stderr); 136 | fputs(" head -c32 /dev/urandom \\\n", stderr); 137 | fputs(" | btct bip39.mnemonics --sentence \\\n", stderr); 138 | fputs(" | btct store.init", stderr); 139 | fputs("\n", stderr); 140 | } 141 | 142 | static int 143 | _store_init_command(int argc, char **argv) 144 | { 145 | int c; 146 | uint32_t bits = 128; 147 | const char *filename = NULL; 148 | 149 | while (1) 150 | { 151 | int option_index = 0; 152 | static struct option long_options[] = { 153 | {"help", no_argument, 0, 'h' }, 154 | {"bits", no_argument, 0, 'b' }, 155 | {"filename", no_argument, 0, 'f' }, 156 | {0, 0, 0, 0} 157 | }; 158 | 159 | c = getopt_long(argc, argv, "hf:b:", long_options, &option_index); 160 | if (c == -1) 161 | break; 162 | 163 | switch (c) { 164 | case 'h': 165 | _store_init_command_usage(); 166 | return EXIT_FAILURE; 167 | 168 | case 'f': 169 | filename = optarg; 170 | break; 171 | 172 | case 'b': 173 | bits = atoi(optarg); 174 | break; 175 | } 176 | } 177 | 178 | return _store_init(filename, bits); 179 | } 180 | 181 | static int 182 | _store_read(const char *filename) 183 | { 184 | char mnemonics[2048] = {0}; 185 | size_t size = sizeof(mnemonics); 186 | char password[256]={0}; 187 | 188 | // prompt for password 189 | _input("Enter password for store", false, password, sizeof(password)); 190 | 191 | if (store_read_mnemonics(filename, password, mnemonics, &size) != 0) 192 | return EXIT_FAILURE; 193 | 194 | fputs(mnemonics, stdout); 195 | 196 | return EXIT_SUCCESS; 197 | } 198 | 199 | static void 200 | _store_read_command_usage(void) 201 | { 202 | fputs("usage: btct store.read \n", stderr); 203 | fputs("\n", stderr); 204 | fputs(" -f, --file Specify a file for the encrypted store to read from.\n", stderr); 205 | fputs(" Default store file is ~/.btct.dat.\n", stderr); 206 | fputs("\n", stderr); 207 | fputs("examples:\n", stderr); 208 | fputs("\n", stderr); 209 | fputs(" Create a random seed of 16 bytes eg. 256 bits seed, generate mnemonics and save to store\n", stderr); 210 | fputs("\n", stderr); 211 | fputs(" btct store.read \\\n", stderr); 212 | fputs(" | btct bip32.masterkey\n", stderr); 213 | fputs("\n", stderr); 214 | } 215 | 216 | static int 217 | _store_read_command(int argc, char **argv) 218 | { 219 | int c; 220 | const char *filename = NULL; 221 | 222 | while (1) 223 | { 224 | int option_index = 0; 225 | static struct option long_options[] = { 226 | {"help", no_argument, 0, 'h' }, 227 | {"filename", no_argument, 0, 'f' }, 228 | {0, 0, 0, 0} 229 | }; 230 | 231 | c = getopt_long(argc, argv, "hf:", long_options, &option_index); 232 | if (c == -1) 233 | break; 234 | 235 | switch (c) { 236 | case 'h': 237 | _store_read_command_usage(); 238 | return EXIT_FAILURE; 239 | 240 | case 'f': 241 | filename = optarg; 242 | break; 243 | } 244 | } 245 | 246 | return _store_read(filename); 247 | } 248 | 249 | static int 250 | _store_import(const char *filename) 251 | { 252 | char password[256]={0}; 253 | uint8_t mnemonics[4096] = {0}; 254 | 255 | // prompt for mnemonics 256 | _input("Enter mnemonics seed phrase", true, mnemonics, sizeof(mnemonics)); 257 | 258 | // prompt for password 259 | _input("Enter password for store", false, password, sizeof(password)); 260 | 261 | // write seed to store 262 | if (store_write_mnemonics(filename, password, mnemonics, strlen(mnemonics)) != 0) 263 | { 264 | fprintf(stderr, "failed to store into file %s\n", filename); 265 | return EXIT_FAILURE; 266 | } 267 | 268 | return EXIT_SUCCESS; 269 | } 270 | 271 | static void 272 | _store_import_command_usage(void) 273 | { 274 | fputs("usage: btct store.import \n", stderr); 275 | fputs("\n", stderr); 276 | fputs("Import a mnemonic seed phrase into the store instead of generating a new one as when\n", stderr); 277 | fputs("using `store.init`, any 12, 24 words seed phrase can be used, for now english words\n", stderr); 278 | fputs("are required.\n", stderr); 279 | fputs("\n", stderr); 280 | fputs(" -f, --file Specify a file for the encrypted store to read from.\n", stderr); 281 | fputs(" Default store file is ~/.btct.dat.\n", stderr); 282 | fputs("\n", stderr); 283 | } 284 | 285 | static int 286 | _store_import_command(int argc, char **argv) 287 | { 288 | int c; 289 | const char *filename = NULL; 290 | 291 | while (1) 292 | { 293 | int option_index = 0; 294 | static struct option long_options[] = { 295 | {"help", no_argument, 0, 'h' }, 296 | {"filename", no_argument, 0, 'f' }, 297 | {0, 0, 0, 0} 298 | }; 299 | 300 | c = getopt_long(argc, argv, "hf:", long_options, &option_index); 301 | if (c == -1) 302 | break; 303 | 304 | switch (c) { 305 | case 'h': 306 | _store_import_command_usage(); 307 | return EXIT_FAILURE; 308 | 309 | case 'f': 310 | filename = optarg; 311 | break; 312 | } 313 | } 314 | 315 | return _store_import(filename); 316 | } 317 | 318 | 319 | static void _store_command_usage(void) 320 | { 321 | fputs("usage: btct store. \n", stderr); 322 | fputs("\n", stderr); 323 | fputs(" init Initialize store with new generated mnemonics seed phrase.\n", stderr); 324 | fputs(" import Import an existing seed phrase into store.\n", stderr); 325 | fputs(" read Read mnemonics sede phrase from store.\n", stderr); 326 | fputs("\n",stderr); 327 | fputs("examples:\n", stderr); 328 | fputs("\n",stderr); 329 | fputs(" Initialize store with a newly generate seed phrase:\n",stderr); 330 | fputs("\n",stderr); 331 | fputs(" btct store.init\n", stderr); 332 | fputs("\n", stderr); 333 | } 334 | 335 | int store_command(int argc, char **argv) 336 | { 337 | int res; 338 | 339 | struct command_t commands[] = { 340 | { "store.init", _store_init_command }, 341 | { "store.read", _store_read_command }, 342 | { "store.import", _store_import_command }, 343 | { NULL, NULL, } 344 | }; 345 | 346 | res = command_dispatch(commands, argv[0], false, argc, argv); 347 | if (res == -1) 348 | _store_command_usage(); 349 | 350 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 351 | } 352 | -------------------------------------------------------------------------------- /src/bip32.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "utils.h" 7 | #include "../external/libbase58/libbase58.h" 8 | #include "../external/secp256k1/include/secp256k1.h" 9 | 10 | #include "bip32.h" 11 | #include "secp256k1.h" 12 | 13 | static int 14 | _bip32_key_init(bip32_key_t *ctx, uint8_t *secret, uint8_t *chain, 15 | uint8_t depth, uint32_t index, 16 | uint8_t *fingerprint, bool public) 17 | { 18 | memset(ctx, 0, sizeof(bip32_key_t)); 19 | 20 | ctx->public = public; 21 | memcpy(ctx->key.private, secret, 32); 22 | memcpy(ctx->chain, chain, 32); 23 | ctx->depth = depth; 24 | ctx->index = index; 25 | memcpy(ctx->parent_fingerprint, fingerprint, 4); 26 | 27 | return 0; 28 | } 29 | 30 | int 31 | bip32_key_secp256k1_serialize_public_key(const bip32_key_t *ctx, bool compressed, uint8_t *result) 32 | { 33 | struct secp256k1_context *secp256k1; 34 | size_t pubkey_size = compressed ? 33 : 65; 35 | 36 | if (ctx->public == false) 37 | return -1; 38 | 39 | secp256k1 = secp256k1_context_create(SECP256K1_CONTEXT_NONE); 40 | secp256k1_context_randomize(secp256k1, 0); 41 | secp256k1_ec_pubkey_serialize(secp256k1, result, &pubkey_size, &ctx->key.public, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); 42 | secp256k1_context_destroy(secp256k1); 43 | 44 | return 0; 45 | } 46 | 47 | int 48 | bip32_key_init_private(bip32_key_t *ctx) 49 | { 50 | memset(ctx, 0, sizeof(bip32_key_t)); 51 | ctx->public = false; 52 | return 0; 53 | } 54 | 55 | int 56 | bip32_key_init_from_entropy(bip32_key_t *ctx, uint8_t *entropy, size_t size) 57 | { 58 | char *key = "Bitcoin seed"; 59 | struct hmac_sha512_ctx hmac_sha512; 60 | 61 | if (size != 64) 62 | return -1; 63 | 64 | uint8_t mac[64]; 65 | hmac_sha512_set_key(&hmac_sha512, strlen(key), (uint8_t *)key); 66 | hmac_sha512_update(&hmac_sha512, size, entropy); 67 | hmac_sha512_digest(&hmac_sha512, 64, mac); 68 | 69 | uint8_t *privkey = mac; 70 | uint8_t *chain = mac + 32; 71 | uint8_t fingerprint[] = { 0, 0, 0, 0 }; 72 | uint32_t index = 0x00; 73 | 74 | return _bip32_key_init(ctx, privkey, chain, 0, index, fingerprint, false); 75 | } 76 | 77 | int 78 | bip32_key_init_public_from_private_key(bip32_key_t *ctx, const bip32_key_t *private) 79 | { 80 | int res; 81 | struct secp256k1_context *secp256k1; 82 | struct secp256k1_pubkey pubkey; 83 | 84 | memcpy(ctx, private, sizeof(bip32_key_t)); 85 | ctx->public = true; 86 | memset(ctx->key.public, 0, sizeof(ctx->key.public)); 87 | 88 | secp256k1 = secp256k1_context_create(SECP256K1_CONTEXT_NONE); 89 | if (secp256k1_context_randomize(secp256k1, 0) != 1) 90 | return -1; 91 | 92 | if (secp256k1_ec_pubkey_create(secp256k1, &pubkey, private->key.private) != 1) 93 | return -2; 94 | 95 | memcpy(ctx->key.public, pubkey.data, sizeof(pubkey.data)); 96 | 97 | secp256k1_context_destroy(secp256k1); 98 | return 0; 99 | } 100 | 101 | #define TWO_TO_POWER_OF_31 ((uint32_t)2<<30) 102 | 103 | int 104 | bip32_key_derive_child_key(const bip32_key_t *parent, uint32_t index, bip32_key_t *child) { 105 | struct hmac_sha512_ctx hmac_sha512; 106 | uint8_t mac[64]; 107 | uint8_t zeros[10] = {0}; 108 | uint8_t tmp[1024]; 109 | 110 | memset(child, 0, sizeof(bip32_key_t)); 111 | 112 | if (parent->public) 113 | return -1; 114 | 115 | hmac_sha512_set_key(&hmac_sha512, sizeof(parent->chain), parent->chain); 116 | 117 | if (index >= TWO_TO_POWER_OF_31) 118 | { 119 | // Create hardened child key 120 | uint8_t *ptmp = tmp; 121 | *ptmp = 0x00; 122 | ptmp++; 123 | 124 | memcpy(ptmp, parent->key.private, 32); 125 | ptmp += 32; 126 | 127 | utils_out_u32_be(ptmp, index); 128 | ptmp += 4; 129 | 130 | hmac_sha512_update(&hmac_sha512, ptmp - tmp, tmp); 131 | } 132 | else 133 | { 134 | // Create non hardened child key 135 | uint8_t *ptmp = tmp; 136 | 137 | // serialize parent public key into buffer 138 | 139 | bip32_key_t parent_public_key; 140 | bip32_key_init_public_from_private_key(&parent_public_key, parent); 141 | 142 | if (bip32_key_secp256k1_serialize_public_key(&parent_public_key, true, ptmp) != 0) 143 | return -2; 144 | ptmp += 33; 145 | 146 | // serialize index 147 | utils_out_u32_be(ptmp, index); 148 | ptmp += 4; 149 | 150 | hmac_sha512_update(&hmac_sha512, ptmp - tmp, tmp); 151 | } 152 | 153 | hmac_sha512_digest(&hmac_sha512, 64, mac); 154 | 155 | // copy chain to child key chain from right part of mac 156 | memcpy(child->chain, mac + 32, 32); 157 | 158 | size_t counter=0; 159 | char buf[256] = {0}; 160 | mpz_t result, left, sec256k1_param_n, parent_key; 161 | 162 | mpz_init(left); 163 | mpz_import(left, 1, 1, 32, 1, 0, mac); 164 | mpz_init_set_str(sec256k1_param_n, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); 165 | mpz_init(parent_key); 166 | mpz_import(parent_key, 1, 1, 32, 1, 0, parent->key.private); 167 | 168 | mpz_init(result); 169 | mpz_add(result, left, parent_key); 170 | mpz_mod(result, result, sec256k1_param_n); 171 | mpz_export(child->key.private, &counter, 1, 32, 1, 0, result); 172 | 173 | child->public = false; 174 | child->depth = parent->depth + 1; 175 | child->index = index; 176 | 177 | bip32_key_identifier_t parent_key_ident; 178 | uint8_t parent_fingerprint[4]; 179 | if (bip32_key_identifier_init_from_key(parent_key_ident, parent) != 0) 180 | return -2; 181 | if (bip32_key_identifier_fingerprint(parent_key_ident, child->parent_fingerprint) != 0) 182 | return -3; 183 | 184 | return 0; 185 | } 186 | 187 | int 188 | bip32_key_derive_child_by_path(const bip32_key_t *ctx, const char *path, bip32_key_t *child) 189 | { 190 | bip32_key_t tmp, *current = ctx; 191 | bool hardened; 192 | uint32_t index; 193 | char *token, buffer[512]; 194 | snprintf(buffer, sizeof(buffer), path); 195 | 196 | // FIXME: for now only private key derive 197 | if (buffer[0] != 'm') 198 | return -1; 199 | 200 | token = strtok(buffer + 1, "/"); 201 | current = ctx; 202 | while(token) 203 | { 204 | hardened = false; 205 | if (token[strlen(token) - 1] == '\'') { 206 | hardened = true; 207 | token[strlen(token) - 1] = '\0'; 208 | } 209 | 210 | index = atoi(token); 211 | if (bip32_key_derive_child_key(current, hardened ? 0x80000000 + index : index, child) != 0) 212 | return -2; 213 | 214 | memcpy(&tmp, child, sizeof(bip32_key_t)); 215 | current = &tmp; 216 | 217 | token = strtok(NULL, "/"); 218 | } 219 | 220 | return 0; 221 | } 222 | 223 | 224 | static inline int 225 | _base58_checksum_encode(uint8_t *data, size_t size, 226 | uint8_t *result, size_t *result_size) 227 | { 228 | uint8_t checksum[4]; 229 | 230 | if (utils_sha256_checksum(data, size, checksum) != 0) 231 | return -1; 232 | 233 | memcpy(data + size, checksum, 4); 234 | 235 | return b58enc((char*)result, result_size, data, size + 4) ? 0 : -2; 236 | } 237 | 238 | int 239 | bip32_key_serialize(bip32_key_t *ctx, bool encoded, 240 | uint8_t *result, size_t *size) 241 | { 242 | uint8_t buf[512] = { 0 }; 243 | uint8_t version_private[4] = { 0x04, 0x88, 0xad, 0xe4 }; 244 | uint8_t version_public[4] = { 0x04, 0x88, 0xb2, 0x1e }; 245 | 246 | uint8_t *ptr = buf; 247 | memcpy(ptr, ctx->public == true ? version_public : version_private, 4); 248 | ptr += 4; 249 | 250 | *ptr = ctx->depth; 251 | ptr++; 252 | 253 | memcpy(ptr, ctx->parent_fingerprint, 4); 254 | ptr += 4; 255 | 256 | utils_out_u32_be(ptr, ctx->index); 257 | ptr += 4; 258 | 259 | memcpy(ptr, ctx->chain, 32); 260 | ptr += 32; 261 | 262 | if (ctx->public == false) 263 | { 264 | *ptr = 0x00; 265 | memcpy(ptr+1, ctx->key.private, 32); 266 | ptr += 33; 267 | } 268 | else 269 | { 270 | if (bip32_key_secp256k1_serialize_public_key(ctx, true, ptr) != 0) 271 | return -1; 272 | ptr += 33; 273 | } 274 | 275 | if (!encoded) 276 | { 277 | memcpy(result, buf, ptr-buf); 278 | *size = ptr-buf; 279 | return 0; 280 | } 281 | else 282 | { 283 | return _base58_checksum_encode(buf, ptr - buf, result, size); 284 | } 285 | 286 | return 0; 287 | } 288 | 289 | static inline int 290 | _base58_checksum_decode(const char *data, size_t size, 291 | uint8_t *result, size_t *result_size) 292 | { 293 | uint8_t checksum[4]; 294 | uint8_t calculated_checksum[4]; 295 | 296 | if (b58tobin(result, result_size, data, size) == false) 297 | return -1; 298 | 299 | memcpy(checksum, result + (*result_size - 4), 4); 300 | *result_size -= 4; 301 | 302 | if (utils_sha256_checksum(result, *result_size, calculated_checksum) != 0) 303 | return -2; 304 | 305 | if (memcmp(checksum, calculated_checksum, 4) != 0) 306 | return -3; 307 | 308 | return 0; 309 | } 310 | 311 | int 312 | bip32_key_deserialize(bip32_key_t *key, const char *encoded_key) 313 | { 314 | uint8_t buf[4 + 1 + 4 + 4 + 32 + 1 + 32 + 4] = { 0 }; 315 | size_t buf_size = sizeof(buf); 316 | uint8_t *pbuf = buf; 317 | 318 | uint8_t version_private[4] = { 0x04, 0x88, 0xad, 0xe4 }; 319 | uint8_t version_public[4] = { 0x04, 0x88, 0xb2, 0x1e }; 320 | 321 | if (_base58_checksum_decode(encoded_key, strlen(encoded_key), buf, &buf_size) != 0) 322 | return -1; 323 | 324 | memset(key, 0, sizeof(bip32_key_t)); 325 | 326 | // verify key version 327 | if (memcmp(version_public, pbuf, 4) == 0) 328 | key->public = true; 329 | else if (memcmp(version_private, pbuf, 4) == 0) 330 | key->public = false; 331 | else return -2; 332 | pbuf += 4; 333 | 334 | // get depth 335 | key->depth = *pbuf; 336 | pbuf++; 337 | 338 | // get parent key fingerprint 339 | memcpy(key->parent_fingerprint, pbuf, 4); 340 | pbuf += 4; 341 | 342 | // get index 343 | key->index = utils_in_u32_be(pbuf); 344 | pbuf += 4; 345 | 346 | // get chain 347 | memcpy(key->chain, pbuf, 32); 348 | pbuf += 32; 349 | 350 | // get key 351 | if (key->public == false) { 352 | pbuf++; 353 | memcpy(key->key.private, pbuf, 32); 354 | } 355 | else 356 | { 357 | int res; 358 | struct secp256k1_context *secp256k1; 359 | struct secp256k1_pubkey pubkey; 360 | secp256k1 = secp256k1_context_create(SECP256K1_CONTEXT_NONE); 361 | secp256k1_context_randomize(secp256k1, 0); 362 | if (secp256k1_ec_pubkey_parse(secp256k1, key->key.public, pbuf, 33) != 1) 363 | return -3; 364 | secp256k1_context_destroy(secp256k1); 365 | } 366 | 367 | return 0; 368 | } 369 | 370 | int 371 | bip32_key_to_wif(bip32_key_t *ctx, uint8_t *result, size_t *size) 372 | { 373 | uint8_t buf[512] = { 0 }; 374 | 375 | if (ctx->public) 376 | return 1; 377 | 378 | uint8_t *ptr = buf; 379 | *ptr = 0x80; 380 | ptr++; 381 | memcpy(ptr, ctx->key.private, 32); 382 | ptr += 32; 383 | *ptr = 0x01; 384 | ptr++; 385 | 386 | return _base58_checksum_encode(buf, ptr - buf, result, size); 387 | } 388 | 389 | 390 | int 391 | bip32_key_identifier_init_from_key(bip32_key_identifier_t ident, const bip32_key_t *key) 392 | { 393 | bip32_key_t public_key; 394 | 395 | if (key->public == false) 396 | { 397 | if (bip32_key_init_public_from_private_key(&public_key, key) != 0) 398 | return -1; 399 | } 400 | else 401 | memcpy(&public_key, key, sizeof(bip32_key_t)); 402 | 403 | memset(ident, 0, sizeof(bip32_key_identifier_t)); 404 | 405 | // serialize public key 406 | uint8_t serialized_public_key[33]; 407 | if (bip32_key_secp256k1_serialize_public_key(&public_key, true, serialized_public_key) != 0) 408 | return -2; 409 | 410 | if (utils_hash160(serialized_public_key, sizeof(serialized_public_key), ident) != 0) 411 | return -3; 412 | 413 | return 0; 414 | } 415 | 416 | int 417 | bip32_key_identifier_fingerprint(const bip32_key_identifier_t ident, uint8_t *fingerprint) 418 | { 419 | memcpy(fingerprint, ident, 4); 420 | return 0; 421 | } 422 | 423 | int 424 | bip32_key_p2pkh_address_from_key(const bip32_key_t *ctx, uint8_t *address, size_t *size) 425 | { 426 | uint8_t buf[256] = {0}; 427 | bip32_key_t *public_key, tmp; 428 | 429 | public_key = ctx; 430 | 431 | // derive public key if private 432 | if (ctx->public == false) 433 | { 434 | if (bip32_key_init_public_from_private_key(&tmp, ctx) != 0) 435 | return -1; 436 | public_key = &tmp; 437 | } 438 | 439 | // serialize uncompressed public key 440 | uint8_t serialized_public_key[33]={0}; 441 | if (bip32_key_secp256k1_serialize_public_key(public_key, true, serialized_public_key) != 0) 442 | return -2; 443 | 444 | if (utils_hash160(serialized_public_key, sizeof(serialized_public_key), buf + 1) != 0) 445 | return -3; 446 | 447 | // calculate checksum of extended key version: 448 | buf[0] = 0x00; 449 | if (utils_sha256_checksum(buf, 1 + RIPEMD160_DIGEST_SIZE, buf + 1 + RIPEMD160_DIGEST_SIZE) != 0) 450 | return -4; 451 | 452 | return b58enc((char*)address, size, buf, 1 + RIPEMD160_DIGEST_SIZE + 4) ? 0 : -5; 453 | } 454 | -------------------------------------------------------------------------------- /src/bip32_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "command.h" 7 | #include "bip32.h" 8 | #include "utils.h" 9 | 10 | static int 11 | _bip32_masterkey(bool encode, bool wif) 12 | { 13 | uint8_t seed[128]={0}; 14 | size_t bytes = 0; 15 | 16 | // Read entrophy bits from stdin 17 | freopen(NULL, "rb", stdin); 18 | while (fread(seed + bytes, 1, 1, stdin)) 19 | bytes++; 20 | 21 | fprintf(stderr,"bip39.masterkey: read %ld bits of seed from stdin to use for creating hierarchical deterministic master key\n", bytes*8); 22 | 23 | char buffer[4096]; 24 | size_t size = sizeof(buffer); 25 | 26 | bip32_key_t ctx; 27 | bip32_key_init_from_entropy(&ctx, seed, bytes); 28 | 29 | freopen(NULL, "wb", stdout); 30 | if (!wif) 31 | { 32 | bip32_key_serialize(&ctx, encode, (uint8_t*)buffer, &size); 33 | fwrite(buffer, 1, size, stdout); 34 | if (encode) 35 | fputs("\n", stdout); 36 | } 37 | else 38 | { 39 | bip32_key_to_wif(&ctx, (uint8_t*)buffer, &size); 40 | fwrite(buffer, 1, size, stdout); 41 | } 42 | 43 | return EXIT_SUCCESS; 44 | } 45 | 46 | static void 47 | _bip32_masterkey_usage(void) 48 | { 49 | fputs("usage: btct bip32.masterkey \n", stderr); 50 | fputs("\n", stderr); 51 | fputs(" -p, --plain Do not perform base58 encoding of key\n", stderr); 52 | fputs(" -w, --wif Wallet import format\n", stderr); 53 | fputs("\n", stderr); 54 | fputs("examples:\n", stderr); 55 | fputs("\n", stderr); 56 | fputs(" Create a HD wallet master key from mnemonics and generate a QR code:\n", stderr); 57 | fputs("\n", stderr); 58 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 59 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 60 | fputs(" btct bip32.masterkey | qrencode -lH -o - -t ANSI256\n", stderr); 61 | fputs("\n", stderr); 62 | } 63 | 64 | static int 65 | _bip32_masterkey_command(int argc, char **argv) 66 | { 67 | int c; 68 | bool encode = true; 69 | bool wif = false; 70 | while (1) 71 | { 72 | int option_index = 0; 73 | static struct option long_options[] = { 74 | {"help", no_argument, 0, 'h' }, 75 | {"plain", no_argument, 0, 'p' }, 76 | {"wif", no_argument, 0, 'w' }, 77 | {0, 0, 0, 0} 78 | }; 79 | 80 | c = getopt_long(argc, argv, "hpw", long_options, &option_index); 81 | if (c == -1) 82 | break; 83 | 84 | switch (c) { 85 | case 'h': 86 | _bip32_masterkey_usage(); 87 | return EXIT_FAILURE; 88 | 89 | case 'p': 90 | encode = false; 91 | break; 92 | 93 | case 'w': 94 | wif = true; 95 | break; 96 | } 97 | } 98 | 99 | return _bip32_masterkey(encode, wif); 100 | } 101 | 102 | static int 103 | _bip32_derive_key(const char *path) 104 | { 105 | bip32_key_t key, child; 106 | char encoded_key[4096]={0}; 107 | uint8_t buf[512] = {0}; 108 | size_t bytes = 0; 109 | 110 | freopen(NULL, "rb", stdin); 111 | while (fread( encoded_key + bytes, 1, 1, stdin)) 112 | bytes++; 113 | 114 | // strip newline 115 | if (encoded_key[bytes-1] == '\n') { 116 | encoded_key[bytes-1] = '\0'; 117 | bytes--; 118 | } 119 | 120 | if (bip32_key_deserialize(&key, encoded_key) != 0) 121 | { 122 | fputs("bip32.derive: Failed to deserialize key from stdin\n", stderr); 123 | return -1; 124 | } 125 | 126 | fprintf(stderr,"bip32.derive: Deriving key from path: %s\n", path); 127 | if (bip32_key_derive_child_by_path(&key, path, &child) != 0) 128 | return -2; 129 | 130 | bytes = sizeof(buf); 131 | bip32_key_serialize(&child, true, buf, &bytes); 132 | 133 | fprintf(stdout, "%s\n", buf); 134 | return 0; 135 | } 136 | 137 | static void 138 | _bip32_derive_usage(void) 139 | { 140 | fputs("usage: btct bip32.derive \n", stderr); 141 | fputs("\n", stderr); 142 | fputs("", stderr); 143 | fputs("\n", stderr); 144 | fputs(" -p, --path Specify a derivation path, default path if not specified is\n", stderr); 145 | fputs(" following hardened årivate key for wallet account 0: `m/0'/0`.\n", stderr); 146 | fputs("\n", stderr); 147 | fputs("examples:\n", stderr); 148 | fputs("\n", stderr); 149 | fputs(" Create a HD wallet master key from mnemonics and derive a hardened wallet key for account #1:\n", stderr); 150 | fputs("\n", stderr); 151 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 152 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 153 | fputs(" btct bip32.masterkey | \\\n", stderr); 154 | fputs(" btct bip32.derive --path=\"m/0'/1\"\\\n", stderr); 155 | fputs("\n", stderr); 156 | } 157 | 158 | static int 159 | _bip32_derive_command(int argc, char **argv) { 160 | int c; 161 | const char *path; 162 | while (1) 163 | { 164 | int option_index = 0; 165 | static struct option long_options[] = { 166 | {"help", no_argument, 0, 'h' }, 167 | {"path", required_argument, 0, 'p' }, 168 | {0, 0, 0, 0} 169 | }; 170 | 171 | c = getopt_long(argc, argv, "hp", long_options, &option_index); 172 | if (c == -1) 173 | break; 174 | 175 | switch (c) { 176 | case 'h': 177 | _bip32_derive_usage(); 178 | return EXIT_FAILURE; 179 | 180 | case 'p': 181 | path = optarg; 182 | break; 183 | } 184 | } 185 | 186 | return _bip32_derive_key(path); 187 | } 188 | 189 | static int 190 | _bip32_pubkey() 191 | { 192 | bip32_key_t key, public; 193 | char encoded_key[4096]={0}; 194 | uint8_t buf[512] = {0}; 195 | size_t bytes = 0; 196 | 197 | freopen(NULL, "rb", stdin); 198 | while (fread( encoded_key + bytes, 1, 1, stdin)) 199 | bytes++; 200 | 201 | // strip newline 202 | if (encoded_key[bytes-1] == '\n') { 203 | encoded_key[bytes-1] = '\0'; 204 | bytes--; 205 | } 206 | 207 | if (bip32_key_deserialize(&key, encoded_key) != 0) 208 | { 209 | fputs("bip32.pubkey: Failed to deserialize key from stdin\n", stderr); 210 | return -1; 211 | } 212 | 213 | if (key.public == true) 214 | { 215 | fputs("bip32.pubkey: Failed, not a private key\n", stderr); 216 | return -2; 217 | } 218 | 219 | if (bip32_key_init_public_from_private_key(&public, &key) != 0) 220 | return -3; 221 | 222 | bytes = sizeof(buf); 223 | bip32_key_serialize(&public, true, buf, &bytes); 224 | 225 | fprintf(stdout, "%s\n", buf); 226 | return 0; 227 | } 228 | 229 | static void 230 | _bip32_pubkey_usage(void) 231 | { 232 | fputs("usage: btct bip32.pubkey\n", stderr); 233 | fputs("\n", stderr); 234 | fputs("Reads a encoded private key and outputs its corresponding public key in encoded format.\n", stderr); 235 | fputs("\n", stderr); 236 | fputs("examples:\n", stderr); 237 | fputs("\n", stderr); 238 | fputs(" Create a HD wallet master key from mnemonics and print its public key:\n", stderr); 239 | fputs("\n", stderr); 240 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 241 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 242 | fputs(" btct bip32.masterkey | \\\n", stderr); 243 | fputs(" btct bip32.pubkey\n", stderr); 244 | fputs("\n", stderr); 245 | } 246 | 247 | static int 248 | _bip32_pubkey_command(int argc, char **argv) 249 | { 250 | int c; 251 | while (1) 252 | { 253 | int option_index = 0; 254 | static struct option long_options[] = { 255 | {"help", no_argument, 0, 'h' }, 256 | {0, 0, 0, 0} 257 | }; 258 | 259 | c = getopt_long(argc, argv, "hp", long_options, &option_index); 260 | if (c == -1) 261 | break; 262 | 263 | switch (c) { 264 | case 'h': 265 | _bip32_pubkey_usage(); 266 | return EXIT_FAILURE; 267 | } 268 | } 269 | 270 | return _bip32_pubkey(); 271 | } 272 | 273 | static int 274 | _bip32_describe(bool show_private) 275 | { 276 | bip32_key_t *pkey, public_key, key; 277 | bip32_key_identifier_t identifier; 278 | uint8_t fingerprint[4] = {0}; 279 | char encoded_key[4096]={0}; 280 | uint8_t buf[512] = {0}; 281 | size_t bytes = 0; 282 | 283 | freopen(NULL, "rb", stdin); 284 | while (fread( encoded_key + bytes, 1, 1, stdin)) 285 | bytes++; 286 | 287 | // strip newline 288 | if (encoded_key[bytes-1] == '\n') { 289 | encoded_key[bytes-1] = '\0'; 290 | bytes--; 291 | } 292 | 293 | if (bip32_key_deserialize(&key, encoded_key) != 0) 294 | { 295 | fputs("bip32.describe: failed to deserialize key from stdin\n", stderr); 296 | return EXIT_FAILURE; 297 | } 298 | pkey = &key; 299 | 300 | if (!show_private && !pkey->public) { 301 | if (bip32_key_init_public_from_private_key(&public_key, &key) != 0) 302 | return EXIT_FAILURE; 303 | pkey = &public_key; 304 | 305 | bytes = sizeof(encoded_key); 306 | if (bip32_key_serialize(pkey, true, (uint8_t *)encoded_key, &bytes) != 0) 307 | return EXIT_FAILURE; 308 | } 309 | 310 | bip32_key_identifier_init_from_key(identifier, pkey); 311 | bip32_key_identifier_fingerprint(identifier, fingerprint); 312 | 313 | fputs( pkey->public ? "\n" : "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", stdout); 314 | fprintf(stdout, " %s\n" , encoded_key); 315 | fputs( pkey->public ? "\n" : "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", stdout); 316 | 317 | fprintf(stdout, " Fingerprint: %.2x%.2x%.2x%.2x\n", 318 | fingerprint[0], fingerprint[1], 319 | fingerprint[2], fingerprint[3]); 320 | 321 | if (pkey->public) { 322 | uint8_t serialized_public_key[33]={0}; 323 | if (bip32_key_secp256k1_serialize_public_key(pkey, true, serialized_public_key) != 0) 324 | return -2; 325 | utils_to_hex_string(serialized_public_key, sizeof(serialized_public_key), (char*)buf); 326 | } else { 327 | bytes = sizeof(buf); 328 | if (bip32_key_to_wif(pkey, buf, &bytes) != 0) 329 | return -3; 330 | } 331 | fprintf(stdout, "%20s: %s\n", pkey->public ? "Key" : "Key (WIF)", buf); 332 | 333 | bytes = sizeof(buf); 334 | bip32_key_p2pkh_address_from_key(pkey, buf, &bytes); 335 | fprintf(stdout, " Address (P2PKH): %s\n", buf); 336 | 337 | fprintf(stdout, " Parent fingerprint: %.2x%.2x%.2x%.2x\n", 338 | key.parent_fingerprint[0], key.parent_fingerprint[1], 339 | key.parent_fingerprint[2], key.parent_fingerprint[3]); 340 | 341 | fputs("\n", stdout); 342 | return EXIT_SUCCESS; 343 | } 344 | 345 | static void 346 | _bip32_describe_usage(void) 347 | { 348 | fputs("usage: btct bip32.describe\n", stderr); 349 | fputs("\n", stderr); 350 | fputs("Deserialize an extended key and prints out detailes about the the key, this will reveal\n", stderr); 351 | fputs("sensitive data as extended private key.\n", stderr); 352 | fputs("\n", stderr); 353 | fputs(" -p, --private Describe the a private extended key, default behaviour is to derive\n",stderr); 354 | fputs(" the public extended key and describe to prevent to disclose private\n", stderr); 355 | fputs(" data to the console.\n", stderr); 356 | fputs("\n", stderr); 357 | fputs("examples:\n", stderr); 358 | fputs("\n", stderr); 359 | fputs(" Create a HD wallet master key from mnemonics and describe private details to console:\n", stderr); 360 | fputs("\n", stderr); 361 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 362 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 363 | fputs(" btct bip32.masterkey | \\\n", stderr); 364 | fputs(" btct bip32.describe --private\n", stderr); 365 | fputs("\n", stderr); 366 | } 367 | 368 | static int 369 | _bip32_describe_command(int argc, char **argv) { 370 | int c; 371 | bool show_private = false; 372 | while (1) 373 | { 374 | int option_index = 0; 375 | static struct option long_options[] = { 376 | {"help", no_argument, 0, 'h' }, 377 | {"private", no_argument, 0, 'p' }, 378 | {0, 0, 0, 0} 379 | }; 380 | 381 | c = getopt_long(argc, argv, "hp", long_options, &option_index); 382 | if (c == -1) 383 | break; 384 | 385 | switch (c) { 386 | case 'h': 387 | _bip32_describe_usage(); 388 | return EXIT_FAILURE; 389 | case 'p': 390 | show_private = true; 391 | } 392 | } 393 | 394 | return _bip32_describe(show_private); 395 | } 396 | 397 | static void _bip32_command_usage(void) 398 | { 399 | fputs("usage: btct bip32. \n", stderr); 400 | fputs("\n", stderr); 401 | fputs(" masterkey Generate hierarchical deterministic master key\n", stderr); 402 | fputs(" pubkey Generate public key for private key in encoded format\n", stderr); 403 | fputs(" read from stdin.\n", stderr); 404 | fputs(" derive Derive a key from specified derivation path\n", stderr); 405 | fputs(" describe Deserialize an extended key and prints information\n", stderr); 406 | fputs("\n",stderr); 407 | fputs("examples:\n", stderr); 408 | fputs("\n",stderr); 409 | fputs(" Generate bip32 serialized HD wallet master key from mnemonics:\n",stderr); 410 | fputs("\n",stderr); 411 | fputs(" echo 'legal winner thank year wave sausage worth useful legal winner thank yellow' | \\\n", stderr); 412 | fputs(" btct bip39.seed --passphrase=TREZOR | \\\n", stderr); 413 | fputs(" btct bip32.masterkey\n", stderr); 414 | fputs("\n", stderr); 415 | } 416 | 417 | int bip32_command(int argc, char **argv) 418 | { 419 | int res; 420 | 421 | struct command_t commands[] = { 422 | { "bip32.masterkey", _bip32_masterkey_command }, 423 | { "bip32.pubkey", _bip32_pubkey_command }, 424 | { "bip32.derive", _bip32_derive_command }, 425 | { "bip32.describe", _bip32_describe_command }, 426 | { NULL, NULL, } 427 | }; 428 | 429 | res = command_dispatch(commands, argv[0], false, argc, argv); 430 | if (res == -1) 431 | _bip32_command_usage(); 432 | 433 | return (res != EXIT_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS); 434 | } 435 | -------------------------------------------------------------------------------- /src/bip39_english.h: -------------------------------------------------------------------------------- 1 | static const char *bip39_english[] = {"abandon","ability","able","about","above","absent","absorb","abstract","absurd","abuse","access","accident","account","accuse","achieve","acid","acoustic","acquire","across","act","action","actor","actress","actual","adapt","add","addict","address","adjust","admit","adult","advance","advice","aerobic","affair","afford","afraid","again","age","agent","agree","ahead","aim","air","airport","aisle","alarm","album","alcohol","alert","alien","all","alley","allow","almost","alone","alpha","already","also","alter","always","amateur","amazing","among","amount","amused","analyst","anchor","ancient","anger","angle","angry","animal","ankle","announce","annual","another","answer","antenna","antique","anxiety","any","apart","apology","appear","apple","approve","april","arch","arctic","area","arena","argue","arm","armed","armor","army","around","arrange","arrest","arrive","arrow","art","artefact","artist","artwork","ask","aspect","assault","asset","assist","assume","asthma","athlete","atom","attack","attend","attitude","attract","auction","audit","august","aunt","author","auto","autumn","average","avocado","avoid","awake","aware","away","awesome","awful","awkward","axis","baby","bachelor","bacon","badge","bag","balance","balcony","ball","bamboo","banana","banner","bar","barely","bargain","barrel","base","basic","basket","battle","beach","bean","beauty","because","become","beef","before","begin","behave","behind","believe","below","belt","bench","benefit","best","betray","better","between","beyond","bicycle","bid","bike","bind","biology","bird","birth","bitter","black","blade","blame","blanket","blast","bleak","bless","blind","blood","blossom","blouse","blue","blur","blush","board","boat","body","boil","bomb","bone","bonus","book","boost","border","boring","borrow","boss","bottom","bounce","box","boy","bracket","brain","brand","brass","brave","bread","breeze","brick","bridge","brief","bright","bring","brisk","broccoli","broken","bronze","broom","brother","brown","brush","bubble","buddy","budget","buffalo","build","bulb","bulk","bullet","bundle","bunker","burden","burger","burst","bus","business","busy","butter","buyer","buzz","cabbage","cabin","cable","cactus","cage","cake","call","calm","camera","camp","can","canal","cancel","candy","cannon","canoe","canvas","canyon","capable","capital","captain","car","carbon","card","cargo","carpet","carry","cart","case","cash","casino","castle","casual","cat","catalog","catch","category","cattle","caught","cause","caution","cave","ceiling","celery","cement","census","century","cereal","certain","chair","chalk","champion","change","chaos","chapter","charge","chase","chat","cheap","check","cheese","chef","cherry","chest","chicken","chief","child","chimney","choice","choose","chronic","chuckle","chunk","churn","cigar","cinnamon","circle","citizen","city","civil","claim","clap","clarify","claw","clay","clean","clerk","clever","click","client","cliff","climb","clinic","clip","clock","clog","close","cloth","cloud","clown","club","clump","cluster","clutch","coach","coast","coconut","code","coffee","coil","coin","collect","color","column","combine","come","comfort","comic","common","company","concert","conduct","confirm","congress","connect","consider","control","convince","cook","cool","copper","copy","coral","core","corn","correct","cost","cotton","couch","country","couple","course","cousin","cover","coyote","crack","cradle","craft","cram","crane","crash","crater","crawl","crazy","cream","credit","creek","crew","cricket","crime","crisp","critic","crop","cross","crouch","crowd","crucial","cruel","cruise","crumble","crunch","crush","cry","crystal","cube","culture","cup","cupboard","curious","current","curtain","curve","cushion","custom","cute","cycle","dad","damage","damp","dance","danger","daring","dash","daughter","dawn","day","deal","debate","debris","decade","december","decide","decline","decorate","decrease","deer","defense","define","defy","degree","delay","deliver","demand","demise","denial","dentist","deny","depart","depend","deposit","depth","deputy","derive","describe","desert","design","desk","despair","destroy","detail","detect","develop","device","devote","diagram","dial","diamond","diary","dice","diesel","diet","differ","digital","dignity","dilemma","dinner","dinosaur","direct","dirt","disagree","discover","disease","dish","dismiss","disorder","display","distance","divert","divide","divorce","dizzy","doctor","document","dog","doll","dolphin","domain","donate","donkey","donor","door","dose","double","dove","draft","dragon","drama","drastic","draw","dream","dress","drift","drill","drink","drip","drive","drop","drum","dry","duck","dumb","dune","during","dust","dutch","duty","dwarf","dynamic","eager","eagle","early","earn","earth","easily","east","easy","echo","ecology","economy","edge","edit","educate","effort","egg","eight","either","elbow","elder","electric","elegant","element","elephant","elevator","elite","else","embark","embody","embrace","emerge","emotion","employ","empower","empty","enable","enact","end","endless","endorse","enemy","energy","enforce","engage","engine","enhance","enjoy","enlist","enough","enrich","enroll","ensure","enter","entire","entry","envelope","episode","equal","equip","era","erase","erode","erosion","error","erupt","escape","essay","essence","estate","eternal","ethics","evidence","evil","evoke","evolve","exact","example","excess","exchange","excite","exclude","excuse","execute","exercise","exhaust","exhibit","exile","exist","exit","exotic","expand","expect","expire","explain","expose","express","extend","extra","eye","eyebrow","fabric","face","faculty","fade","faint","faith","fall","false","fame","family","famous","fan","fancy","fantasy","farm","fashion","fat","fatal","father","fatigue","fault","favorite","feature","february","federal","fee","feed","feel","female","fence","festival","fetch","fever","few","fiber","fiction","field","figure","file","film","filter","final","find","fine","finger","finish","fire","firm","first","fiscal","fish","fit","fitness","fix","flag","flame","flash","flat","flavor","flee","flight","flip","float","flock","floor","flower","fluid","flush","fly","foam","focus","fog","foil","fold","follow","food","foot","force","forest","forget","fork","fortune","forum","forward","fossil","foster","found","fox","fragile","frame","frequent","fresh","friend","fringe","frog","front","frost","frown","frozen","fruit","fuel","fun","funny","furnace","fury","future","gadget","gain","galaxy","gallery","game","gap","garage","garbage","garden","garlic","garment","gas","gasp","gate","gather","gauge","gaze","general","genius","genre","gentle","genuine","gesture","ghost","giant","gift","giggle","ginger","giraffe","girl","give","glad","glance","glare","glass","glide","glimpse","globe","gloom","glory","glove","glow","glue","goat","goddess","gold","good","goose","gorilla","gospel","gossip","govern","gown","grab","grace","grain","grant","grape","grass","gravity","great","green","grid","grief","grit","grocery","group","grow","grunt","guard","guess","guide","guilt","guitar","gun","gym","habit","hair","half","hammer","hamster","hand","happy","harbor","hard","harsh","harvest","hat","have","hawk","hazard","head","health","heart","heavy","hedgehog","height","hello","helmet","help","hen","hero","hidden","high","hill","hint","hip","hire","history","hobby","hockey","hold","hole","holiday","hollow","home","honey","hood","hope","horn","horror","horse","hospital","host","hotel","hour","hover","hub","huge","human","humble","humor","hundred","hungry","hunt","hurdle","hurry","hurt","husband","hybrid","ice","icon","idea","identify","idle","ignore","ill","illegal","illness","image","imitate","immense","immune","impact","impose","improve","impulse","inch","include","income","increase","index","indicate","indoor","industry","infant","inflict","inform","inhale","inherit","initial","inject","injury","inmate","inner","innocent","input","inquiry","insane","insect","inside","inspire","install","intact","interest","into","invest","invite","involve","iron","island","isolate","issue","item","ivory","jacket","jaguar","jar","jazz","jealous","jeans","jelly","jewel","job","join","joke","journey","joy","judge","juice","jump","jungle","junior","junk","just","kangaroo","keen","keep","ketchup","key","kick","kid","kidney","kind","kingdom","kiss","kit","kitchen","kite","kitten","kiwi","knee","knife","knock","know","lab","label","labor","ladder","lady","lake","lamp","language","laptop","large","later","latin","laugh","laundry","lava","law","lawn","lawsuit","layer","lazy","leader","leaf","learn","leave","lecture","left","leg","legal","legend","leisure","lemon","lend","length","lens","leopard","lesson","letter","level","liar","liberty","library","license","life","lift","light","like","limb","limit","link","lion","liquid","list","little","live","lizard","load","loan","lobster","local","lock","logic","lonely","long","loop","lottery","loud","lounge","love","loyal","lucky","luggage","lumber","lunar","lunch","luxury","lyrics","machine","mad","magic","magnet","maid","mail","main","major","make","mammal","man","manage","mandate","mango","mansion","manual","maple","marble","march","margin","marine","market","marriage","mask","mass","master","match","material","math","matrix","matter","maximum","maze","meadow","mean","measure","meat","mechanic","medal","media","melody","melt","member","memory","mention","menu","mercy","merge","merit","merry","mesh","message","metal","method","middle","midnight","milk","million","mimic","mind","minimum","minor","minute","miracle","mirror","misery","miss","mistake","mix","mixed","mixture","mobile","model","modify","mom","moment","monitor","monkey","monster","month","moon","moral","more","morning","mosquito","mother","motion","motor","mountain","mouse","move","movie","much","muffin","mule","multiply","muscle","museum","mushroom","music","must","mutual","myself","mystery","myth","naive","name","napkin","narrow","nasty","nation","nature","near","neck","need","negative","neglect","neither","nephew","nerve","nest","net","network","neutral","never","news","next","nice","night","noble","noise","nominee","noodle","normal","north","nose","notable","note","nothing","notice","novel","now","nuclear","number","nurse","nut","oak","obey","object","oblige","obscure","observe","obtain","obvious","occur","ocean","october","odor","off","offer","office","often","oil","okay","old","olive","olympic","omit","once","one","onion","online","only","open","opera","opinion","oppose","option","orange","orbit","orchard","order","ordinary","organ","orient","original","orphan","ostrich","other","outdoor","outer","output","outside","oval","oven","over","own","owner","oxygen","oyster","ozone","pact","paddle","page","pair","palace","palm","panda","panel","panic","panther","paper","parade","parent","park","parrot","party","pass","patch","path","patient","patrol","pattern","pause","pave","payment","peace","peanut","pear","peasant","pelican","pen","penalty","pencil","people","pepper","perfect","permit","person","pet","phone","photo","phrase","physical","piano","picnic","picture","piece","pig","pigeon","pill","pilot","pink","pioneer","pipe","pistol","pitch","pizza","place","planet","plastic","plate","play","please","pledge","pluck","plug","plunge","poem","poet","point","polar","pole","police","pond","pony","pool","popular","portion","position","possible","post","potato","pottery","poverty","powder","power","practice","praise","predict","prefer","prepare","present","pretty","prevent","price","pride","primary","print","priority","prison","private","prize","problem","process","produce","profit","program","project","promote","proof","property","prosper","protect","proud","provide","public","pudding","pull","pulp","pulse","pumpkin","punch","pupil","puppy","purchase","purity","purpose","purse","push","put","puzzle","pyramid","quality","quantum","quarter","question","quick","quit","quiz","quote","rabbit","raccoon","race","rack","radar","radio","rail","rain","raise","rally","ramp","ranch","random","range","rapid","rare","rate","rather","raven","raw","razor","ready","real","reason","rebel","rebuild","recall","receive","recipe","record","recycle","reduce","reflect","reform","refuse","region","regret","regular","reject","relax","release","relief","rely","remain","remember","remind","remove","render","renew","rent","reopen","repair","repeat","replace","report","require","rescue","resemble","resist","resource","response","result","retire","retreat","return","reunion","reveal","review","reward","rhythm","rib","ribbon","rice","rich","ride","ridge","rifle","right","rigid","ring","riot","ripple","risk","ritual","rival","river","road","roast","robot","robust","rocket","romance","roof","rookie","room","rose","rotate","rough","round","route","royal","rubber","rude","rug","rule","run","runway","rural","sad","saddle","sadness","safe","sail","salad","salmon","salon","salt","salute","same","sample","sand","satisfy","satoshi","sauce","sausage","save","say","scale","scan","scare","scatter","scene","scheme","school","science","scissors","scorpion","scout","scrap","screen","script","scrub","sea","search","season","seat","second","secret","section","security","seed","seek","segment","select","sell","seminar","senior","sense","sentence","series","service","session","settle","setup","seven","shadow","shaft","shallow","share","shed","shell","sheriff","shield","shift","shine","ship","shiver","shock","shoe","shoot","shop","short","shoulder","shove","shrimp","shrug","shuffle","shy","sibling","sick","side","siege","sight","sign","silent","silk","silly","silver","similar","simple","since","sing","siren","sister","situate","six","size","skate","sketch","ski","skill","skin","skirt","skull","slab","slam","sleep","slender","slice","slide","slight","slim","slogan","slot","slow","slush","small","smart","smile","smoke","smooth","snack","snake","snap","sniff","snow","soap","soccer","social","sock","soda","soft","solar","soldier","solid","solution","solve","someone","song","soon","sorry","sort","soul","sound","soup","source","south","space","spare","spatial","spawn","speak","special","speed","spell","spend","sphere","spice","spider","spike","spin","spirit","split","spoil","sponsor","spoon","sport","spot","spray","spread","spring","spy","square","squeeze","squirrel","stable","stadium","staff","stage","stairs","stamp","stand","start","state","stay","steak","steel","stem","step","stereo","stick","still","sting","stock","stomach","stone","stool","story","stove","strategy","street","strike","strong","struggle","student","stuff","stumble","style","subject","submit","subway","success","such","sudden","suffer","sugar","suggest","suit","summer","sun","sunny","sunset","super","supply","supreme","sure","surface","surge","surprise","surround","survey","suspect","sustain","swallow","swamp","swap","swarm","swear","sweet","swift","swim","swing","switch","sword","symbol","symptom","syrup","system","table","tackle","tag","tail","talent","talk","tank","tape","target","task","taste","tattoo","taxi","teach","team","tell","ten","tenant","tennis","tent","term","test","text","thank","that","theme","then","theory","there","they","thing","this","thought","three","thrive","throw","thumb","thunder","ticket","tide","tiger","tilt","timber","time","tiny","tip","tired","tissue","title","toast","tobacco","today","toddler","toe","together","toilet","token","tomato","tomorrow","tone","tongue","tonight","tool","tooth","top","topic","topple","torch","tornado","tortoise","toss","total","tourist","toward","tower","town","toy","track","trade","traffic","tragic","train","transfer","trap","trash","travel","tray","treat","tree","trend","trial","tribe","trick","trigger","trim","trip","trophy","trouble","truck","true","truly","trumpet","trust","truth","try","tube","tuition","tumble","tuna","tunnel","turkey","turn","turtle","twelve","twenty","twice","twin","twist","two","type","typical","ugly","umbrella","unable","unaware","uncle","uncover","under","undo","unfair","unfold","unhappy","uniform","unique","unit","universe","unknown","unlock","until","unusual","unveil","update","upgrade","uphold","upon","upper","upset","urban","urge","usage","use","used","useful","useless","usual","utility","vacant","vacuum","vague","valid","valley","valve","van","vanish","vapor","various","vast","vault","vehicle","velvet","vendor","venture","venue","verb","verify","version","very","vessel","veteran","viable","vibrant","vicious","victory","video","view","village","vintage","violin","virtual","virus","visa","visit","visual","vital","vivid","vocal","voice","void","volcano","volume","vote","voyage","wage","wagon","wait","walk","wall","walnut","want","warfare","warm","warrior","wash","wasp","waste","water","wave","way","wealth","weapon","wear","weasel","weather","web","wedding","weekend","weird","welcome","west","wet","whale","what","wheat","wheel","when","where","whip","whisper","wide","width","wife","wild","will","win","window","wine","wing","wink","winner","winter","wire","wisdom","wise","wish","witness","wolf","woman","wonder","wood","wool","word","work","world","worry","worth","wrap","wreck","wrestle","wrist","write","wrong","yard","year","yellow","you","young","youth","zebra","zero","zone","zoo"}; 2 | -------------------------------------------------------------------------------- /test/bip32_spec.c: -------------------------------------------------------------------------------- 1 | #include "./bdd-for-c.h" 2 | #include "./test_vectors.h" 3 | #include "../src/bip32.h" 4 | 5 | #define check_str(got, expected) check(strcmp(got, expected) == 0, "expected string '%s' got '%s'", expected, got) 6 | #define check_number(got, expected) check(got == expected, "expected '%d' got '%d'", expected, got) 7 | #define check_number_hex(got, expected) check(got == expected, "expected '0x%x' got '0x%x'", expected, got) 8 | //#define check_bytes(got, expected, size) check(memcmp(got, expected, size) == 0, "expected '0x%x' got '0x%x'", expected, got) 9 | 10 | spec("bip32") { 11 | 12 | context("given initializing key from entropy") { 13 | static bip32_key_t key; 14 | 15 | before_each() { 16 | } 17 | 18 | describe("when using 32bit entropy") { 19 | static uint8_t entropy[] = {0,0,0,0}; 20 | it("then it should fail") 21 | check(bip32_key_init_from_entropy(&key, entropy, sizeof(entropy)) != 0); 22 | } 23 | 24 | describe("when using 128bit entropy") { 25 | static uint8_t entropy[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 26 | it("then it should fail") 27 | check(bip32_key_init_from_entropy(&key, entropy, sizeof(entropy)) != 0); 28 | } 29 | 30 | describe("when using 256bit entropy") { 31 | static uint8_t entropy[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 32 | it("then it should fail") 33 | check(bip32_key_init_from_entropy(&key, entropy, sizeof(entropy)) != 0); 34 | } 35 | 36 | describe("when using required 512bit entropy") { 37 | static uint8_t entropy[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 38 | it("then it should succeed") 39 | check(bip32_key_init_from_entropy(&key, entropy, sizeof(entropy)) == 0); 40 | } 41 | 42 | } 43 | 44 | context("given initialized master key from know seed") { 45 | static bip32_key_t key; 46 | static bip32_key_t public_key; 47 | static int result = -1; 48 | 49 | before_each() { 50 | result = bip32_key_init_from_entropy(&key, vectors[0].seed, sizeof(vectors[0].seed)); 51 | } 52 | 53 | it("should not return error") 54 | check_number(result, 0); 55 | 56 | context("and generating public key") { 57 | static int result = -1; 58 | before() { 59 | result = bip32_key_init_public_from_private_key(&public_key, &key); 60 | } 61 | 62 | it("should not fail") 63 | check_number(result, 0); 64 | } 65 | } 66 | 67 | context("key (de)serialization") { 68 | static bip32_key_t private_key; 69 | static bip32_key_t public_key; 70 | static int result = -1; 71 | 72 | before() { 73 | bip32_key_init_from_entropy(&private_key, vectors[0].seed, sizeof(vectors[0].seed)); 74 | bip32_key_init_public_from_private_key(&public_key, &private_key); 75 | } 76 | 77 | describe("when serializing master key (m)") { 78 | static uint8_t buffer[1024]; 79 | static size_t size = sizeof(buffer); 80 | static int result = -1; 81 | 82 | before_each() { 83 | result = bip32_key_serialize(&private_key, false, (uint8_t*)buffer, &size); 84 | } 85 | 86 | it("then not return error") 87 | check_number(result, 0); 88 | 89 | it("then version should be { 0x04, 0x88, 0xad, 0xe4 } (xprv)") 90 | check(memcmp(buffer, (uint8_t[]){ 0x04, 0x88, 0xad, 0xe4 }, 4) == 0); 91 | 92 | it("then depth should be 1") 93 | check(memcmp((buffer + 4), (uint8_t[]){ 0x0 }, 1) == 0); 94 | 95 | it("then parent fingerprint should be { 0x0, 0x0, 0x0, 0x0 }") 96 | check(memcmp((buffer + 4 + 1), (uint8_t[]){ 0x0, 0x0, 0x0, 0x0 }, 4) == 0); 97 | 98 | it("then index should be { 0x0, 0x0, 0x0, 0x0 }") 99 | check(memcmp((buffer + 4 + 1 + 4), (uint8_t[]){ 0x0, 0x0, 0x0, 0x0 }, 4) == 0); 100 | } 101 | 102 | describe("when serializing derived hardend key key (m/0')") { 103 | static bip32_key_t child; 104 | static uint8_t buffer[1024]; 105 | static size_t size = sizeof(buffer); 106 | static int result = -1; 107 | 108 | before() { 109 | result = bip32_key_derive_child_key(&private_key, 2<<30, &child); 110 | result = bip32_key_serialize(&child, false, (uint8_t*)buffer, &size); 111 | } 112 | 113 | it("then not return error") 114 | check_number(result, 0); 115 | 116 | it("then version should be { 0x04, 0x88, 0xad, 0xe4 } (xprv)") 117 | check(memcmp(buffer, (uint8_t[]){ 0x04, 0x88, 0xad, 0xe4 }, 4) == 0); 118 | 119 | it("then depth should be 1") 120 | check(memcmp((buffer + 4), (uint8_t[]){ 0x01 }, 1) == 0); 121 | 122 | it("then parent fingerprint should be { 0xb8, 0x68, 0x8d, 0xf1}") 123 | check(memcmp((buffer + 4 + 1), (uint8_t[]){ 0xb8, 0x68, 0x8d, 0xf1 }, 4) == 0); 124 | 125 | it("then index should be { 0x80, 0x0, 0x0, 0x0 }") 126 | check(memcmp((buffer + 4 + 1 + 4), (uint8_t[]){ 0x80, 0x0, 0x0, 0x0 }, 4) == 0); 127 | } 128 | 129 | describe("when serializing private key (encoded)") { 130 | char buffer[1024]; 131 | static size_t size = sizeof(buffer); 132 | static int result = -1; 133 | 134 | before_each() { 135 | result = bip32_key_serialize(&private_key, true, (uint8_t*)buffer, &size); 136 | } 137 | 138 | it("then should not return error") 139 | check_number(result, 0); 140 | 141 | it("then should return the correct xprv* string") 142 | check_str(buffer, vectors[0].masterkey); 143 | } 144 | 145 | describe("when deserializing encoded private key") { 146 | static bip32_key_t deserialized_key; 147 | static int result = -1; 148 | before() { 149 | result = bip32_key_deserialize(&deserialized_key, vectors[0].masterkey); 150 | } 151 | it("then should not return error") 152 | check_number(result, 0); 153 | 154 | it ("then should create expected key") 155 | check(memcmp(&private_key, &deserialized_key, sizeof(bip32_key_t)) == 0); 156 | } 157 | 158 | describe("when serialize public key (encoded)") { 159 | char buffer[1024]; 160 | static size_t size = sizeof(buffer); 161 | static int result = -1; 162 | 163 | before_each() { 164 | result = bip32_key_serialize(&public_key, true, (uint8_t*)buffer, &size); 165 | } 166 | 167 | it("then should not return error") 168 | check_number(result, 0); 169 | 170 | it("should return the expected xpub* string") 171 | check_str(buffer, vectors[0].publickey); 172 | } 173 | 174 | describe("when deserializing encoded public key") { 175 | static bip32_key_t deserialized_key; 176 | static int result = -1; 177 | 178 | before() { 179 | bip32_key_init_public_from_private_key(&public_key, &private_key); 180 | result = bip32_key_deserialize(&deserialized_key, vectors[0].publickey); 181 | } 182 | 183 | it("then should not fail deserialize") 184 | check_number(result, 0); 185 | 186 | it ("then should create expected key") 187 | check(memcmp(&public_key, &deserialized_key, sizeof(bip32_key_t)) == 0); 188 | } 189 | } 190 | 191 | context("key deriviation") { 192 | static bip32_key_t private_key; 193 | static bip32_key_t public_key; 194 | static int result = -1; 195 | 196 | before() { 197 | bip32_key_init_from_entropy(&private_key, vectors[0].seed, sizeof(vectors[0].seed)); 198 | bip32_key_init_public_from_private_key(&public_key, &private_key); 199 | } 200 | 201 | context("given a known private key") { 202 | 203 | describe("when derive a hardend account child private key m/'0") { 204 | static bip32_key_t child; 205 | static int result = -1; 206 | before() { 207 | result = bip32_key_derive_child_key(&private_key, 2<<30, &child); 208 | } 209 | 210 | it("then should return non error") 211 | check_number(result, 0); 212 | 213 | it("then should be a private key") 214 | check(child.public == false); 215 | 216 | it("then depth should be 1") 217 | check_number(child.depth, 1); 218 | 219 | it("then parent fingerprint should be { 0xb8, 0x68, 0x8d, 0xf1}") 220 | check(memcmp(child.parent_fingerprint, (uint8_t[]){ 0xb8, 0x68, 0x8d, 0xf1 }, 4) == 0); 221 | 222 | it("then index should be 0x80000000") 223 | check_number_hex(child.index, 0x80000000); 224 | 225 | describe("and serialize child key (encoded)") { 226 | static bip32_key_t child; 227 | static char buffer[1024]; 228 | static size_t size = sizeof(buffer); 229 | before() { 230 | bip32_key_derive_child_key(&private_key, 0x80000000, &child); 231 | bip32_key_serialize(&child, true, (uint8_t*)buffer, &size); 232 | } 233 | it("should return the expected xprv9vFm...xEjA string") 234 | check_str(buffer, "xprv9vFm7KXyXnwkE9E1ETdKZWq9n5WqMP8eomCbJj3cAvwVErBjeq4U7Zw9EVXXABvrSFqik5ZfWH1VbSVCvmvVYD9ox31YnzmRkrXU22mxEjA"); 235 | } 236 | } 237 | 238 | describe("when derive a hardend account child private key m/'0/'1") { 239 | static bip32_key_t child_idx1, child_idx2; 240 | static int result = -1; 241 | before() { 242 | result = bip32_key_derive_child_key(&private_key, 0x80000000, &child_idx1); 243 | result = bip32_key_derive_child_key(&child_idx1, 0x80000001, &child_idx2); 244 | } 245 | 246 | it("then should return non error") 247 | check_number(result, 0); 248 | 249 | it("then should be a private key") 250 | check(child_idx2.public == false); 251 | 252 | it("then depth should be 2") 253 | check_number(child_idx2.depth, 2); 254 | 255 | it("then parent fingerprint should be { 0xc8, 0x79, 0x95, 0x0c}") 256 | check(memcmp(child_idx2.parent_fingerprint, (uint8_t[]){ 0xc8, 0x79, 0x95, 0x0c }, 4) == 0); 257 | 258 | it("then index should be 0x80000001") 259 | check_number_hex(child_idx2.index, 0x80000001); 260 | 261 | describe("and serialize child key (encoded)") { 262 | static char buffer[1024]; 263 | static size_t size = sizeof(buffer); 264 | before() { 265 | bip32_key_serialize(&child_idx2, true, (uint8_t*)buffer, &size); 266 | } 267 | it("should return the expected xprv9xFk...B8WH string") 268 | check_str(buffer, "xprv9xFkEEMD49vRgSHunvh2tyzK1ByWFygQEEBiSAUxmRqaE27AS73Hu9AV3Pb5zj1noVtAcySiqnkYC5VXi1wBoZFcRTGXCsiW6BwbfbHB8WH"); 269 | } 270 | } 271 | 272 | describe("when derive a normal account child private key m/'0/1") { 273 | static bip32_key_t child_idx1, child_idx2; 274 | static int result = -1; 275 | before() { 276 | result = bip32_key_derive_child_key(&private_key, 0x80000000, &child_idx1); 277 | result = bip32_key_derive_child_key(&child_idx1, 1, &child_idx2); 278 | } 279 | 280 | it("then should return non error") 281 | check_number(result, 0); 282 | 283 | it("then should be a private key") 284 | check(child_idx2.public == false); 285 | 286 | it("then depth should be 2") 287 | check_number(child_idx2.depth, 2); 288 | 289 | it("then parent fingerprint should be { 0xc8, 0x79, 0x95, 0x0c}") 290 | check(memcmp(child_idx2.parent_fingerprint, (uint8_t[]){ 0xc8, 0x79, 0x95, 0x0c }, 4) == 0); 291 | 292 | it("then index should be 1") 293 | check_number(child_idx2.index, 1); 294 | 295 | describe("and serialize child key (encoded)") { 296 | static char buffer[1024]; 297 | static size_t size = sizeof(buffer); 298 | before() { 299 | bip32_key_serialize(&child_idx2, true, (uint8_t*)buffer, &size); 300 | } 301 | it("should return the expected xprv9xFk...b13t string") 302 | check_str(buffer, "xprv9xFkEEM4iVPTUh9qEBen68GJ9ZfL5okMUwH6o9td98zu3HPduhYqpqybE6po8NuKA1e43opFjZUJn4p1xwanNf3RJQU3u5PRqz8aA8wb13t"); 303 | } 304 | } 305 | 306 | describe("when derive a normal account child private key using path derive m/0'/1'") { 307 | static bip32_key_t child; 308 | static int result = -1; 309 | before() { 310 | result = bip32_key_derive_child_by_path(&private_key, "m/0'/1'", &child); 311 | } 312 | 313 | it("then should return non error") 314 | check_number(result, 0); 315 | 316 | it("then should be a private key") 317 | check(child.public == false); 318 | 319 | it("then depth should be 2") 320 | check_number(child.depth, 2); 321 | 322 | it("then parent fingerprint should be { 0xc8, 0x79, 0x95, 0x0c}") 323 | check(memcmp(child.parent_fingerprint, (uint8_t[]){ 0xc8, 0x79, 0x95, 0x0c }, 4) == 0); 324 | 325 | it("then index should be 1") 326 | check_number_hex(child.index, 0x80000001); 327 | 328 | describe("and serialize child key (encoded)") { 329 | static char buffer[1024]; 330 | static size_t size = sizeof(buffer); 331 | before() { 332 | bip32_key_serialize(&child, true, (uint8_t*)buffer, &size); 333 | } 334 | it("should return the expected xprv9xFk...B8WH string") 335 | check_str(buffer, "xprv9xFkEEMD49vRgSHunvh2tyzK1ByWFygQEEBiSAUxmRqaE27AS73Hu9AV3Pb5zj1noVtAcySiqnkYC5VXi1wBoZFcRTGXCsiW6BwbfbHB8WH"); 336 | } 337 | } 338 | } 339 | } 340 | 341 | context("key identifiers") { 342 | static bip32_key_t private_key; 343 | static bip32_key_t public_key; 344 | static int result = -1; 345 | 346 | before() { 347 | bip32_key_init_from_entropy(&private_key, vectors[0].seed, sizeof(vectors[0].seed)); 348 | bip32_key_init_public_from_private_key(&public_key, &private_key); 349 | } 350 | 351 | describe("when creating identifier from private key") { 352 | static bip32_key_identifier_t ident; 353 | it("then should return non error") 354 | check(bip32_key_identifier_init_from_key(ident, &private_key) == 0); 355 | 356 | describe("and generating a fingerprint") { 357 | static uint8_t fingerprint[4]; 358 | static int result; 359 | 360 | before() { 361 | result = bip32_key_identifier_fingerprint(ident, fingerprint); 362 | } 363 | 364 | it("then should not return error") 365 | check_number(result, 0); 366 | 367 | it("then should generate expected fingerprint") 368 | check(memcmp(fingerprint, (uint8_t[]){0xb8, 0x68, 0x8d, 0xf1}, 4) == 0); 369 | } 370 | } 371 | 372 | describe("when creating identifier from public key") { 373 | static bip32_key_identifier_t ident; 374 | it("then should not return error") 375 | check(bip32_key_identifier_init_from_key(ident, &public_key) == 0); 376 | 377 | describe("and generating a fingerprint") { 378 | static uint8_t fingerprint[4]; 379 | static int result; 380 | 381 | before() { 382 | result = bip32_key_identifier_fingerprint(ident, fingerprint); 383 | } 384 | 385 | it("then should not return error") 386 | check_number(result, 0); 387 | 388 | it("then should generate expected fingerprint") 389 | check(memcmp(fingerprint, (uint8_t[]){0xb8, 0x68, 0x8d, 0xf1}, 4) == 0); 390 | } 391 | } 392 | 393 | describe("when creating identifier from public key") { 394 | static bip32_key_identifier_t ident; 395 | static uint32_t fingerprint; 396 | static int result; 397 | 398 | it("then should not return error") 399 | check(bip32_key_identifier_init_from_key(ident, &public_key) == 0); 400 | } 401 | } 402 | 403 | context("public key address") { 404 | static bip32_key_t private_key; 405 | static bip32_key_t public_key; 406 | static int result = -1; 407 | 408 | before() { 409 | bip32_key_init_from_entropy(&private_key, vectors[0].seed, sizeof(vectors[0].seed)); 410 | bip32_key_init_public_from_private_key(&public_key, &private_key); 411 | } 412 | 413 | describe("when creating p2pkh (legacy) address from private key") { 414 | static char buf[256]; 415 | static size_t size; 416 | before() { 417 | size = sizeof(buf); 418 | result = bip32_key_p2pkh_address_from_key(&private_key, buf, &size); 419 | } 420 | 421 | it ("then should not return error code") 422 | check_number(result, 0); 423 | 424 | it ("then should generate expected 1Hp4...x6NB address") 425 | check_str(buf, vectors[0].p2pkh_legacy_address); 426 | } 427 | 428 | describe("when creating p2pkh (legacy) address from public key") { 429 | static char buf[256]; 430 | static size_t size; 431 | before() { 432 | size = sizeof(buf); 433 | result = bip32_key_p2pkh_address_from_key(&public_key, buf, &size); 434 | } 435 | 436 | it ("then should not return error code") 437 | check_number(result, 0); 438 | 439 | it ("then should generate expected 1Hp4...x6NB address") 440 | check_str(buf, vectors[0].p2pkh_legacy_address); 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /test/bdd-for-c.h: -------------------------------------------------------------------------------- 1 | /*! 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Dmitriy Kubyshkin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef BDD_FOR_C_H 26 | #define BDD_FOR_C_H 27 | 28 | #ifdef _WIN32 29 | #include 30 | #include 31 | #include 32 | #define __BDD_IS_ATTY__() _isatty(_fileno(stdout)) 33 | #else 34 | #ifndef _POSIX_C_SOURCE 35 | // This definition is required for `fileno` to be defined 36 | #define _POSIX_C_SOURCE 200809 37 | #endif 38 | #include 39 | #include 40 | #include 41 | #define __BDD_IS_ATTY__() isatty(fileno(stdout)) 42 | #endif 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #ifdef _MSC_VER 51 | #pragma warning(push) 52 | #pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS 53 | #endif 54 | 55 | #ifndef BDD_USE_COLOR 56 | #define BDD_USE_COLOR 1 57 | #endif 58 | 59 | #ifndef BDD_USE_TAP 60 | #define BDD_USE_TAP 0 61 | #endif 62 | 63 | #define __BDD_COLOR_RESET__ "\x1B[0m" 64 | #define __BDD_COLOR_RED__ "\x1B[31m" 65 | #define __BDD_COLOR_GREEN__ "\x1B[32m" 66 | #define __BDD_COLOR_YELLOW__ "\x1B[33m" 67 | #define __BDD_COLOR_BOLD__ "\x1B[1m" // Bold White 68 | #define __BDD_COLOR_MAGENTA__ "\x1B[35m" 69 | 70 | typedef struct __bdd_array__ { 71 | void **values; 72 | size_t capacity; 73 | size_t size; 74 | } __bdd_array__; 75 | 76 | __bdd_array__ *__bdd_array_create__() { 77 | __bdd_array__ *arr = malloc(sizeof(__bdd_array__)); 78 | if (!arr) { 79 | perror("malloc(array)"); 80 | abort(); 81 | } 82 | arr->capacity = 4; 83 | arr->size = 0; 84 | arr->values = calloc(arr->capacity, sizeof(void *)); 85 | return arr; 86 | } 87 | 88 | void *__bdd_array_push__(__bdd_array__ *arr, void *item) { 89 | if (arr->size == arr->capacity) { 90 | arr->capacity *= 2; 91 | void *v = realloc(arr->values, sizeof(void*) * arr->capacity); 92 | if (!v) { 93 | perror("realloc(array)"); 94 | abort(); 95 | } 96 | arr->values = v; 97 | } 98 | arr->values[arr->size++] = item; 99 | return item; 100 | } 101 | 102 | void *__bdd_array_last__(__bdd_array__ *arr) { 103 | if (arr->size == 0) { 104 | return NULL; 105 | } 106 | return arr->values[arr->size - 1]; 107 | } 108 | 109 | void *__bdd_array_pop__(__bdd_array__ *arr) { 110 | if (arr->size == 0) { 111 | return NULL; 112 | } 113 | void *result = arr->values[arr->size - 1]; 114 | --arr->size; 115 | return result; 116 | } 117 | 118 | void __bdd_array_free__(__bdd_array__ *arr) { 119 | free(arr->values); 120 | free(arr); 121 | } 122 | 123 | typedef enum __bdd_node_type__ { 124 | __BDD_NODE_GROUP__ = 1, 125 | __BDD_NODE_TEST__ = 2, 126 | __BDD_NODE_INTERIM__ = 3 127 | } __bdd_node_type__; 128 | 129 | typedef enum __bdd_node_flags__ { 130 | __bdd_node_flags_none__ = 0, 131 | __bdd_node_flags_focus__ = 1 << 0, 132 | __bdd_node_flags_skip__ = 1 << 1, 133 | } __bdd_node_flags__; 134 | 135 | typedef struct __bdd_test_step__ { 136 | size_t level; 137 | int id; 138 | char *name; 139 | __bdd_node_type__ type; 140 | __bdd_node_flags__ flags; 141 | } __bdd_test_step__; 142 | 143 | typedef struct __bdd_node__ { 144 | int id; 145 | int next_node_id; 146 | char *name; 147 | __bdd_node_flags__ flags; 148 | __bdd_node_type__ type; 149 | __bdd_array__ *list_before; 150 | __bdd_array__ *list_after; 151 | __bdd_array__ *list_before_each; 152 | __bdd_array__ *list_after_each; 153 | __bdd_array__ *list_children; 154 | } __bdd_node__; 155 | 156 | enum __bdd_run_type__ { 157 | __BDD_INIT_RUN__ = 1, 158 | __BDD_TEST_RUN__ = 2 159 | }; 160 | 161 | typedef struct __bdd_config_type__ { 162 | enum __bdd_run_type__ run; 163 | int id; 164 | size_t test_index; 165 | size_t test_tap_index; 166 | size_t failed_test_count; 167 | __bdd_test_step__ *current_test; 168 | __bdd_array__ *node_stack; 169 | __bdd_array__ *nodes; 170 | char *error; 171 | char *location; 172 | bool use_color; 173 | bool use_tap; 174 | bool has_focus_nodes; 175 | } __bdd_config_type__; 176 | 177 | __bdd_test_step__ *__bdd_test_step_create__(size_t level, __bdd_node__ *node) { 178 | __bdd_test_step__ *step = malloc(sizeof(__bdd_test_step__)); 179 | if (!step) { 180 | perror("malloc(step)"); 181 | abort(); 182 | } 183 | step->id = node->id; 184 | step->level = level; 185 | step->type = node->type; 186 | step->name = node->name; 187 | step->flags = node->flags; 188 | return step; 189 | } 190 | 191 | __bdd_node__ *__bdd_node_create__(int id, char *name, __bdd_node_type__ type, __bdd_node_flags__ flags) { 192 | __bdd_node__ *n = malloc(sizeof(__bdd_node__)); 193 | if (!n) { 194 | perror("malloc(node)"); 195 | abort(); 196 | } 197 | n->id = id; 198 | n->next_node_id = id + 1; 199 | n->name = name; // node takes ownership of name 200 | n->type = type; 201 | n->flags = flags; 202 | n->list_before = __bdd_array_create__(); 203 | n->list_after = __bdd_array_create__(); 204 | n->list_before_each = __bdd_array_create__(); 205 | n->list_after_each = __bdd_array_create__(); 206 | n->list_children = __bdd_array_create__(); 207 | return n; 208 | } 209 | 210 | bool __bdd_node_is_leaf__(__bdd_node__ *node) { 211 | return node->list_children->size == 0; 212 | } 213 | 214 | void __bdd_node_flatten_internal__( 215 | __bdd_config_type__ *config, 216 | size_t level, 217 | __bdd_node__ *node, 218 | __bdd_array__ *steps, 219 | __bdd_array__ *before_each_lists, 220 | __bdd_array__ *after_each_lists 221 | ) { 222 | if (__bdd_node_is_leaf__(node)) { 223 | if (config->has_focus_nodes && !(node->flags & __bdd_node_flags_focus__)) { 224 | return; 225 | } 226 | 227 | for (size_t listIndex = 0; listIndex < before_each_lists->size; ++listIndex) { 228 | __bdd_array__ *list = before_each_lists->values[listIndex]; 229 | for (size_t i = 0; i < list->size; ++i) { 230 | __bdd_array_push__(steps, __bdd_test_step_create__(level, list->values[i])); 231 | } 232 | } 233 | 234 | __bdd_array_push__(steps, __bdd_test_step_create__(level, node)); 235 | 236 | for (size_t listIndex = 0; listIndex < after_each_lists->size; ++listIndex) { 237 | size_t reverseListIndex = after_each_lists->size - listIndex - 1; 238 | __bdd_array__ *list = after_each_lists->values[reverseListIndex]; 239 | for (size_t i = 0; i < list->size; ++i) { 240 | __bdd_array_push__(steps, __bdd_test_step_create__(level, list->values[i])); 241 | } 242 | } 243 | return; 244 | } 245 | 246 | __bdd_array_push__(steps, __bdd_test_step_create__(level, node)); 247 | 248 | for (size_t i = 0; i < node->list_before->size; ++i) { 249 | __bdd_array_push__(steps, __bdd_test_step_create__(level + 1, node->list_before->values[i])); 250 | } 251 | 252 | __bdd_array_push__(before_each_lists, node->list_before_each); 253 | __bdd_array_push__(after_each_lists, node->list_after_each); 254 | 255 | for (size_t i = 0; i < node->list_children->size; ++i) { 256 | __bdd_node_flatten_internal__( 257 | config, level + 1, node->list_children->values[i], steps, before_each_lists, after_each_lists 258 | ); 259 | } 260 | 261 | __bdd_array_pop__(before_each_lists); 262 | __bdd_array_pop__(after_each_lists); 263 | 264 | for (size_t i = 0; i < node->list_after->size; ++i) { 265 | __bdd_array_push__(steps, __bdd_test_step_create__(level + 1, node->list_after->values[i])); 266 | } 267 | } 268 | 269 | __bdd_array__ *__bdd_node_flatten__(__bdd_config_type__ *config, __bdd_node__ *node, __bdd_array__ *steps) { 270 | if (node == NULL) { 271 | return steps; 272 | } 273 | 274 | __bdd_array__ *before_each_lists = __bdd_array_create__(); 275 | __bdd_array__ *after_each_lists = __bdd_array_create__(); 276 | __bdd_node_flatten_internal__(config, 0, node, steps, before_each_lists, after_each_lists); 277 | __bdd_array_free__(before_each_lists); 278 | __bdd_array_free__(after_each_lists); 279 | 280 | return steps; 281 | } 282 | 283 | void __bdd_node_free__(__bdd_node__ *n) { 284 | free(n->name); 285 | __bdd_array_free__(n->list_before); 286 | __bdd_array_free__(n->list_after); 287 | __bdd_array_free__(n->list_before_each); 288 | __bdd_array_free__(n->list_after_each); 289 | __bdd_array_free__(n->list_children); 290 | free(n); 291 | } 292 | 293 | char *__bdd_spec_name__; 294 | void __bdd_test_main__(__bdd_config_type__ *__bdd_config__); 295 | char *__bdd_vformat__(const char *format, va_list va); 296 | 297 | void __bdd_indent__(FILE *fp, size_t level) { 298 | for (size_t i = 0; i < level; ++i) { 299 | fprintf(fp, " "); 300 | } 301 | } 302 | 303 | bool __bdd_enter_node__(__bdd_node_flags__ node_flags, __bdd_config_type__ *config, __bdd_node_type__ type, ptrdiff_t list_offset, char *fmt, ...) { 304 | va_list va; 305 | va_start(va, fmt); 306 | char *name = __bdd_vformat__(fmt, va); 307 | va_end(va); 308 | 309 | if (config->run == __BDD_INIT_RUN__) { 310 | __bdd_node__ *top = __bdd_array_last__(config->node_stack); 311 | __bdd_array__ *list = *(__bdd_array__ **)((unsigned char *)top + list_offset); 312 | 313 | int id = config->id++; 314 | __bdd_node__ *node = __bdd_node_create__(id, name, type, node_flags); 315 | if (node_flags & __bdd_node_flags_focus__) { 316 | // Propagate focus to group nodes up the tree to print inly them 317 | top->flags |= node_flags & __bdd_node_flags_focus__; 318 | config->has_focus_nodes = true; 319 | } 320 | __bdd_array_push__(list, node); 321 | __bdd_array_push__(config->nodes, node); 322 | if (type == __BDD_NODE_GROUP__) { 323 | __bdd_array_push__(config->node_stack, node); 324 | return true; 325 | } 326 | return false; 327 | } 328 | 329 | if (config->id >= (int)config->nodes->size) { 330 | fprintf(stderr, "non-deterministic spec\n"); 331 | abort(); 332 | } 333 | __bdd_node__ *node = config->nodes->values[config->id]; 334 | if (node->type != type || strcmp(node->name, name) != 0) { 335 | fprintf(stderr, "non-deterministic spec\n"); 336 | abort(); 337 | } 338 | free(name); 339 | 340 | __bdd_test_step__ *step = config->current_test; 341 | bool should_enter = step->id >= node->id && step->id < node->next_node_id; 342 | if (should_enter) { 343 | __bdd_array_push__(config->node_stack, node); 344 | config->id++; 345 | } else { 346 | config->id = node->next_node_id; 347 | } 348 | #if defined(BDD_PRINT_TRACE) 349 | const char *color = config->use_color ? __BDD_COLOR_MAGENTA__ : ""; 350 | fprintf(stderr, "%s% 3d ", color, step->id); 351 | __bdd_indent__(stderr, config->node_stack->size - 1 - (int)should_enter); 352 | const char *reset = config->use_color ? __BDD_COLOR_RESET__ : ""; 353 | fprintf(stderr, 354 | "%s [%d, %d) %s%s\n", 355 | should_enter ? ">" : "|", 356 | node->id, 357 | node->next_node_id, 358 | node->name, 359 | reset); 360 | #endif 361 | return should_enter; 362 | } 363 | 364 | void __bdd_exit_node__(__bdd_config_type__ *config) { 365 | __bdd_node__ *top = __bdd_array_pop__(config->node_stack); 366 | if (config->run == __BDD_INIT_RUN__) { 367 | top->next_node_id = config->id; 368 | } 369 | } 370 | 371 | void __bdd_run__(__bdd_config_type__ *config) { 372 | __bdd_test_step__ *step = config->current_test; 373 | 374 | if (step->type == __BDD_NODE_GROUP__ && !config->use_tap) { 375 | if (config->has_focus_nodes && !(step->flags & __bdd_node_flags_focus__)) { 376 | return; 377 | } 378 | __bdd_indent__(stdout, step->level); 379 | printf( 380 | "%s%s%s\n", 381 | config->use_color ? __BDD_COLOR_BOLD__ : "", 382 | step->name, 383 | config->use_color ? __BDD_COLOR_RESET__ : "" 384 | ); 385 | return; 386 | } 387 | 388 | bool skipped = false; 389 | if (step->type == __BDD_NODE_TEST__) { 390 | if (config->has_focus_nodes && !(step->flags & __bdd_node_flags_focus__)) { 391 | skipped = true; 392 | } else if (step->flags & __bdd_node_flags_skip__) { 393 | skipped = true; 394 | } 395 | ++config->test_tap_index; 396 | // Print the step name before running the test so it is visible 397 | // even if the test itself crashes. 398 | if ((!skipped || !config->has_focus_nodes) && config->run == __BDD_TEST_RUN__ && !config->use_tap) { 399 | __bdd_indent__(stdout, step->level); 400 | printf("%s ", step->name); 401 | } 402 | 403 | if (!skipped) { 404 | __bdd_test_main__(config); 405 | } 406 | 407 | if (skipped) { 408 | if (config->run == __BDD_TEST_RUN__) { 409 | if (!config->has_focus_nodes) { 410 | if (config->use_tap) { 411 | // We only to report tests and not setup / teardown success 412 | if (config->test_tap_index) { 413 | printf("skipped %zu - %s\n", config->test_tap_index, step->name); 414 | } 415 | } else { 416 | printf( 417 | "%s(SKIP)%s\n", 418 | config->use_color ? __BDD_COLOR_YELLOW__ : "", 419 | config->use_color ? __BDD_COLOR_RESET__ : "" 420 | ); 421 | } 422 | } 423 | } 424 | } else if (config->error == NULL) { 425 | if (config->run == __BDD_TEST_RUN__) { 426 | if (config->use_tap) { 427 | // We only to report tests and not setup / teardown success 428 | if (config->test_tap_index) { 429 | printf("ok %zu - %s\n", config->test_tap_index, step->name); 430 | } 431 | } else { 432 | printf( 433 | "%s(OK)%s\n", 434 | config->use_color ? __BDD_COLOR_GREEN__ : "", 435 | config->use_color ? __BDD_COLOR_RESET__ : "" 436 | ); 437 | } 438 | } 439 | } else { 440 | ++config->failed_test_count; 441 | if (config->use_tap) { 442 | // We only to report tests and not setup / teardown errors 443 | if (config->test_tap_index) { 444 | printf("not ok %zu - %s\n", config->test_tap_index, step->name); 445 | } 446 | } else { 447 | printf( 448 | "%s(FAIL)%s\n", 449 | config->use_color ? __BDD_COLOR_RED__ : "", 450 | config->use_color ? __BDD_COLOR_RESET__ : "" 451 | ); 452 | __bdd_indent__(stdout, step->level + 1); 453 | printf("%s\n", config->error); 454 | __bdd_indent__(stdout, step->level + 2); 455 | printf("%s\n", config->location); 456 | } 457 | free(config->error); 458 | config->error = NULL; 459 | } 460 | } else if (!skipped) { 461 | __bdd_test_main__(config); 462 | } 463 | } 464 | 465 | char *__bdd_vformat__(const char *format, va_list va) { 466 | // First we over-allocate 467 | const size_t size = 2048; 468 | char *result = calloc(size, sizeof(char)); 469 | if (!result) { 470 | perror("calloc(result)"); 471 | abort(); 472 | } 473 | vsnprintf(result, size - 1, format, va); 474 | 475 | // Then clip to an actual size 476 | void* r = realloc(result, strlen(result) + 1); 477 | if (!r) { 478 | perror("realloc(result)"); 479 | abort(); 480 | } 481 | result = r; 482 | return result; 483 | } 484 | 485 | char *__bdd_format__(const char *format, ...) { 486 | va_list va; 487 | va_start(va, format); 488 | char *result = __bdd_vformat__(format, va); 489 | va_end(va); 490 | return result; 491 | } 492 | 493 | bool __bdd_is_supported_term__() { 494 | bool result; 495 | const char *term = getenv("TERM"); 496 | result = term && strcmp(term, "") != 0; 497 | #ifndef _WIN32 498 | return result; 499 | #else 500 | if (result) { 501 | return 1; 502 | } 503 | 504 | // Attempt to enable virtual terminal processing on Windows. 505 | // See: https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx 506 | HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 507 | if (hOut == INVALID_HANDLE_VALUE) { 508 | return 0; 509 | } 510 | 511 | DWORD dwMode = 0; 512 | if (!GetConsoleMode(hOut, &dwMode)) { 513 | return 0; 514 | } 515 | 516 | dwMode |= 0x4; // ENABLE_VIRTUAL_TERMINAL_PROCESSING 517 | if (!SetConsoleMode(hOut, dwMode)) { 518 | return 0; 519 | } 520 | 521 | return 1; 522 | #endif 523 | } 524 | 525 | int main(void) { 526 | struct __bdd_config_type__ config = { 527 | .run = __BDD_INIT_RUN__, 528 | .id = 0, 529 | .test_index = 0, 530 | .test_tap_index = 0, 531 | .failed_test_count = 0, 532 | .node_stack = __bdd_array_create__(), 533 | .nodes = __bdd_array_create__(), 534 | .error = NULL, 535 | .use_color = 0, 536 | .use_tap = 0 537 | }; 538 | 539 | const char *tap_env = getenv("BDD_USE_TAP"); 540 | if (BDD_USE_TAP || (tap_env && strcmp(tap_env, "") != 0 && strcmp(tap_env, "0") != 0)) { 541 | config.use_tap = 1; 542 | } 543 | 544 | if (!config.use_tap && BDD_USE_COLOR && __BDD_IS_ATTY__() && __bdd_is_supported_term__()) { 545 | config.use_color = 1; 546 | } 547 | 548 | __bdd_node__ *root = __bdd_node_create__(-1, __bdd_spec_name__, __BDD_NODE_GROUP__, __bdd_node_flags_none__); 549 | __bdd_array_push__(config.node_stack, root); 550 | 551 | // During the first run we just gather the 552 | // count of the tests and their descriptions 553 | __bdd_test_main__(&config); 554 | 555 | __bdd_array__ *steps = __bdd_array_create__(); 556 | __bdd_node_flatten__(&config, root, steps); 557 | 558 | size_t test_count = 0; 559 | for (size_t i = 0; i < steps->size; ++i) { 560 | __bdd_test_step__ *step = steps->values[i]; 561 | if(step->type == __BDD_NODE_TEST__) { 562 | ++test_count; 563 | } 564 | } 565 | 566 | // Outputting the name of the suite 567 | if (config.use_tap) { 568 | printf("TAP version 13\n1..%zu\n", test_count); 569 | } 570 | 571 | config.run = __BDD_TEST_RUN__; 572 | 573 | for (size_t i = 0; i < steps->size; ++i) { 574 | __bdd_test_step__ *step = steps->values[i]; 575 | config.node_stack->size = 1; 576 | config.id = 0; 577 | config.current_test = step; 578 | __bdd_run__(&config); 579 | } 580 | 581 | for (size_t i = 0; i < config.nodes->size; ++i) { 582 | __bdd_node_free__(config.nodes->values[i]); 583 | } 584 | root->name = NULL; // name is statically allocated 585 | __bdd_node_free__(root); 586 | for (size_t i = 0; i < steps->size; ++i) { 587 | free(steps->values[i]); 588 | } 589 | __bdd_array_free__(config.nodes); 590 | __bdd_array_free__(config.node_stack); 591 | __bdd_array_free__(steps); 592 | 593 | if (config.failed_test_count > 0) { 594 | if (!config.use_tap) { 595 | printf( 596 | "\n%zu test%s run, %zu failed.\n", 597 | test_count, test_count == 1 ? "" : "s", config.failed_test_count 598 | ); 599 | } 600 | return 1; 601 | } 602 | return 0; 603 | } 604 | 605 | #define spec(name) \ 606 | char *__bdd_spec_name__ = (name);\ 607 | void __bdd_test_main__ (__bdd_config_type__ *__bdd_config__)\ 608 | 609 | #define __BDD_NODE__(flags, node_list, type, ...)\ 610 | for(\ 611 | bool __bdd_has_run__ = 0;\ 612 | (\ 613 | !__bdd_has_run__ && \ 614 | __bdd_enter_node__(flags, __bdd_config__, (type), offsetof(struct __bdd_node__, node_list), __VA_ARGS__) \ 615 | );\ 616 | __bdd_exit_node__(__bdd_config__), \ 617 | __bdd_has_run__ = 1 \ 618 | ) 619 | 620 | #define describe(...) __BDD_NODE__(__bdd_node_flags_none__, list_children, __BDD_NODE_GROUP__, __VA_ARGS__) 621 | #define it(...) __BDD_NODE__(__bdd_node_flags_none__, list_children, __BDD_NODE_TEST__, __VA_ARGS__) 622 | #define it_only(...) __BDD_NODE__(__bdd_node_flags_focus__, list_children, __BDD_NODE_TEST__, __VA_ARGS__) 623 | #define fit(...) it_only(__VA_ARGS__) 624 | #define it_skip(...) __BDD_NODE__(__bdd_node_flags_skip__, list_children, __BDD_NODE_TEST__, __VA_ARGS__) 625 | #define xit(...) it_skip(__VA_ARGS__) 626 | #define before_each() __BDD_NODE__(__bdd_node_flags_none__, list_before_each, __BDD_NODE_INTERIM__, "before_each") 627 | #define after_each() __BDD_NODE__(__bdd_node_flags_none__, list_after_each, __BDD_NODE_INTERIM__, "after_each") 628 | #define before() __BDD_NODE__(__bdd_node_flags_none__, list_before, __BDD_NODE_INTERIM__, "before") 629 | #define after() __BDD_NODE__(__bdd_node_flags_none__, list_after, __BDD_NODE_INTERIM__, "after") 630 | 631 | #ifndef BDD_NO_CONTEXT_KEYWORD 632 | #define context(name) describe(name) 633 | #endif 634 | 635 | #define __BDD_MACRO__(M, ...) __BDD_OVERLOAD__(M, __BDD_COUNT_ARGS__(__VA_ARGS__)) (__VA_ARGS__) 636 | #define __BDD_OVERLOAD__(macro_name, suffix) __BDD_EXPAND_OVERLOAD__(macro_name, suffix) 637 | #define __BDD_EXPAND_OVERLOAD__(macro_name, suffix) macro_name##suffix 638 | 639 | #define __BDD_COUNT_ARGS__(...) __BDD_PATTERN_MATCH__(__VA_ARGS__,_,_,_,_,_,_,_,_,_,ONE__) 640 | #define __BDD_PATTERN_MATCH__(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N, ...) N 641 | 642 | #define __BDD_STRING_HELPER__(x) #x 643 | #define __BDD_STRING__(x) __BDD_STRING_HELPER__(x) 644 | #define __STRING__LINE__ __BDD_STRING__(__LINE__) 645 | 646 | #define __BDD_FMT_COLOR__ __BDD_COLOR_RED__ "Check failed:" __BDD_COLOR_RESET__ " %s" 647 | #define __BDD_FMT_PLAIN__ "Check failed: %s" 648 | 649 | #define __BDD_CHECK__(condition, ...) if (!(condition))\ 650 | {\ 651 | char *message = __bdd_format__(__VA_ARGS__);\ 652 | const char *fmt = __bdd_config__->use_color ? __BDD_FMT_COLOR__ : __BDD_FMT_PLAIN__;\ 653 | __bdd_config__->location = "at " __FILE__ ":" __STRING__LINE__;\ 654 | size_t bufflen = strlen(fmt) + strlen(message) + 1;\ 655 | __bdd_config__->error = calloc(bufflen, sizeof(char));\ 656 | if (__bdd_config__->use_color) {\ 657 | snprintf(__bdd_config__->error, bufflen, __BDD_FMT_COLOR__, message);\ 658 | } else {\ 659 | snprintf(__bdd_config__->error, bufflen, __BDD_FMT_PLAIN__, message);\ 660 | }\ 661 | free(message);\ 662 | return;\ 663 | } 664 | 665 | #define __BDD_CHECK_ONE__(condition) __BDD_CHECK__(condition, #condition) 666 | 667 | #define check(...) __BDD_MACRO__(__BDD_CHECK_, __VA_ARGS__) 668 | 669 | #ifdef _MSC_VER 670 | #pragma warning(pop) 671 | #endif 672 | 673 | #endif //BDD_FOR_C_H 674 | -------------------------------------------------------------------------------- /src/bip44_coins.h: -------------------------------------------------------------------------------- 1 | #ifndef __bip44_coins__ 2 | #define __bip44_coins__ 3 | 4 | #include 5 | 6 | typedef struct bip44_coin_t { 7 | uint32_t type; 8 | char *symbol; 9 | char *coin; 10 | } bip44_coin_t; 11 | 12 | static bip44_coin_t bip44_coins[] = { 13 | { 0, "BTC", "Bitcoin" }, 14 | { 1, "", "Testnet (all coins)" }, 15 | { 2, "LTC", "Litecoin" }, 16 | { 3, "DOGE", "Dogecoin" }, 17 | { 4, "RDD", "Reddcoin" }, 18 | { 5, "DASH", "Dash" }, 19 | { 6, "PPC", "Peercoin" }, 20 | { 7, "NMC", "Namecoin" }, 21 | { 8, "FTC", "Feathercoin" }, 22 | { 9, "XCP", "Counterparty" }, 23 | { 10, "BLK", "Blackcoin" }, 24 | { 11, "NSR", "NuShares" }, 25 | { 12, "NBT", "NuBits" }, 26 | { 13, "MZC", "Mazacoin" }, 27 | { 14, "VIA", "Viacoin" }, 28 | { 15, "XCH", "ClearingHouse" }, 29 | { 16, "RBY", "Rubycoin" }, 30 | { 17, "GRS", "Groestlcoin" }, 31 | { 18, "DGC", "Digitalcoin" }, 32 | { 19, "CCN", "Cannacoin" }, 33 | { 20, "DGB", "DigiByte" }, 34 | { 21, "", "Open Assets" }, 35 | { 22, "MONA", "Monacoin" }, 36 | { 23, "CLAM", "Clams" }, 37 | { 24, "XPM", "Primecoin" }, 38 | { 25, "NEOS", "Neoscoin" }, 39 | { 26, "JBS", "Jumbucks" }, 40 | { 27, "ZRC", "ziftrCOIN" }, 41 | { 28, "VTC", "Vertcoin" }, 42 | { 29, "NXT", "NXT" }, 43 | { 30, "BURST", "Burst" }, 44 | { 31, "MUE", "MonetaryUnit" }, 45 | { 32, "ZOOM", "Zoom" }, 46 | { 33, "VASH", "Virtual Cash" }, 47 | { 34, "CDN", "Canada eCoin" }, 48 | { 35, "SDC", "ShadowCash" }, 49 | { 36, "PKB", "ParkByte" }, 50 | { 37, "PND", "Pandacoin" }, 51 | { 38, "START", "StartCOIN" }, 52 | { 39, "MOIN", "MOIN" }, 53 | { 40, "EXP", "Expanse" }, 54 | { 41, "EMC2", "Einsteinium" }, 55 | { 42, "DCR", "Decred" }, 56 | { 43, "XEM", "NEM" }, 57 | { 44, "PART", "Particl" }, 58 | { 45, "ARG", "Argentum (dead)" }, 59 | { 46, "", "Libertas" }, 60 | { 47, "", "Posw coin" }, 61 | { 48, "SHR", "Shreeji" }, 62 | { 49, "GCR", "Global Currency Reserve (GCRcoin)" }, 63 | { 50, "NVC", "Novacoin" }, 64 | { 51, "AC", "Asiacoin" }, 65 | { 52, "BTCD", "BitcoinDark" }, 66 | { 53, "DOPE", "Dopecoin" }, 67 | { 54, "TPC", "Templecoin" }, 68 | { 55, "AIB", "AIB" }, 69 | { 56, "EDRC", "EDRCoin" }, 70 | { 57, "SYS", "Syscoin" }, 71 | { 58, "SLR", "Solarcoin" }, 72 | { 59, "SMLY", "Smileycoin" }, 73 | { 60, "ETH", "Ether" }, 74 | { 61, "ETC", "Ether Classic" }, 75 | { 62, "PSB", "Pesobit" }, 76 | { 63, "LDCN", "Landcoin (dead)" }, 77 | { 64, "", "Open Chain" }, 78 | { 65, "XBC", "Bitcoinplus" }, 79 | { 66, "IOP", "Internet of People" }, 80 | { 67, "NXS", "Nexus" }, 81 | { 68, "INSN", "InsaneCoin" }, 82 | { 69, "OK", "OKCash" }, 83 | { 70, "BRIT", "BritCoin" }, 84 | { 71, "CMP", "Compcoin" }, 85 | { 72, "CRW", "Crown" }, 86 | { 73, "BELA", "BelaCoin" }, 87 | { 74, "ICX", "ICON" }, 88 | { 75, "FJC", "FujiCoin" }, 89 | { 76, "MIX", "MIX" }, 90 | { 77, "XVG", "Verge Currency" }, 91 | { 78, "EFL", "Electronic Gulden" }, 92 | { 79, "CLUB", "ClubCoin" }, 93 | { 80, "RICHX", "RichCoin" }, 94 | { 81, "POT", "Potcoin" }, 95 | { 82, "QRK", "Quarkcoin" }, 96 | { 83, "TRC", "Terracoin" }, 97 | { 84, "GRC", "Gridcoin" }, 98 | { 85, "AUR", "Auroracoin" }, 99 | { 86, "IXC", "IXCoin" }, 100 | { 87, "NLG", "Gulden" }, 101 | { 88, "BITB", "BitBean" }, 102 | { 89, "BTA", "Bata" }, 103 | { 90, "XMY", "Myriadcoin" }, 104 | { 91, "BSD", "BitSend" }, 105 | { 92, "UNO", "Unobtanium" }, 106 | { 93, "MTR", "MasterTrader" }, 107 | { 94, "GB", "GoldBlocks" }, 108 | { 95, "SHM", "Saham" }, 109 | { 96, "CRX", "Chronos" }, 110 | { 97, "BIQ", "Ubiquoin" }, 111 | { 98, "EVO", "Evotion" }, 112 | { 99, "STO", "SaveTheOcean" }, 113 | { 100, "BIGUP", "BigUp" }, 114 | { 101, "GAME", "GameCredits" }, 115 | { 102, "DLC", "Dollarcoins" }, 116 | { 103, "ZYD", "Zayedcoin" }, 117 | { 104, "DBIC", "Dubaicoin" }, 118 | { 105, "STRAT", "Stratis" }, 119 | { 106, "SH", "Shilling" }, 120 | { 107, "MARS", "MarsCoin" }, 121 | { 108, "UBQ", "Ubiq" }, 122 | { 109, "PTC", "Pesetacoin" }, 123 | { 110, "NRO", "Neurocoin" }, 124 | { 111, "ARK", "ARK" }, 125 | { 112, "USC", "UltimateSecureCashMain" }, 126 | { 113, "THC", "Hempcoin" }, 127 | { 114, "LINX", "Linx" }, 128 | { 115, "ECN", "Ecoin" }, 129 | { 116, "DNR", "Denarius" }, 130 | { 117, "PINK", "Pinkcoin" }, 131 | { 118, "ATOM", "Atom" }, 132 | { 119, "PIVX", "Pivx" }, 133 | { 120, "FLASH", "Flashcoin" }, 134 | { 121, "ZEN", "Zencash" }, 135 | { 122, "PUT", "Putincoin" }, 136 | { 123, "ZNY", "BitZeny" }, 137 | { 124, "UNIFY", "Unify" }, 138 | { 125, "XST", "StealthCoin" }, 139 | { 126, "BRK", "Breakout Coin" }, 140 | { 127, "VC", "Vcash" }, 141 | { 128, "XMR", "Monero" }, 142 | { 129, "VOX", "Voxels" }, 143 | { 130, "NAV", "NavCoin" }, 144 | { 131, "FCT", "Factom Factoids" }, 145 | { 132, "EC", "Factom Entry Credits" }, 146 | { 133, "ZEC", "Zcash" }, 147 | { 134, "LSK", "Lisk" }, 148 | { 135, "STEEM", "Steem" }, 149 | { 136, "XZC", "ZCoin" }, 150 | { 137, "RBTC", "Rootstock" }, 151 | { 138, "", "Giftblock" }, 152 | { 139, "RPT", "RealPointCoin" }, 153 | { 140, "LBC", "LBRY Credits" }, 154 | { 141, "KMD", "Komodo" }, 155 | { 142, "BSQ", "bisq Token" }, 156 | { 143, "RIC", "Riecoin" }, 157 | { 144, "XRP", "XRP" }, 158 | { 145, "BCH", "Bitcoin Cash" }, 159 | { 146, "NEBL", "Neblio" }, 160 | { 147, "ZCL", "ZClassic" }, 161 | { 148, "XLM", "Stellar Lumens" }, 162 | { 149, "NLC2", "NoLimitCoin2" }, 163 | { 150, "WHL", "WhaleCoin" }, 164 | { 151, "ERC", "EuropeCoin" }, 165 | { 152, "DMD", "Diamond" }, 166 | { 153, "BTM", "Bytom" }, 167 | { 154, "BIO", "Biocoin" }, 168 | { 155, "XWCC", "Whitecoin Classic" }, 169 | { 156, "BTG", "Bitcoin Gold" }, 170 | { 157, "BTC2X", "Bitcoin 2x" }, 171 | { 158, "SSN", "SuperSkynet" }, 172 | { 159, "TOA", "TOACoin" }, 173 | { 160, "BTX", "Bitcore" }, 174 | { 161, "ACC", "Adcoin" }, 175 | { 162, "BCO", "Bridgecoin" }, 176 | { 163, "ELLA", "Ellaism" }, 177 | { 164, "PIRL", "Pirl" }, 178 | { 165, "XNO", "Nano" }, 179 | { 166, "VIVO", "Vivo" }, 180 | { 167, "FRST", "Firstcoin" }, 181 | { 168, "HNC", "Helleniccoin" }, 182 | { 169, "BUZZ", "BUZZ" }, 183 | { 170, "MBRS", "Ember" }, 184 | { 171, "HC", "Hcash" }, 185 | { 172, "HTML", "HTMLCOIN" }, 186 | { 173, "ODN", "Obsidian" }, 187 | { 174, "ONX", "OnixCoin" }, 188 | { 175, "RVN", "Ravencoin" }, 189 | { 176, "GBX", "GoByte" }, 190 | { 177, "BTCZ", "BitcoinZ" }, 191 | { 178, "POA", "Poa" }, 192 | { 179, "NYC", "NewYorkCoin" }, 193 | { 180, "MXT", "MarteXcoin" }, 194 | { 181, "WC", "Wincoin" }, 195 | { 182, "MNX", "Minexcoin" }, 196 | { 183, "BTCP", "Bitcoin Private" }, 197 | { 184, "MUSIC", "Musicoin" }, 198 | { 185, "BCA", "Bitcoin Atom" }, 199 | { 186, "CRAVE", "Crave" }, 200 | { 187, "STAK", "STRAKS" }, 201 | { 188, "WBTC", "World Bitcoin" }, 202 | { 189, "LCH", "LiteCash" }, 203 | { 190, "EXCL", "ExclusiveCoin" }, 204 | { 191, "", "Lynx" }, 205 | { 192, "LCC", "LitecoinCash" }, 206 | { 193, "XFE", "Feirm" }, 207 | { 194, "EOS", "EOS" }, 208 | { 195, "TRX", "Tron" }, 209 | { 196, "KOBO", "Kobocoin" }, 210 | { 197, "HUSH", "HUSH" }, 211 | { 198, "BAN", "Banano" }, 212 | { 199, "ETF", "ETF" }, 213 | { 200, "OMNI", "Omni" }, 214 | { 201, "BIFI", "BitcoinFile" }, 215 | { 202, "UFO", "Uniform Fiscal Object" }, 216 | { 203, "CNMC", "Cryptonodes" }, 217 | { 204, "BCN", "Bytecoin" }, 218 | { 205, "RIN", "Ringo" }, 219 | { 206, "ATP", "Alaya" }, 220 | { 207, "EVT", "everiToken" }, 221 | { 208, "ATN", "ATN" }, 222 | { 209, "BIS", "Bismuth" }, 223 | { 210, "NEET", "NEETCOIN" }, 224 | { 211, "BOPO", "BopoChain" }, 225 | { 212, "OOT", "Utrum" }, 226 | { 213, "ALIAS", "Alias" }, 227 | { 214, "MONK", "Monkey Project" }, 228 | { 215, "BOXY", "BoxyCoin" }, 229 | { 216, "FLO", "Flo" }, 230 | { 217, "MEC", "Megacoin" }, 231 | { 218, "BTDX", "BitCloud" }, 232 | { 219, "XAX", "Artax" }, 233 | { 220, "ANON", "ANON" }, 234 | { 221, "LTZ", "LitecoinZ" }, 235 | { 222, "BITG", "Bitcoin Green" }, 236 | { 223, "ICP", "Internet Computer (DFINITY)" }, 237 | { 224, "SMART", "Smartcash" }, 238 | { 225, "XUEZ", "XUEZ" }, 239 | { 226, "HLM", "Helium" }, 240 | { 227, "WEB", "Webchain" }, 241 | { 228, "ACM", "Actinium" }, 242 | { 229, "NOS", "NOS Stable Coins" }, 243 | { 230, "BITC", "BitCash" }, 244 | { 231, "HTH", "Help The Homeless Coin" }, 245 | { 232, "TZC", "Trezarcoin" }, 246 | { 233, "VAR", "Varda" }, 247 | { 234, "IOV", "IOV" }, 248 | { 235, "FIO", "FIO" }, 249 | { 236, "BSV", "BitcoinSV" }, 250 | { 237, "DXN", "DEXON" }, 251 | { 238, "QRL", "Quantum Resistant Ledger" }, 252 | { 239, "PCX", "ChainX" }, 253 | { 240, "LOKI", "Loki" }, 254 | { 241, "", "Imagewallet" }, 255 | { 242, "NIM", "Nimiq" }, 256 | { 243, "SOV", "Sovereign Coin" }, 257 | { 244, "JCT", "Jibital Coin" }, 258 | { 245, "SLP", "Simple Ledger Protocol" }, 259 | { 246, "EWT", "Energy Web" }, 260 | { 247, "UC", "Ulord" }, 261 | { 248, "EXOS", "EXOS" }, 262 | { 249, "ECA", "Electra" }, 263 | { 250, "SOOM", "Soom" }, 264 | { 251, "XRD", "Redstone" }, 265 | { 252, "FREE", "FreeCoin" }, 266 | { 253, "NPW", "NewPowerCoin" }, 267 | { 254, "BST", "BlockStamp" }, 268 | { 255, "", "SmartHoldem" }, 269 | { 256, "NANO", "Bitcoin Nano" }, 270 | { 257, "BTCC", "Bitcoin Core" }, 271 | { 258, "", "Zen Protocol" }, 272 | { 259, "ZEST", "Zest" }, 273 | { 260, "ABT", "ArcBlock" }, 274 | { 261, "PION", "Pion" }, 275 | { 262, "DT3", "DreamTeam3" }, 276 | { 263, "ZBUX", "Zbux" }, 277 | { 264, "KPL", "Kepler" }, 278 | { 265, "TPAY", "TokenPay" }, 279 | { 266, "ZILLA", "ChainZilla" }, 280 | { 267, "ANK", "Anker" }, 281 | { 268, "BCC", "BCChain" }, 282 | { 269, "HPB", "HPB" }, 283 | { 270, "ONE", "ONE" }, 284 | { 271, "SBC", "SBC" }, 285 | { 272, "IPC", "IPChain" }, 286 | { 273, "DMTC", "Dominantchain" }, 287 | { 274, "OGC", "Onegram" }, 288 | { 275, "SHIT", "Shitcoin" }, 289 | { 276, "ANDES", "Andescoin" }, 290 | { 277, "AREPA", "Arepacoin" }, 291 | { 278, "BOLI", "Bolivarcoin" }, 292 | { 279, "RIL", "Rilcoin" }, 293 | { 280, "HTR", "Hathor Network" }, 294 | { 281, "ACME", "Accumulate" }, 295 | { 282, "BRAVO", "BRAVO" }, 296 | { 283, "ALGO", "Algorand" }, 297 | { 284, "BZX", "Bitcoinzero" }, 298 | { 285, "GXX", "GravityCoin" }, 299 | { 286, "HEAT", "HEAT" }, 300 | { 287, "XDN", "DigitalNote" }, 301 | { 288, "FSN", "FUSION" }, 302 | { 289, "CPC", "Capricoin" }, 303 | { 290, "BOLD", "Bold" }, 304 | { 291, "IOST", "IOST" }, 305 | { 292, "TKEY", "Tkeycoin" }, 306 | { 293, "USE", "Usechain" }, 307 | { 294, "BCZ", "BitcoinCZ" }, 308 | { 295, "IOC", "Iocoin" }, 309 | { 296, "ASF", "Asofe" }, 310 | { 297, "MASS", "MASS" }, 311 | { 298, "FAIR", "FairCoin" }, 312 | { 299, "NUKO", "Nekonium" }, 313 | { 300, "GNX", "Genaro Network" }, 314 | { 301, "DIVI", "Divi Project" }, 315 | { 302, "CMT", "Community" }, 316 | { 303, "EUNO", "EUNO" }, 317 | { 304, "IOTX", "IoTeX" }, 318 | { 305, "ONION", "DeepOnion" }, 319 | { 306, "8BIT", "8Bit" }, 320 | { 307, "ATC", "AToken Coin" }, 321 | { 308, "BTS", "Bitshares" }, 322 | { 309, "CKB", "Nervos CKB" }, 323 | { 310, "UGAS", "Ultrain" }, 324 | { 311, "ADS", "Adshares" }, 325 | { 312, "ARA", "Aura" }, 326 | { 313, "ZIL", "Zilliqa" }, 327 | { 314, "MOAC", "MOAC" }, 328 | { 315, "SWTC", "SWTC" }, 329 | { 316, "VNSC", "vnscoin" }, 330 | { 317, "PLUG", "Pl^g" }, 331 | { 318, "MAN", "Matrix AI Network" }, 332 | { 319, "ECC", "ECCoin" }, 333 | { 320, "RPD", "Rapids" }, 334 | { 321, "RAP", "Rapture" }, 335 | { 322, "GARD", "Hashgard" }, 336 | { 323, "ZER", "Zero" }, 337 | { 324, "EBST", "eBoost" }, 338 | { 325, "SHARD", "Shard" }, 339 | { 326, "MRX", "Metrix Coin" }, 340 | { 327, "CMM", "Commercium" }, 341 | { 328, "BLOCK", "Blocknet" }, 342 | { 329, "AUDAX", "AUDAX" }, 343 | { 330, "LUNA", "Terra" }, 344 | { 331, "ZPM", "zPrime" }, 345 | { 332, "KUVA", "Kuva Utility Note" }, 346 | { 333, "MEM", "MemCoin" }, 347 | { 334, "CS", "Credits" }, 348 | { 335, "SWIFT", "SwiftCash" }, 349 | { 336, "FIX", "FIX" }, 350 | { 337, "CPC", "CPChain" }, 351 | { 338, "VGO", "VirtualGoodsToken" }, 352 | { 339, "DVT", "DeVault" }, 353 | { 340, "N8V", "N8VCoin" }, 354 | { 341, "MTNS", "OmotenashiCoin" }, 355 | { 342, "BLAST", "BLAST" }, 356 | { 343, "DCT", "DECENT" }, 357 | { 344, "AUX", "Auxilium" }, 358 | { 345, "USDP", "USDP" }, 359 | { 346, "HTDF", "HTDF" }, 360 | { 347, "YEC", "Ycash" }, 361 | { 348, "QLC", "QLC Chain" }, 362 | { 349, "TEA", "Icetea Blockchain" }, 363 | { 350, "ARW", "ArrowChain" }, 364 | { 351, "MDM", "Medium" }, 365 | { 352, "CYB", "Cybex" }, 366 | { 353, "LTO", "LTO Network" }, 367 | { 354, "DOT", "Polkadot" }, 368 | { 355, "AEON", "Aeon" }, 369 | { 356, "RES", "Resistance" }, 370 | { 357, "AYA", "Aryacoin" }, 371 | { 358, "DAPS", "Dapscoin" }, 372 | { 359, "CSC", "CasinoCoin" }, 373 | { 360, "VSYS", "V Systems" }, 374 | { 361, "NOLLAR", "Nollar" }, 375 | { 362, "XNOS", "NOS" }, 376 | { 363, "CPU", "CPUchain" }, 377 | { 364, "LAMB", "Lambda Storage Chain" }, 378 | { 365, "VCT", "ValueCyber" }, 379 | { 366, "CZR", "Canonchain" }, 380 | { 367, "ABBC", "ABBC" }, 381 | { 368, "HET", "HET" }, 382 | { 369, "XAS", "Asch" }, 383 | { 370, "VDL", "Vidulum" }, 384 | { 371, "MED", "MediBloc" }, 385 | { 372, "ZVC", "ZVChain" }, 386 | { 373, "VESTX", "Vestx" }, 387 | { 374, "DBT", "DarkBit" }, 388 | { 375, "SEOS", "SuperEOS" }, 389 | { 376, "MXW", "Maxonrow" }, 390 | { 377, "ZNZ", "ZENZO" }, 391 | { 378, "XCX", "XChain" }, 392 | { 379, "SOX", "SonicX" }, 393 | { 380, "NYZO", "Nyzo" }, 394 | { 381, "ULC", "ULCoin" }, 395 | { 382, "RYO", "Ryo Currency" }, 396 | { 383, "KAL", "Kaleidochain" }, 397 | { 384, "XSN", "Stakenet" }, 398 | { 385, "DOGEC", "DogeCash" }, 399 | { 386, "BMV", "Bitcoin Matteo's Vision" }, 400 | { 387, "QBC", "Quebecoin" }, 401 | { 388, "IMG", "ImageCoin" }, 402 | { 389, "QOS", "QOS" }, 403 | { 390, "PKT", "PKT" }, 404 | { 391, "LHD", "LitecoinHD" }, 405 | { 392, "CENNZ", "CENNZnet" }, 406 | { 393, "HSN", "Hyper Speed Network" }, 407 | { 394, "CRO", "Crypto Chain" }, 408 | { 395, "UMBRU", "Umbru" }, 409 | { 396, "EVER", "Everscale" }, 410 | { 397, "NEAR", "NEAR Protocol" }, 411 | { 398, "XPC", "XPChain" }, 412 | { 399, "ZOC", "01coin" }, 413 | { 400, "NIX", "NIX" }, 414 | { 401, "UC", "Utopiacoin" }, 415 | { 402, "GALI", "Galilel" }, 416 | { 403, "OLT", "Oneledger" }, 417 | { 404, "XBI", "XBI" }, 418 | { 405, "DONU", "DONU" }, 419 | { 406, "EARTHS", "Earths" }, 420 | { 407, "HDD", "HDDCash" }, 421 | { 408, "SUGAR", "Sugarchain" }, 422 | { 409, "AILE", "AileCoin" }, 423 | { 410, "TENT", "TENT" }, 424 | { 411, "TAN", "Tangerine Network" }, 425 | { 412, "AIN", "AIN" }, 426 | { 413, "MSR", "Masari" }, 427 | { 414, "SUMO", "Sumokoin" }, 428 | { 415, "ETN", "Electroneum" }, 429 | { 416, "BYTZ", "BYTZ" }, 430 | { 417, "WOW", "Wownero" }, 431 | { 418, "XTNC", "XtendCash" }, 432 | { 419, "LTHN", "Lethean" }, 433 | { 420, "NODE", "NodeHost" }, 434 | { 421, "AGM", "Argoneum" }, 435 | { 422, "CCX", "Conceal Network" }, 436 | { 423, "TNET", "Title Network" }, 437 | { 424, "TELOS", "TelosCoin" }, 438 | { 425, "AION", "Aion" }, 439 | { 426, "BC", "Bitcoin Confidential" }, 440 | { 427, "KTV", "KmushiCoin" }, 441 | { 428, "ZCR", "ZCore" }, 442 | { 429, "ERG", "Ergo" }, 443 | { 430, "PESO", "Criptopeso" }, 444 | { 431, "BTC2", "Bitcoin 2" }, 445 | { 432, "XRPHD", "XRPHD" }, 446 | { 433, "WE", "WE Coin" }, 447 | { 434, "KSM", "Kusama" }, 448 | { 435, "PCN", "Peepcoin" }, 449 | { 436, "NCH", "NetCloth" }, 450 | { 437, "ICU", "CHIPO" }, 451 | { 438, "FNSA", "FINSCHIA" }, 452 | { 439, "DTP", "DeVault Token Protocol" }, 453 | { 440, "BTCR", "Bitcoin Royale" }, 454 | { 441, "AERGO", "AERGO" }, 455 | { 442, "XTH", "Dothereum" }, 456 | { 443, "LV", "Lava" }, 457 | { 444, "PHR", "Phore" }, 458 | { 445, "VITAE", "Vitae" }, 459 | { 446, "COCOS", "Cocos-BCX" }, 460 | { 447, "DIN", "Dinero" }, 461 | { 448, "SPL", "Simplicity" }, 462 | { 449, "YCE", "MYCE" }, 463 | { 450, "XLR", "Solaris" }, 464 | { 451, "KTS", "Klimatas" }, 465 | { 452, "DGLD", "DGLD" }, 466 | { 453, "XNS", "Insolar" }, 467 | { 454, "EM", "EMPOW" }, 468 | { 455, "SHN", "ShineBlocks" }, 469 | { 456, "SEELE", "Seele" }, 470 | { 457, "AE", "æternity" }, 471 | { 458, "ODX", "ObsidianX" }, 472 | { 459, "KAVA", "Kava" }, 473 | { 460, "GLEEC", "GLEEC" }, 474 | { 461, "FIL", "Filecoin" }, 475 | { 462, "RUTA", "Rutanio" }, 476 | { 463, "CSDT", "CSDT" }, 477 | { 464, "ETI", "EtherInc" }, 478 | { 465, "ZSLP", "Zclassic Simple Ledger Protocol" }, 479 | { 466, "ERE", "EtherCore" }, 480 | { 467, "DX", "DxChain Token" }, 481 | { 468, "CPS", "Capricoin+" }, 482 | { 469, "BTH", "Bithereum" }, 483 | { 470, "MESG", "MESG" }, 484 | { 471, "FIMK", "FIMK" }, 485 | { 472, "AR", "Arweave" }, 486 | { 473, "OGO", "Origo" }, 487 | { 474, "ROSE", "Oasis Network" }, 488 | { 475, "BARE", "BARE Network" }, 489 | { 476, "GLEEC", "GleecBTC" }, 490 | { 477, "CLR", "Color Coin" }, 491 | { 478, "RNG", "Ring" }, 492 | { 479, "OLO", "Tool Global" }, 493 | { 480, "PEXA", "Pexa" }, 494 | { 481, "MOON", "Mooncoin" }, 495 | { 482, "OCEAN", "Ocean Protocol" }, 496 | { 483, "BNT", "Bluzelle Native" }, 497 | { 484, "AMO", "AMO Blockchain" }, 498 | { 485, "FCH", "FreeCash" }, 499 | { 486, "LAT", "PlatON" }, 500 | { 487, "COIN", "Bitcoin Bank" }, 501 | { 488, "VEO", "Amoveo" }, 502 | { 489, "CCA", "Counos Coin" }, 503 | { 490, "GFN", "Graphene" }, 504 | { 491, "BIP", "Minter Network" }, 505 | { 492, "KPG", "Kunpeng Network" }, 506 | { 493, "FIN", "FINL Chain" }, 507 | { 494, "BAND", "Band" }, 508 | { 495, "DROP", "Dropil" }, 509 | { 496, "BHT", "Bluehelix Chain" }, 510 | { 497, "LYRA", "Scrypta" }, 511 | { 498, "CS", "Credits" }, 512 | { 499, "RUPX", "Rupaya" }, 513 | { 500, "THETA", "Theta" }, 514 | { 501, "SOL", "Solana" }, 515 | { 502, "THT", "ThoughtAI" }, 516 | { 503, "CFX", "Conflux" }, 517 | { 504, "KUMA", "Kumacoin" }, 518 | { 505, "HASH", "Provenance" }, 519 | { 506, "CSPR", "Casper" }, 520 | { 507, "EARTH", "EARTH" }, 521 | { 508, "EGLD", "MultiversX" }, 522 | { 509, "CHI", "Xaya" }, 523 | { 510, "KOTO", "Koto" }, 524 | { 511, "OTC", "θ" }, 525 | { 512, "RXD", "Radiant" }, 526 | { 513, "SEELEN", "Seele-N" }, 527 | { 514, "AETH", "AETH" }, 528 | { 515, "DNA", "Idena" }, 529 | { 516, "VEE", "Virtual Economy Era" }, 530 | { 517, "SIERRA", "SierraCoin" }, 531 | { 518, "LET", "Linkeye" }, 532 | { 519, "BSC", "Bitcoin Smart Contract" }, 533 | { 520, "BTCV", "BitcoinVIP" }, 534 | { 521, "ABA", "Dabacus" }, 535 | { 522, "SCC", "StakeCubeCoin" }, 536 | { 523, "EDG", "Edgeware" }, 537 | { 524, "AMS", "AmsterdamCoin" }, 538 | { 525, "GOSS", "GOSSIP Coin" }, 539 | { 526, "BU", "BUMO" }, 540 | { 527, "GRAM", "GRAM" }, 541 | { 528, "YAP", "Yapstone" }, 542 | { 529, "SCRT", "Secret Network" }, 543 | { 530, "NOVO", "Novo" }, 544 | { 531, "GHOST", "Ghost" }, 545 | { 532, "HST", "HST" }, 546 | { 533, "PRJ", "ProjectCoin" }, 547 | { 534, "YOU", "YOUChain" }, 548 | { 535, "XHV", "Haven Protocol" }, 549 | { 536, "BYND", "Beyondcoin" }, 550 | { 537, "JOYS", "Joys Digital" }, 551 | { 538, "VAL", "Valorbit" }, 552 | { 539, "FLOW", "Flow" }, 553 | { 540, "SMESH", "Spacemesh Coin" }, 554 | { 541, "SCDO", "SCDO" }, 555 | { 542, "IQS", "IQ-Cash" }, 556 | { 543, "BIND", "Compendia" }, 557 | { 544, "COINEVO", "Coinevo" }, 558 | { 545, "SCRIBE", "Scribe" }, 559 | { 546, "HYN", "Hyperion" }, 560 | { 547, "BHP", "BHP" }, 561 | { 548, "BBC", "BigBang Core" }, 562 | { 549, "MKF", "MarketFinance" }, 563 | { 550, "XDC", "XinFin" }, 564 | { 551, "STR", "Straightedge" }, 565 | { 552, "SUM", "Sumcoin" }, 566 | { 553, "HBC", "HuobiChain" }, 567 | { 554, "---", "reserved" }, 568 | { 555, "BCS", "Bitcoin Smart" }, 569 | { 556, "KTS", "Kratos" }, 570 | { 557, "LKR", "Lkrcoin" }, 571 | { 558, "TAO", "Tao" }, 572 | { 559, "XWC", "Whitecoin" }, 573 | { 560, "DEAL", "DEAL" }, 574 | { 561, "NTY", "Nexty" }, 575 | { 562, "TOP", "TOP NetWork" }, 576 | { 563, "---", "reserved" }, 577 | { 564, "AG", "Agoric" }, 578 | { 565, "CICO", "Coinicles" }, 579 | { 566, "IRIS", "Irisnet" }, 580 | { 567, "NCG", "Nine Chronicles" }, 581 | { 568, "LRG", "Large Coin" }, 582 | { 569, "SERO", "Super Zero Protocol" }, 583 | { 570, "BDX", "Beldex" }, 584 | { 571, "CCXX", "Counos X" }, 585 | { 572, "SLS", "Saluscoin" }, 586 | { 573, "SRM", "Serum" }, 587 | { 574, "---", "reserved" }, 588 | { 575, "VIVT", "VIDT Datalink" }, 589 | { 576, "BPS", "BitcoinPoS" }, 590 | { 577, "NKN", "NKN" }, 591 | { 578, "ICL", "ILCOIN" }, 592 | { 579, "BONO", "Bonorum" }, 593 | { 580, "PLC", "PLATINCOIN" }, 594 | { 581, "DUN", "Dune" }, 595 | { 582, "DMCH", "Darmacash" }, 596 | { 583, "CTC", "Creditcoin" }, 597 | { 584, "KELP", "Haidai Network" }, 598 | { 585, "GBCR", "GoldBCR" }, 599 | { 586, "XDAG", "XDAG" }, 600 | { 587, "PRV", "Incognito Privacy" }, 601 | { 588, "SCAP", "SafeCapital" }, 602 | { 589, "TFUEL", "Theta Fuel" }, 603 | { 590, "GTM", "Gentarium" }, 604 | { 591, "RNL", "RentalChain" }, 605 | { 592, "GRIN", "Grin" }, 606 | { 593, "MWC", "MimbleWimbleCoin" }, 607 | { 594, "DOCK", "Dock" }, 608 | { 595, "POLYX", "Polymesh" }, 609 | { 596, "DIVER", "Divergenti" }, 610 | { 597, "XEP", "Electra Protocol" }, 611 | { 598, "APN", "Apron" }, 612 | { 599, "TFC", "Turbo File Coin" }, 613 | { 600, "UTE", "Unit-e" }, 614 | { 601, "MTC", "Metacoin" }, 615 | { 602, "NC", "NobodyCash" }, 616 | { 603, "XINY", "Xinyuehu" }, 617 | { 604, "DYN", "Dynamo" }, 618 | { 605, "BUFS", "Buffer" }, 619 | { 606, "STOS", "Stratos" }, 620 | { 607, "TON", "TON" }, 621 | { 608, "TAFT", "TAFT" }, 622 | { 609, "HYDRA", "HYDRA" }, 623 | { 610, "NOR", "Noir" }, 624 | { 611, "", "Manta Network Private Asset" }, 625 | { 612, "", "Calamari Network Private Asset" }, 626 | { 613, "WCN", "Widecoin" }, 627 | { 614, "OPT", "Optimistic Ethereum" }, 628 | { 615, "PSWAP", "PolkaSwap" }, 629 | { 616, "VAL", "Validator" }, 630 | { 617, "XOR", "Sora" }, 631 | { 618, "SSP", "SmartShare" }, 632 | { 619, "DEI", "DeimosX" }, 633 | { 620, "---", "reserved" }, 634 | { 621, "ZERO", "Singularity" }, 635 | { 622, "ALPHA", "AlphaDAO" }, 636 | { 623, "BDECO", "BDCashProtocol Ecosystem" }, 637 | { 624, "NOBL", "Nobility" }, 638 | { 625, "EAST", "Eastcoin" }, 639 | { 626, "KDA", "Kadena" }, 640 | { 627, "SOUL", "Phantasma" }, 641 | { 628, "LORE", "Gitopia" }, 642 | { 629, "FNR", "Fincor" }, 643 | { 630, "NEXUS", "Nexus" }, 644 | { 631, "QTZ", "Quartz" }, 645 | { 632, "MAS", "Massa" }, 646 | { 633, "CALL", "Callchain" }, 647 | { 634, "VAL", "Validity" }, 648 | { 635, "POKT", "Pocket Network" }, 649 | { 636, "EMIT", "EMIT" }, 650 | { 637, "APTOS", "Aptos" }, 651 | { 638, "ADON", "ADON" }, 652 | { 639, "BTSG", "BitSong" }, 653 | { 640, "LFC", "Leofcoin" }, 654 | { 641, "KCS", "KuCoin Shares" }, 655 | { 642, "KCC", "KuCoin Community Chain" }, 656 | { 643, "AZERO", "Aleph Zero" }, 657 | { 644, "TREE", "Tree" }, 658 | { 645, "LX", "Lynx" }, 659 | { 646, "XLN", "Lunarium" }, 660 | { 647, "CIC", "CIC Chain" }, 661 | { 648, "ZRB", "Zarb" }, 662 | { 649, "---", "reserved" }, 663 | { 650, "UCO", "Archethic" }, 664 | { 651, "SFX", "Safex Cash" }, 665 | { 652, "SFT", "Safex Token" }, 666 | { 653, "WSFX", "Wrapped Safex Cash" }, 667 | { 654, "USDG", "US Digital Gold" }, 668 | { 655, "WMP", "WAMP" }, 669 | { 656, "EKTA", "Ekta" }, 670 | { 657, "YDA", "YadaCoin" }, 671 | { 658, "WHIVE", "Whive" }, 672 | { 659, "KOIN", "Koinos" }, 673 | { 660, "PIRATE", "PirateCash" }, 674 | { 661, "UNQ", "Unique" }, 675 | { 662, "ULM", "UltonSmartchain" }, 676 | { 663, "SFRX", "EtherGem Sapphire" }, 677 | { 664, "BSTY", "GlobalBoost-Y" }, 678 | { 665, "IMP", "Impact Protocol" }, 679 | { 666, "ACT", "Achain" }, 680 | { 667, "PRKL", "Perkle" }, 681 | { 668, "SSC", "SelfSell" }, 682 | { 669, "GC", "GateChain" }, 683 | { 670, "PLGR", "Pledger" }, 684 | { 671, "MPLGR", "Pledger" }, 685 | { 672, "KNOX", "Knox" }, 686 | { 673, "ZED", "ZED" }, 687 | { 674, "CNDL", "Candle" }, 688 | { 675, "WLKR", "Walker Crypto Innovation Index" }, 689 | { 676, "WLKRR", "Walker" }, 690 | { 677, "YUNGE", "Yunge" }, 691 | { 678, "Voken", "Voken" }, 692 | { 679, "APL", "Apollo" }, 693 | { 680, "Evrynet", "Evrynet" }, 694 | { 681, "NENG", "Nengcoin" }, 695 | { 682, "CHTA", "Cheetahcoin" }, 696 | { 683, "ALEO", "Aleo Network" }, 697 | { 684, "HMS", "Hemis" }, 698 | { 685, "OAS", "Oasys" }, 699 | { 686, "KAR", "Karura Network" }, 700 | { 687, "FLON", "FullOn Network" }, 701 | { 688, "CET", "CoinEx Chain" }, 702 | { 689, "XLINK", "XLink Chain" }, 703 | { 690, "KLV", "KleverChain" }, 704 | { 691, "TNT", "Tangle" }, 705 | { 692, "GTG", "Gotigin" }, 706 | { 693, "NET", "RealityNet" }, 707 | { 694, "VTBC", "VTB Community" }, 708 | { 695, "DIONE", "Odyssey Chain" }, 709 | { 696, "LUM", "Lumos" }, 710 | { 697, "AVA", "Avalon" }, 711 | { 698, "VEIL", "Veil" }, 712 | { 699, "GTB", "GotaBit" }, 713 | { 700, "XDAI", "xDai" }, 714 | { 701, "COM", "Commercio" }, 715 | { 702, "CCC", "Commercio Cash Credit" }, 716 | { 703, "SNR", "Sonr" }, 717 | { 704, "RAQ", "Ra Quantum" }, 718 | { 705, "PEG", "Pegasus Token" }, 719 | { 706, "LKG", "Lionking" }, 720 | { 707, "MCOIN", "Moneta Coin" }, 721 | { 708, "---", "reserved" }, 722 | { 709, "AVAIL", "Avail" }, 723 | { 710, "FURY", "Highbury" }, 724 | { 711, "CHC", "Chaincoin" }, 725 | { 712, "SERF", "Serfnet" }, 726 | { 713, "XTL", "Katal Chain" }, 727 | { 714, "BNB", "Binance" }, 728 | { 715, "SIN", "Sinovate" }, 729 | { 716, "DLN", "Delion" }, 730 | { 717, "BONTE", "Bontecoin" }, 731 | { 718, "PEER", "Peer" }, 732 | { 719, "ZET", "Zetacoin" }, 733 | { 720, "ABY", "Artbyte" }, 734 | { 721, "PGX", "Mirai Chain" }, 735 | { 722, "IL8P", "InfiniLooP" }, 736 | { 723, "VOI", "Voi" }, 737 | { 724, "XVC", "Vanillacash" }, 738 | { 725, "MCX", "MultiCash" }, 739 | { 726, "TARA", "Taraxa" }, 740 | { 727, "BLU", "BluCrates" }, 741 | { 728, "BFC", "BFC" }, 742 | { 729, "DCC", "DecentraCast" }, 743 | { 730, "HEALIOS", "Tenacity" }, 744 | { 731, "BMK", "Bitmark" }, 745 | { 732, "", "" }, 746 | { 733, "TBC", "TBChat" }, 747 | { 734, "DENTX", "DENTNet" }, 748 | { 735, "", "" }, 749 | { 736, "", "" }, 750 | { 737, "ATOP", "Financial Blockchain" }, 751 | { 738, "BTE", "Bitweb" }, 752 | { 739, "DPC", "Dpowcoin (DualPowCoin)" }, 753 | { 740, "MDC", "MyDataCoin" }, 754 | { 741, "RIV", "Rigvid" }, 755 | { 742, "", "" }, 756 | { 743, "", "" }, 757 | { 744, "DUSK", "Dusk" }, 758 | { 745, "", "" }, 759 | { 746, "", "" }, 760 | { 747, "CFG", "Centrifuge" }, 761 | { 748, "", "" }, 762 | { 749, "", "" }, 763 | { 750, "XPRT", "Persistence" }, 764 | { 751, "", "" }, 765 | { 752, "", "" }, 766 | { 753, "", "Age X25519 Encryption" }, 767 | { 754, "", "Age NIST Encryption" }, 768 | { 755, "", "" }, 769 | { 756, "", "" }, 770 | { 757, "HONEY", "HoneyWood" }, 771 | { 758, "XDD", "XDDCoin" }, 772 | { 759, "", "" }, 773 | { 760, "", "" }, 774 | { 761, "", "" }, 775 | { 762, "", "" }, 776 | { 763, "", "" }, 777 | { 764, "", "" }, 778 | { 765, "TGN", "Tagion" }, 779 | { 766, "", "" }, 780 | { 767, "", "" }, 781 | { 768, "BALLZ", "Ballzcoin" }, 782 | { 769, "", "" }, 783 | { 770, "COSA", "Cosanta" }, 784 | { 771, "BR", "BR" }, 785 | { 772, "", "" }, 786 | { 773, "CSB", "CosmoBliss" }, 787 | { 774, "", "" }, 788 | { 775, "PLSR", "Pulsar Coin" }, 789 | { 776, "KEY", "Keymaker Coin" }, 790 | { 777, "BTW", "Bitcoin World" }, 791 | { 778, "", "" }, 792 | { 779, "", "" }, 793 | { 780, "PLCUC", "PLC Ultima Classic" }, 794 | { 781, "PLCUX", "PLC Ultima X" }, 795 | { 782, "PLCU", "PLC Ultima" }, 796 | { 783, "SMARTBC", "SMART Blockchain" }, 797 | { 784, "SUI", "Sui" }, 798 | { 785, "ULTIMA", "ULTIMA" }, 799 | { 786, "UIDD", "UIDD" }, 800 | { 787, "ACA", "Acala" }, 801 | { 788, "BNC", "Bifrost" }, 802 | { 789, "TAU", "Lamden" }, 803 | { 790, "", "" }, 804 | { 791, "", "" }, 805 | { 792, "", "" }, 806 | { 793, "", "" }, 807 | { 794, "INTR", "Interlay" }, 808 | { 795, "KINT", "Kintsugi" }, 809 | { 796, "", "" }, 810 | { 797, "", "" }, 811 | { 798, "", "" }, 812 | { 799, "PDEX", "Polkadex" }, 813 | { 800, "BEET", "Beetle Coin" }, 814 | { 801, "DST", "DSTRA" }, 815 | { 802, "CY", "Cyberyen" }, 816 | { 803, "RYME", "Ryme Network" }, 817 | { 804, "ZKS", "zkSync" }, 818 | { 805, "SCASH", "Scash" }, 819 | { 806, "", "" }, 820 | { 807, "", "" }, 821 | { 808, "QVT", "Qvolta" }, 822 | { 809, "SDN", "Shiden Network" }, 823 | { 810, "ASTR", "Astar Network" }, 824 | { 811, "---", "reserved" }, 825 | { 812, "", "" }, 826 | { 813, "MEER", "Qitmeer" }, 827 | { 814, "", "" }, 828 | { 815, "FACT", "ImFACT" }, 829 | { 816, "FSC", "FSC" }, 830 | { 817, "", "" }, 831 | { 818, "VET", "VeChain Token" }, 832 | { 819, "REEF", "Reef" }, 833 | { 820, "CLO", "Callisto" }, 834 | { 821, "", "" }, 835 | { 822, "BDB", "BigchainDB" }, 836 | { 823, "", "" }, 837 | { 824, "RBNT", "Redbelly Network" }, 838 | { 825, "", "" }, 839 | { 826, "", "" }, 840 | { 827, "ACE", "Endurance" }, 841 | { 828, "CCN", "ComputeCoin" }, 842 | { 829, "BBA", "BBACHAIN" }, 843 | { 830, "", "" }, 844 | { 831, "CRUZ", "cruzbit" }, 845 | { 832, "SAPP", "Sapphire" }, 846 | { 833, "777", "Jackpot" }, 847 | { 834, "KYAN", "Kyanite" }, 848 | { 835, "AZR", "Azzure" }, 849 | { 836, "CFL", "CryptoFlow" }, 850 | { 837, "DASHD", "Dash Diamond" }, 851 | { 838, "TRTT", "Trittium" }, 852 | { 839, "UCR", "Ultra Clear" }, 853 | { 840, "PNY", "Peony" }, 854 | { 841, "BECN", "Beacon" }, 855 | { 842, "MONK", "Monk" }, 856 | { 843, "SAGA", "CryptoSaga" }, 857 | { 844, "SUV", "Suvereno" }, 858 | { 845, "ESK", "EskaCoin" }, 859 | { 846, "OWO", "OneWorld Coin" }, 860 | { 847, "PEPS", "PEPS Coin" }, 861 | { 848, "BIR", "Birake" }, 862 | { 849, "MOBIC", "MobilityCoin" }, 863 | { 850, "FLS", "Flits" }, 864 | { 851, "FRECO", "Freco" }, 865 | { 852, "DSM", "Desmos" }, 866 | { 853, "PRCY", "PRCY Coin" }, 867 | { 854, "", "" }, 868 | { 855, "", "" }, 869 | { 856, "", "" }, 870 | { 857, "", "" }, 871 | { 858, "HVH", "HAVAH" }, 872 | { 859, "", "" }, 873 | { 860, "XBIT", "XBIT Coin" }, 874 | { 861, "", "" }, 875 | { 862, "", "" }, 876 | { 863, "", "" }, 877 | { 864, "CVM", "Convex" }, 878 | { 865, "", "" }, 879 | { 866, "MOB", "MobileCoin" }, 880 | { 867, "", "" }, 881 | { 868, "IF", "Infinitefuture" }, 882 | { 869, "", "" }, 883 | { 870, "", "" }, 884 | { 871, "", "" }, 885 | { 872, "", "" }, 886 | { 873, "QUORUM", "Quorum" }, 887 | { 874, "", "" }, 888 | { 875, "", "" }, 889 | { 876, "", "" }, 890 | { 877, "NAM", "Namada" }, 891 | { 878, "SCR", "Scorum Network" }, 892 | { 879, "", "" }, 893 | { 880, "LUM", "Lum Network" }, 894 | { 881, "", "" }, 895 | { 882, "", "" }, 896 | { 883, "ZBC", "ZooBC" }, 897 | { 884, "", "" }, 898 | { 885, "", "" }, 899 | { 886, "ADF", "AD Token" }, 900 | { 887, "", "" }, 901 | { 888, "NEO", "NEO" }, 902 | { 889, "TOMO", "TOMO" }, 903 | { 890, "XSEL", "Seln" }, 904 | { 891, "", "" }, 905 | { 892, "", "" }, 906 | { 893, "", "" }, 907 | { 894, "", "" }, 908 | { 895, "", "" }, 909 | { 896, "LKSC", "LKSCoin" }, 910 | { 897, "", "" }, 911 | { 898, "AS", "Assetchain" }, 912 | { 899, "XEC", "eCash" }, 913 | { 900, "LMO", "Lumeneo" }, 914 | { 901, "NXT", "NxtMeta" }, 915 | { 902, "", "" }, 916 | { 903, "", "" }, 917 | { 904, "HNT", "Helium" }, 918 | { 905, "", "" }, 919 | { 906, "XPX", "Sirius" }, 920 | { 907, "FIS", "StaFi" }, 921 | { 908, "", "" }, 922 | { 909, "SGE", "Saage" }, 923 | { 910, "", "" }, 924 | { 911, "GERT", "Gert" }, 925 | { 912, "", "" }, 926 | { 913, "VARA", "Vara Network" }, 927 | { 914, "", "" }, 928 | { 915, "", "" }, 929 | { 916, "META", "Metadium" }, 930 | { 917, "FRA", "Findora" }, 931 | { 918, "", "" }, 932 | { 919, "CCD", "Concordium" }, 933 | { 920, "", "" }, 934 | { 921, "AVN", "Avian Network" }, 935 | { 922, "", "" }, 936 | { 923, "", "" }, 937 | { 924, "", "" }, 938 | { 925, "DIP", "Dipper Network" }, 939 | { 926, "", "" }, 940 | { 927, "", "" }, 941 | { 928, "GHM", "HermitMatrixNetwork" }, 942 | { 929, "", "" }, 943 | { 930, "", "" }, 944 | { 931, "RUNE", "THORChain (RUNE)" }, 945 | { 932, "", "" }, 946 | { 933, "", "" }, 947 | { 934, "", "" }, 948 | { 935, "", "" }, 949 | { 936, "", "" }, 950 | { 937, "", "" }, 951 | { 938, "MGO", "Mango Network" }, 952 | { 939, "AB", "Argot Protocol" }, 953 | { 940, "", "" }, 954 | { 941, "---", "reserved" }, 955 | { 942, "KCN", "Kylacoin" }, 956 | { 943, "LCN", "Lyncoin" }, 957 | { 944, "", "" }, 958 | { 945, "UNLOCK", "Jasiri protocol" }, 959 | { 946, "", "" }, 960 | { 947, "", "" }, 961 | { 948, "", "" }, 962 | { 949, "", "" }, 963 | { 950, "", "" }, 964 | { 951, "", "" }, 965 | { 952, "", "" }, 966 | { 953, "", "" }, 967 | { 954, "", "" }, 968 | { 955, "LTP", "LifetionCoin" }, 969 | { 956, "", "" }, 970 | { 957, "", "" }, 971 | { 958, "", "KickSoccer" }, 972 | { 959, "", "" }, 973 | { 960, "VKAX", "Vkax" }, 974 | { 961, "", "" }, 975 | { 962, "", "" }, 976 | { 963, "", "" }, 977 | { 964, "", "" }, 978 | { 965, "", "" }, 979 | { 966, "MATIC", "Matic" }, 980 | { 967, "", "" }, 981 | { 968, "UNW", "UNW" }, 982 | { 969, "QI", "Quai Network" }, 983 | { 970, "TWINS", "TWINS" }, 984 | { 971, "", "" }, 985 | { 972, "", "" }, 986 | { 973, "", "" }, 987 | { 974, "", "" }, 988 | { 975, "", "" }, 989 | { 976, "", "" }, 990 | { 977, "TLOS", "Telos" }, 991 | { 978, "", "" }, 992 | { 979, "", "" }, 993 | { 980, "", "" }, 994 | { 981, "TAFECO", "Taf ECO Chain" }, 995 | { 982, "", "" }, 996 | { 983, "", "" }, 997 | { 984, "", "" }, 998 | { 985, "AU", "Autonomy" }, 999 | { 986, "", "" }, 1000 | { 987, "VCG", "VipCoin" }, 1001 | { 988, "XAZAB", "Xazab core" }, 1002 | { 989, "AIOZ", "AIOZ" }, 1003 | { 990, "CORE", "Coreum" }, 1004 | { 991, "PEC", "Phoenix" }, 1005 | { 992, "UNT", "Unit" }, 1006 | { 993, "XRB", "X Currency" }, 1007 | { 994, "QUAI", "Quai Network" }, 1008 | { 995, "CAPS", "Ternoa" }, 1009 | { 996, "OKT", "OKChain Token" }, 1010 | { 997, "SUM", "Solidum" }, 1011 | { 998, "LBTC", "Lightning Bitcoin" }, 1012 | { 999, "BCD", "Bitcoin Diamond" }, 1013 | { 1000, "BTN", "Bitcoin New" }, 1014 | { 1001, "TT", "ThunderCore" }, 1015 | { 1002, "BKT", "BanKitt" }, 1016 | { 1003, "NODL", "Nodle" }, 1017 | { 1004, "PCOIN", "PCOIN" }, 1018 | { 1005, "TAO", "Bittensor" }, 1019 | { 1006, "HSK", "HashKey Chain" }, 1020 | { 1007, "FTM", "Fantom" }, 1021 | { 1008, "RPG", "RPG" }, 1022 | { 1009, "LAKE", "iconLake" }, 1023 | { 1010, "HT", "Huobi ECO Chain" }, 1024 | { 1011, "ELV", "Eluvio" }, 1025 | { 1012, "JOC", "Japan Open Chain" }, 1026 | { 1013, "BIC", "Beincrypto" }, 1027 | { 1014, "JOY", "Joystream" }, 1028 | { 1015, "ZCX", "ZEN Exchange Token" }, 1029 | { 1016, "---", "reserved" }, 1030 | { 1020, "EVC", "Evrice" }, 1031 | { 1022, "XRD", "Radix DLT" }, 1032 | { 1023, "ONE", "HARMONY-ONE (Legacy)" }, 1033 | { 1024, "ONT", "Ontology" }, 1034 | { 1025, "CZZ", "Classzz" }, 1035 | { 1026, "KEX", "Kira Exchange Token" }, 1036 | { 1027, "MCM", "Mochimo" }, 1037 | { 1028, "PLS", "Pulse Coin" }, 1038 | { 1032, "BTCR", "BTCR" }, 1039 | { 1042, "MFID", "Moonfish ID" }, 1040 | { 1111, "BBC", "Big Bitcoin" }, 1041 | { 1116, "CORE", "Core" }, 1042 | { 1120, "RISE", "RISE" }, 1043 | { 1122, "CMT", "CyberMiles Token" }, 1044 | { 1128, "ETSC", "Ethereum Social" }, 1045 | { 1129, "DFI", "DeFiChain" }, 1046 | { 1130, "DFI", "DeFiChain EVM Network" }, 1047 | { 1134, "MESH", "StateMesh" }, 1048 | { 1137, "$DAG", "Constellation Labs" }, 1049 | { 1145, "CDY", "Bitcoin Candy" }, 1050 | { 1155, "ENJ", "Enjin Coin" }, 1051 | { 1170, "HOO", "Hoo Smart Chain" }, 1052 | { 1234, "ALPH", "Alephium" }, 1053 | { 1236, "", "Masca" }, 1054 | { 1237, "", "Nostr" }, 1055 | { 1280, "", "Kudos Setler" }, 1056 | { 1284, "GLMR", "Moonbeam" }, 1057 | { 1285, "MOVR", "Moonriver" }, 1058 | { 1286, "DSG", "Dessage Social Protocol" }, 1059 | { 1298, "WPC", "Wpc" }, 1060 | { 1308, "WEI", "WEI" }, 1061 | { 1312, "BITS", "Entropy" }, 1062 | { 1337, "DFC", "Defcoin" }, 1063 | { 1338, "IRON", "Iron Fish" }, 1064 | { 1348, "ISLM", "IslamicCoin" }, 1065 | { 1397, "HYC", "Hycon" }, 1066 | { 1410, "TENTSLP", "TENT Simple Ledger Protocol" }, 1067 | { 1510, "XSC", "XT Smart Chain" }, 1068 | { 1512, "AAC", "Double-A Chain" }, 1069 | { 1524, "", "Taler" }, 1070 | { 1533, "BEAM", "Beam" }, 1071 | { 1536, "GAS", "BubiChain" }, 1072 | { 1540, "ATHENA", "Athena" }, 1073 | { 1551, "SDK", "Sovereign SDK" }, 1074 | { 1555, "APC", "Apc Chain" }, 1075 | { 1616, "ELF", "AELF" }, 1076 | { 1618, "AUDL", "AUDL" }, 1077 | { 1620, "ATH", "Atheios" }, 1078 | { 1627, "LUME", "Lume Web" }, 1079 | { 1642, "NEW", "Newton" }, 1080 | { 1657, "BTA", "Btachain" }, 1081 | { 1668, "NEOX", "Neoxa" }, 1082 | { 1669, "MEWC", "Meowcoin" }, 1083 | { 1688, "BCX", "BitcoinX" }, 1084 | { 1729, "XTZ", "Tezos" }, 1085 | { 1776, "LBTC", "Liquid BTC" }, 1086 | { 1777, "BBP", "Biblepay" }, 1087 | { 1784, "JPYS", "JPY Stablecoin" }, 1088 | { 1789, "VEGA", "Vega Protocol" }, 1089 | { 1815, "ADA", "Cardano" }, 1090 | { 1818, "CUBE", "Cube Chain Native Token" }, 1091 | { 1888, "ZTX", "Zetrix" }, 1092 | { 1899, "XEC", "eCash token" }, 1093 | { 1900, "XNA", "Neurai" }, 1094 | { 1901, "CLC", "Classica" }, 1095 | { 1907, "BITCI", "Bitcicoin" }, 1096 | { 1918, "BKC", "Briskcoin" }, 1097 | { 1919, "VIPS", "VIPSTARCOIN" }, 1098 | { 1926, "CITY", "City Coin" }, 1099 | { 1951, "ESA", "Esa" }, 1100 | { 1952, "ESC", "EsaCoin" }, 1101 | { 1955, "XX", "xx coin" }, 1102 | { 1977, "XMX", "Xuma" }, 1103 | { 1984, "TRTL", "TurtleCoin" }, 1104 | { 1985, "SLRT", "Solarti Chain" }, 1105 | { 1986, "QTH", "Qing Tong Horizon" }, 1106 | { 1987, "EGEM", "EtherGem" }, 1107 | { 1988, "MIRA", "Mira Chain" }, 1108 | { 1989, "HODL", "HOdlcoin" }, 1109 | { 1990, "PHL", "Placeholders" }, 1110 | { 1991, "SC", "Sia" }, 1111 | { 1996, "MYT", "Mineyourtime" }, 1112 | { 1997, "POLIS", "Polis" }, 1113 | { 1998, "XMCC", "Monoeci" }, 1114 | { 1999, "COLX", "ColossusXT" }, 1115 | { 2000, "GIN", "GinCoin" }, 1116 | { 2001, "MNP", "MNPCoin" }, 1117 | { 2002, "MLN", "Miraland" }, 1118 | { 2003, "ISNA", "iSarrana" }, 1119 | { 2015, "TEER", "Integritee" }, 1120 | { 2017, "KIN", "Kin" }, 1121 | { 2018, "EOSC", "EOSClassic" }, 1122 | { 2019, "GBT", "GoldBean Token" }, 1123 | { 2020, "PKC", "PKC" }, 1124 | { 2021, "SKT", "Sukhavati" }, 1125 | { 2024, "USBC", "Universal Ledger USBC" }, 1126 | { 2022, "XHT", "Xinghuo Token" }, 1127 | { 2023, "COC", "Chat On Chain" }, 1128 | { 2025, "ROCK", "Zenrock Labs" }, 1129 | { 2026, "ASTRON", "ASTRON Token" }, 1130 | { 2046, "ANY", "Any" }, 1131 | { 2048, "MCASH", "MCashChain" }, 1132 | { 2049, "TRUE", "TrueChain" }, 1133 | { 2050, "MOVO", "Movo Smart Chain" }, 1134 | { 2086, "KILT", "KILT Spiritnet" }, 1135 | { 2091, "FRQCY", "Frequency" }, 1136 | { 2109, "SAMA", "Exosama Network" }, 1137 | { 2112, "IoTE", "IoTE" }, 1138 | { 2121, "CBTC", "Coordinate BTC (Anduro)" }, 1139 | { 2125, "BAY", "BitBay" }, 1140 | { 2137, "XRG", "Ergon" }, 1141 | { 2199, "SAMA", "Moonsama Network" }, 1142 | { 2221, "ASK", "ASK" }, 1143 | { 2222, "CWEB", "Coinweb" }, 1144 | { 2285, "", "Qiyi Chain" }, 1145 | { 2301, "QTUM", "QTUM" }, 1146 | { 2302, "ETP", "Metaverse" }, 1147 | { 2303, "GXC", "GXChain" }, 1148 | { 2304, "CRP", "CranePay" }, 1149 | { 2305, "ELA", "Elastos" }, 1150 | { 2338, "SNOW", "Snowblossom" }, 1151 | { 2365, "XIN", "Mixin" }, 1152 | { 2500, "NEXI", "Nexi" }, 1153 | { 2570, "AOA", "Aurora" }, 1154 | { 2686, "AIPG", "AIPowerGrid" }, 1155 | { 2718, "NAS", "Nebulas" }, 1156 | { 2809, "LAN", "Lanify" }, 1157 | { 2894, "REOSC", "REOSC Ecosystem" }, 1158 | { 2941, "BND", "Blocknode" }, 1159 | { 3000, "SM", "Stealth Message" }, 1160 | { 3003, "LUX", "LUX" }, 1161 | { 3030, "HBAR", "Hedera HBAR" }, 1162 | { 3077, "COS", "Contentos" }, 1163 | { 3276, "CCC", "CodeChain" }, 1164 | { 3344, "PLMC", "Polimec" }, 1165 | { 3333, "SXP", "Solar" }, 1166 | { 3338, "PEAQ", "peaq" }, 1167 | { 3377, "ROI", "ROIcoin" }, 1168 | { 3381, "DYN", "Dynamic" }, 1169 | { 3383, "SEQ", "Sequence" }, 1170 | { 3434, "PEPE", "Pepecoin Core" }, 1171 | { 3501, "JFIN", "JFIN Coin" }, 1172 | { 3552, "DEO", "Destocoin" }, 1173 | { 3564, "DST", "DeStream" }, 1174 | { 3601, "CY", "Cybits" }, 1175 | { 3757, "MPC", "Partisia Blockchain" }, 1176 | { 3840, "RED", "ReDeFi RED" }, 1177 | { 4040, "FC8", "FCH Network" }, 1178 | { 4096, "YEE", "YeeCo" }, 1179 | { 4218, "IOTA", "IOTA" }, 1180 | { 4219, "SMR", "Shimmer" }, 1181 | { 4242, "AXE", "Axe" }, 1182 | { 4343, "XYM", "Symbol" }, 1183 | { 4444, "C4E", "Chain4Energy" }, 1184 | { 4646, "MST", "MST" }, 1185 | { 4919, "XVM", "Venidium" }, 1186 | { 4976, "VARA", "Vara" }, 1187 | { 4999, "BXN", "BlackFort Exchange Network" }, 1188 | { 5000, "V12", "Vet The Vote" }, 1189 | { 5006, "SBC", "Senior Blockchain" }, 1190 | { 5248, "FIC", "FIC" }, 1191 | { 5353, "HNS", "Handshake" }, 1192 | { 5404, "ISK", "ISKRA" }, 1193 | { 5467, "ALTME", "ALTME" }, 1194 | { 5555, "FUND", "Unification" }, 1195 | { 5757, "STX", "Stacks" }, 1196 | { 5895, "VOW", "VowChain VOW" }, 1197 | { 5920, "SLU", "SILUBIUM" }, 1198 | { 5995, "DUSK", "Dusk Network" }, 1199 | { 6060, "GO", "GoChain GO" }, 1200 | { 6144, "DTS", "Datos" }, 1201 | { 6174, "MOI", "My Own Internet" }, 1202 | { 6278, "STEAMX", "Rails Network Mainnet" }, 1203 | { 6532, "UM", "Penumbra" }, 1204 | { 6599, "RSC", "Royal Sports City" }, 1205 | { 6666, "BPA", "Bitcoin Pizza" }, 1206 | { 6688, "SAFE", "SAFE" }, 1207 | { 6779, "COTI", "COTI" }, 1208 | { 6969, "ROGER", "TheHolyrogerCoin" }, 1209 | { 7000, "ZETA", "ZetaChain" }, 1210 | { 7027, "ELLA", "Ella the heart" }, 1211 | { 7028, "AA", "Arthera" }, 1212 | { 7070, "DOI", "Doichain" }, 1213 | { 7091, "TOPL", "Topl" }, 1214 | { 7272, "ABTC", "Alys BTC (Anduro)" }, 1215 | { 7331, "KLY", "KLYNTAR" }, 1216 | { 7341, "SHFT", "Shyft" }, 1217 | { 7518, "MEV", "MEVerse" }, 1218 | { 7576, "ADIL", "ADIL Chain" }, 1219 | { 7777, "BTV", "Bitvote" }, 1220 | { 7779, "CPV", "Compverse" }, 1221 | { 8000, "SKY", "Skycoin" }, 1222 | { 8017, "ISC", "iSunCoin" }, 1223 | { 8080, "", "DSRV" }, 1224 | { 8181, "BOC", "BeOne Chain" }, 1225 | { 8192, "PAC", "pacprotocol" }, 1226 | { 8217, "KAIA", "KAIA" }, 1227 | { 8339, "BTQ", "BitcoinQuark" }, 1228 | { 8444, "XCH", "Chia" }, 1229 | { 8453, "", "Base" }, 1230 | { 8520, "---", "reserved" }, 1231 | { 8680, "PLMNT", "Planetmint" }, 1232 | { 8732, "BLN", "Bullions" }, 1233 | { 8738, "ALPH", "Alph Network" }, 1234 | { 8866, "GGX", "Golden Gate" }, 1235 | { 8886, "GGXT", "Golden Gate Sydney" }, 1236 | { 8888, "SBTC", "Super Bitcoin" }, 1237 | { 8964, "NULS", "NULS" }, 1238 | { 8997, "BBC", "Babacoin" }, 1239 | { 8998, "JGC", "JagoanCoin" }, 1240 | { 8999, "BTP", "Bitcoin Pay" }, 1241 | { 9000, "AVAX", "Avalanche" }, 1242 | { 9001, "ARB1", "Arbitrum" }, 1243 | { 9002, "BOBA", "Boba" }, 1244 | { 9003, "LOOP", "Loopring" }, 1245 | { 9004, "STRK", "StarkNet" }, 1246 | { 9005, "AVAXC", "Avalanche C-Chain" }, 1247 | { 9006, "BSC", "Binance Smart Chain" }, 1248 | { 9797, "NRG", "Energi" }, 1249 | { 9888, "BTF", "Bitcoin Faith" }, 1250 | { 9969, "OSMI", "Osmium" }, 1251 | { 9999, "GOD", "Bitcoin God" }, 1252 | { 10000, "FO", "FIBOS" }, 1253 | { 10001, "SPACE", "Space" }, 1254 | { 10007, "S", "SONIC" }, 1255 | { 10111, "DHP", "dHealth" }, 1256 | { 10226, "RTM", "Raptoreum" }, 1257 | { 10242, "AA", "Arthera" }, 1258 | { 10291, "XRC", "XRhodium" }, 1259 | { 10507, "NUM", "Numbers Protocol" }, 1260 | { 10605, "XPI", "Lotus" }, 1261 | { 11111, "ESS", "Essentia One" }, 1262 | { 11742, "VARCH", "InvArch" }, 1263 | { 11743, "TNKR", "Tinkernet" }, 1264 | { 12345, "IPOS", "IPOS" }, 1265 | { 12586, "MINA", "Mina" }, 1266 | { 13107, "BTY", "BitYuan" }, 1267 | { 13108, "YCC", "Yuan Chain Coin" }, 1268 | { 13381, "PHX", "Phoenix" }, 1269 | { 14001, "WAX", "Worldwide Asset Exchange" }, 1270 | { 15845, "SDGO", "SanDeGo" }, 1271 | { 16181, "XTX", "Totem Live Network" }, 1272 | { 16754, "ARDR", "Ardor" }, 1273 | { 18000, "MTR", "Meter" }, 1274 | { 19165, "SAFE", "Safecoin" }, 1275 | { 19167, "FLUX", "Flux" }, 1276 | { 19169, "RITO", "Ritocoin" }, 1277 | { 19788, "ML", "Mintlayer" }, 1278 | { 20036, "XND", "ndau" }, 1279 | { 21004, "C4EI", "c4ei" }, 1280 | { 21337, "XAH", "Xahau" }, 1281 | { 21888, "PAC", "Pactus" }, 1282 | { 22504, "PWR", "PWRcoin" }, 1283 | { 23000, "EPIC", "Epic Cash" }, 1284 | { 25252, "BELL", "Bellcoin" }, 1285 | { 25718, "CHX", "Own" }, 1286 | { 26417, "G1", "Ğ1" }, 1287 | { 29223, "NEXA", "Nexa" }, 1288 | { 30001, "---", "reserved" }, 1289 | { 31102, "ESN", "EtherSocial Network" }, 1290 | { 31337, "", "ThePower" }, 1291 | { 33416, "TEO", "Trust Eth reOrigin" }, 1292 | { 33878, "BTCS", "Bitcoin Stake" }, 1293 | { 34952, "BTT", "ByteTrade" }, 1294 | { 37992, "FXTC", "FixedTradeCoin" }, 1295 | { 39321, "AMA", "Amabig" }, 1296 | { 42069, "FACT", "FACT0RN" }, 1297 | { 43028, "AXIV", "AXIV" }, 1298 | { 47803, "BAX", "BAX" }, 1299 | { 49262, "EVE", "evan" }, 1300 | { 49344, "STASH", "STASH" }, 1301 | { 52752, "CELO", "Celo" }, 1302 | { 54176, "OVER", "OverProtocol" }, 1303 | { 61616, "TH", "TianHe" }, 1304 | { 65536, "KETH", "Krypton World" }, 1305 | { 69420, "GRLC", "Garlicoin" }, 1306 | { 70007, "GWL", "Gewel" }, 1307 | { 83293, "QUBIC", "Qubic" }, 1308 | { 77777, "ZYN", "Wethio" }, 1309 | { 88888, "RYO", "c0ban" }, 1310 | { 99999, "WICC", "Waykichain" }, 1311 | { 100500, "HOME", "HomeCoin" }, 1312 | { 101010, "STC", "Starcoin" }, 1313 | { 104109, "", "Seed Hypermedia" }, 1314 | { 105105, "STRAX", "Strax" }, 1315 | { 111111, "KAS", "Kaspa" }, 1316 | { 121337, "KLS", "Karlsen" }, 1317 | { 123456, "SPR", "Spectre" }, 1318 | { 130822, "WBT", "WhiteBIT Coin" }, 1319 | { 161803, "APTA", "Bloqs4Good" }, 1320 | { 200625, "AKA", "Akroma" }, 1321 | { 200901, "BTR", "Bitlayer" }, 1322 | { 224433, "CONET", "CONET Holesky Network" }, 1323 | { 246529, "ATS", "ARTIS sigma1" }, 1324 | { 261131, "ZAMA", "Zama" }, 1325 | { 314159, "PI", "Pi Network" }, 1326 | { 333332, "VALUE", "Value Chain" }, 1327 | { 333333, "3333", "Pi Value Consensus" }, 1328 | { 424242, "X42", "x42" }, 1329 | { 440017, "@G", "Graphite" }, 1330 | { 534352, "SCR", "Scroll" }, 1331 | { 666666, "VITE", "Vite" }, 1332 | { 696365, "ICE", "Ice Network" }, 1333 | { 888888, "SEA", "Second Exchange Alliance" }, 1334 | { 1048576, "AMAX", "Armonia Meta Chain" }, 1335 | { 1171337, "ILT", "iOlite" }, 1336 | { 1313114, "ETHO", "Etho Protocol" }, 1337 | { 1313500, "XERO", "Xerom" }, 1338 | { 1712144, "LAX", "LAPO" }, 1339 | { 3924011, "EPK", "EPIK Protocol" }, 1340 | { 4741444, "HYD", "Hydra Token" }, 1341 | { 5249353, "BCO", "BitcoinOre" }, 1342 | { 5249354, "BHD", "BitcoinHD" }, 1343 | { 5264462, "PTN", "PalletOne" }, 1344 | { 5655640, "VLX", "Velas" }, 1345 | { 5718350, "WAN", "Wanchain" }, 1346 | { 5741564, "WAVES", "Waves" }, 1347 | { 5741565, "WEST", "Waves Enterprise" }, 1348 | { 6382179, "ABC", "Abcmint" }, 1349 | { 6517357, "CRM", "Creamcoin" }, 1350 | { 7171666, "BROCK", "Bitrock" }, 1351 | { 7562605, "SEM", "Semux" }, 1352 | { 7567736, "ION", "ION" }, 1353 | { 7777777, "FCT", "FirmaChain" }, 1354 | { 7825266, "WGR", "WGR" }, 1355 | { 7825267, "OBSR", "OBServer" }, 1356 | { 8163271, "AFS", "ANFS" }, 1357 | { 10000118, "OSMO", "Osmosis" }, 1358 | { 15118976, "XDS", "XDS" }, 1359 | { 19000118, "SEI", "SEI" }, 1360 | { 22000118, "DYDX", "Dydx" }, 1361 | { 61717561, "AQUA", "Aquachain" }, 1362 | { 77777777, "AZT", "Aztecoin" }, 1363 | { 88888888, "HATCH", "Hatch" }, 1364 | { 91927009, "kUSD", "kUSD" }, 1365 | { 99999996, "GENS", "GENS" }, 1366 | { 99999997, "EQ", "EQ" }, 1367 | { 99999998, "FLUID", "Fluid Chains" }, 1368 | { 99999999, "QKC", "QuarkChain" }, 1369 | { 11259375, "LBR", "0L" }, 1370 | { 20230101, "ROH", "Rooch" }, 1371 | { 20240430, "NLK", "NuLinkCoin" }, 1372 | { 608589380, "FVDC", "ForumCoin" }, 1373 | { 1010101010, "FAIC", "Free AI Chain" }, 1374 | { 1179993420, "", "Fuel" }, 1375 | { 1179993421, "TTNC", "TakeTitan" }, 1376 | { 1179993431, "MTGBP", "MTGBP" }, 1377 | { 1179993441, "QFS", "Qfs" }, 1378 | { 1179993451, "RWA", "Asset Chain" }, 1379 | { 1179993461, "HXC", "HuaXia Chain" } 1380 | }; 1381 | #endif 1382 | --------------------------------------------------------------------------------